TRYWEAR
SDK reference
Drop-in virtual try-on

Add a fitting room to your store in two script tags.

Load one script, mount a button, and your shoppers can try any garment on their own photo — in a modal that opens right on your page, themed to your brand. No redirects, no SDK build step.

≈ 5 min to first try-on Vanilla JS · no dependencies Works with any stack
Overview

What you're integrating

The Trywear SDK (tryon.js) adds a Try it on button next to your existing buttons. When a shopper taps it, the studio opens in an overlay on your own page, pre-loaded with that product. The shopper adds a photo, and the AI renders them wearing it — garment and identity preserved.

It's built to feel like the rest of your store, not a third-party pop-up. Set your colours and font once in your Console under Brand and every shopper's studio wears them — see Theming.

Key

You'll need an API key. Generate one in your Console under Integration. It's shown once — store it like any secret. The key identifies your account and, once you're live, locks the SDK to your authorized domains.

Quickstart

Three steps to your first try-on

Paste these into any product page. Point src at wherever you host the studio, and use public image URLs for the product.

Step 1
Load the SDK
One <script> tag, loaded once per page.
Step 2
Initialise
TryOn.init with your API key.
Step 3
Mount a button
TryOn.mount beside Add to Cart.
product page
<!-- 1 · Load the SDK -->
<script src="https://trywear.co.za/tryon.js"></script>

<!-- 2 · Initialise, then 3 · mount a button -->
<script>
  TryOn.init({ apiKey: "tos_live_..." });

  TryOn.mount({
    anchor:    "#add-to-cart",              // element to sit beside
    productId: "SKU-2481",
    images:    ["https://cdn.yourstore.com/tee.png"]
  });
  // Brand lives in your Console, under Brand — no theme option needed here.
</script>

That's the whole integration. Everything below is optional polish — trying on a whole cart, pinning your brand, and locking down the key for production.

URLs

Product images must be public URLs. The render fetches them server-side, so a localhost or behind-login path won't load. Use your CDN or storage URLs — the same ones your product page already shows.

Under the hood

How the handoff works

You never touch our API directly from the button — the SDK does it. Here's the full path a tap takes:

On your page
Tap “Try it on”
The SDK opens the studio in a modal iframe on your domain.
Handoff
Product + brand
Image URLs and your theme are passed to the studio.
In the studio
Classify & dress
Each garment is sorted into a slot and worn automatically.
Result
Render on their photo
Shopper adds a photo; the AI renders the look. Photos auto-delete.
Cart integration

Try on the whole bag

Give the SDK a way to read the cart and it can style everything in it at once. Register a getCart provider — the SDK calls it fresh each time, so it always reflects the live cart — then mount a single button in your cart drawer.

cart drawer
TryOn.init({
  apiKey: "tos_live_...",

  // Return your live cart. Tag each item with a category —
  // it's how the SDK knows what can be tried on (see below).
  getCart: () => store.cart.items.map(i => ({
    productId: i.sku,
    name:      i.title,
    category:  i.category,      // "Clothing", "Electronics"…
    images:    [i.image]
  }))
});

// One button for the whole bag. Non-wearable items are skipped.
TryOn.mountCart({ anchor: "#cart-footer" });

Prefer to pass the cart at click time instead of a provider? Call TryOn.openCart({ cart: [...] }) from your own handler — same filtering, no provider needed.

Wearable filtering

Only clothes reach the studio — and only clothes cost anything

A real cart has things you can't wear — a charger, a gift card, a mug. Filtering these out isn't just about a clean experience: the AI classifier is the only step that costs money, so the SDK is built to reject obvious non-garments before it's ever called.

1 · Category filter — runs in the browser, costs nothing

Each cart item carries a category (or name). The SDK checks it against a standard e-commerce taxonomy — the same top-level departments Shopify, BigCommerce and Google Shopping already use — so most stores can pass their existing category field straight through with no mapping work. A match either way settles it instantly, with zero network call. Force it explicitly any time with tryable: true / false.

✓ Hoodie ✓ Sneakers ✓ Dress ✓ Cap ✗ USB Charger ✗ Gift card ✗ Coffee mug

2 · AI classifier — the authoritative last check, called only when needed

Anything with no category, or one the taxonomy doesn't recognise, still reaches the studio — and is classified server-side the first time it's ever seen. That result is cached globally by image, so the same product is never classified twice, on your store or anyone else's. If the model decides it isn't wearable, it's dropped silently and the shopper sees a quiet “Skipped 1 item that can't be tried on.”

Tip

You don't have to tag perfectly. Tagging makes filtering instant and free; leaving an item untagged just costs one classification, ever. Either way, a non-garment never reaches the try-on — and a real garment is never wrongly hidden by an imperfect category.

Category reference

What's in, what's out

The full list the client-side filter checks. Map your store's category field to these where you can — an exact or partial match (e.g. your "Men's Footwear" matches "footwear") is enough.

✓ Wearable — reaches the studio

Apparel & AccessoriesClothing Footwear / ShoesCostumes Uniforms / WorkwearSwimwear UndergarmentsSleepwear ActivewearOuterwear HeadwearEyewear

✗ Not wearable — rejected, no AI call

ElectronicsComputers AppliancesFurniture Home & GardenKitchen & Dining Hardware & ToolsAutomotive Office SuppliesSoftware Media / BooksToys & Games Baby GearSporting Goods* Pet SuppliesFood & Beverages Luggage & BagsHealth & Beauty Jewelry & WatchesGift Cards

* A department being excluded doesn't hide a real garment inside it — a product named "Team Jersey" under "Sporting Goods" still matches the wearable word list and is kept. The category check only settles items the name doesn't already answer for.

Theming

Wear your brand

Your Console owns the studio's brand. Set it once under Brand — paste your store address into Match my website, or pick an accent by hand — check the live preview, and press Publish. Every shopper's studio picks it up from there; no code change, no redeploy. Publish nothing and nothing changes: the studio keeps its own look rather than guessing at yours.

The SDK does not read your page or try to detect your brand. It used to, and it was the wrong call: it picked whatever coloured button it found first, and on storefronts that paint through a canvas (Flutter, Unity, some SPAs) it found nothing at all — leaving retailers unable to tell a bad guess from a broken feature. Your brand is what you publish, and you see it before you publish it.

Want it pinned in code instead? Pass the tokens directly — per call, or once on TryOn.init to cover every open:

explicit brand
TryOn.mount({
  anchor: "#add-to-cart",
  images: [product.image],
  theme: {
    brand:  "#1F6FEB",   // accent / buttons
    ink:    "#10151F",   // text
    paper:  "#FFFFFF",   // background
    font:   "Inter",     // body font family
    radius: 12               // corner rounding
  }
});

Accepts #RGB, #RRGGBB, or rgb(). Any token you leave out falls back to the brand saved in your Console.

Reference

API reference

The global TryOn object, available as soon as tryon.js loads.

TryOn.init(options)

Configure the SDK once per page.

apiKey
Your tos_live_… key.
studioUrl ?
Studio origin. Defaults to where tryon.js was loaded from.
getCart ?
Function returning the live cart array — used by mountCart.
theme ?
Pin the brand in code for every open: a token object, or "house" for the default studio. Omit it — the normal case — and the studio wears what you published in Console → Brand.
preboot ?
Set false to stop the studio loading in the background while the shopper browses. On by default — it's what makes Try On open instantly.
verifyUrl ?
Override the key-verification endpoint (self-host only).
TryOn.reportAddToCart(options)

Tell us a try-on converted. Call it from your own Add to Cart handler — we can't see that click, so without this your Analytics funnel stops at "generated a look" and the tried-but-not-bought list stays empty. Fire-and-forget: never blocks, never throws.

productId
The SKU that was added. Match the productId you pass to mount so the two line up in your reports.
TryOn.mount(options)

Insert a Try-On button after an anchor element, for one product.

anchor
CSS selector or element to place the button beside.
images
Array of public product image URLs.
productId ?
Your SKU, passed through for reference.
theme ?
Override this open's brand: a token object, or "house". Omit to use your published Console brand.
label · color · radius ?
Style the button itself.
TryOn.mountCart(options)

Insert a “Try on your bag” button that styles every wearable item in the cart.

anchor
Where the button goes (e.g. your cart drawer).
cart ?
Explicit cart array. Omit to use the getCart provider.
theme · label ?
As above.
TryOn.open(options) · TryOn.openCart(options)

Open the studio from your own click handler, without mounting a button. open takes one product's images; openCart takes a cart (or uses the provider) and filters it.

TryOn.close()

Close the studio overlay programmatically.

Keys & security

How the key is protected

Your key is created server-side and only ever shown once — we store just a SHA-256 hash of it, so a database leak can never expose a working key. You can regenerate or revoke it anytime from the Console.

The SDK verifies the key on init against a public endpoint that checks it against your authorized domains and returns your saved brand. It returns no secrets, so it's safe to call from the browser:

verifyKey
curl -X POST https://us-central1-ai-try-on-be537.cloudfunctions.net/verifyKey \
  -H "Content-Type: application/json" \
  -d '{ "apiKey": "tos_live_...", "origin": "https://yourstore.com" }'

# → { "valid": true, "retailer": "acme", "brand": { ... } }

Domain enforcement is live

Authorization is checked twice, and the second check is the one that matters. verifyKey runs at init so a misconfigured integration is obvious in your console during development — but it's cosmetic, and anyone can skip it. The enforced gate runs inside staticTryOn and requestMotion: the calls that actually spend money. Every embedded render carries your key plus the host page's origin, and both must pass before a single token is billed.

The policy, exactly:

  • ✓
    Your storefront must be on the list. A domain matches itself and its subdomains (shop.you.com passes under you.com).
  • ✓
    An empty list authorizes nothing. There is no "unset means allow" — if you save no domains, no storefront renders. Add your domains before you ship.
  • ✓
    Removing a domain takes effect immediately. The next GENERATE from that storefront is refused. No cache, no propagation delay.
  • ✓
    App Check runs underneath all of it. Every function requires a valid device-attestation token, so scripted callers never reach the gate in the first place.
Local

localhost always works — zero setup. localhost and 127.0.0.1 skip the domain list entirely, so your developers can integrate and test before any domain is saved. Enforcement applies to real storefront origins.

What a shopper sees when a store isn't authorized

Nothing about your configuration. A shopper on a blocked storefront cannot fix the problem and has no business knowing which lock is open, so the studio tells them only that something went wrong on the store's side, that it isn't their photo, and gives them a short reference code:

The shopper's view
TRY-ON IS UNAVAILABLE HERE
This store's try-on needs attention — nothing to do with your photo.

REF  A3F91C          [ REPORT THIS ]

The real cause — unknown key, unauthorized origin, empty domain list — is written to our logs under that same code. Set a Support contact on your Integration page and REPORT THIS mails your inbox with the code attached; forward it to us and we can pinpoint the exact request in seconds. Leave it blank and the shopper simply gets the code.

Dev

You still get the full diagnosis — from verifyKey. The init handshake returns the precise reason ("origin x not authorized", "no authorized domains saved"), so a misconfigured integration is obvious in your console at development time. Only the shopper-facing render error is generic.

Roadmap

Mobile apps

A native app has no origin to check — so what replaces the domain? Its signed application identity. A web page proves where it is; an app proves what it is, and the operating system does the proving. That's a stronger guarantee than an origin string, not a weaker one: an origin is asserted by the page, whereas an app's identity is attested by Google or Apple and signed.

The shape it takes:

  • ✓
    Authorized Origins is one list — and it's live in the Console today. The card holds web domains (yourstore.com) and app IDs (com.yourstore.shop) side by side. You add an app exactly the way you add a domain, and removing it cuts that app off exactly the same way. You can register your app IDs now, ahead of the SDK.
  • ✓
    The OS attests the app. Play Integrity on Android, App Attest / DeviceCheck on iOS, both through Firebase App Check — which our backend already enforces on every function today. Your app registers its package name and signing certificate (Android) or bundle ID (iOS).
  • ✓
    The gate doesn't change. The render path already matches a caller's origin against your list; for an app it matches the attested app ID instead. Same list, same enforcement, same immediate revocation.
  • ✓
    WebView embeds are covered too. An app that hosts the existing web SDK in a WebView passes its app identifier through the same attestation, so it authorizes as an app rather than as a spoofable page origin.
Status

The mobile SDK itself isn't released yet — but the authorization side is already real: the Console's Authorized Origins card accepts app IDs today, and the backend needs no new architecture because App Check is already the gate on every call. When the SDK lands, your registered app IDs simply start being enforced. Building a native app against Trywear? Tell us — we'll size it with you.

Ship it

Going live

  • ✓
    Host tryon.js and the studio. Point the script src at that host; the SDK infers studioUrl from it.
  • ✓
    Generate your API key in Console → Integration, and drop it into TryOn.init.
  • ✓
    Add your storefront domains — enforcement is live, and a key with an empty list renders nothing. See Domain enforcement.
  • ✓
    Set a support contact so a shopper who ever hits a blocked store can report it to you in one tap.
  • ✓
    Use public product image URLs — the same CDN URLs your store already serves.
  • ✓
    Tag cart items with a category for instant, free wearable filtering.
Trywear · SDK reference. Questions or a token estimate for your volume? Your Console's Integration page has a live pricing calculator.