Skip to content

Custom storefront

If you’re not on WooCommerce or BigCommerce, the integration is the same three pieces described in Storefront integration — you just wire them into whatever templating or rendering layer your storefront already uses.

Wherever you generate a product page — a template render, a static build step, an API response your frontend consumes — check the product’s current state before deciding whether to include the try-on snippets at all:

Request
curl https://api.integration.tryonvirtual.com/v1/products/TRYON_PRODUCT_UUID \
-H "Authorization: Bearer tryon_sk_your_key"

TRYON_PRODUCT_UUID is the TryOn id — REST paths like this one always take that UUID, never your own external_id (that works only in the storefront snippet’s productId and the ?external_id= filter — see Using your own IDs). If your sync job only has its own identifier on hand, look the product up first with GET /v1/products?external_id=... to get the id this call needs.

Render the button only when tryon_status == "active" and swap_ready == true. If you sync TryOn state into your own database rather than calling the API per request (recommended for anything higher-traffic than a handful of pages), keep both fields current there and branch on your local copy instead — see Product lifecycle for why both checks matter independently.

2. Template the two snippets with your own id

Section titled “2. Template the two snippets with your own id”

Wire your product identifier — whichever value your system already has, ideally the same one you’re using as external_id (see Using your own IDs) — into both snippets:

Your product template
<button type="button" data-tryon-product-id="{{ product.id }}">Try On</button>
<script async
src="https://api.integration.tryonvirtual.com/api/v1/tryon/scripts/embed/bootstrap.js?shop_slug=YOUR_SHOP_SLUG&productId={{ product.id }}">
</script>

({{ product.id }} here is a stand-in for whatever your templating language uses — Jinja, ERB, JSX, a plain string concatenation in a hand-rolled renderer. The two values that matter are your shop slug and the product identifier.)

The button-binding behavior described in Storefront integration runs once, on DOMContentLoaded: the bootstrap script scans the page for elements carrying data-tryon-product-id at that moment and binds them. That works fine for a traditional multi-page site, where every product page is a fresh document load. It does not work for a single-page app that swaps product views via client-side routing without a full page load — the button rendered by your router after the initial load was never scanned, so it won’t be bound.

Two ways to handle this in a client-routed app:

  • Trigger try-on from your own UI instead of relying on the auto-bound button. Skip data-tryon-product-id entirely and call window.openTryOn() from your route/component’s click handler once the bootstrap script for the current product has loaded.
  • Re-insert the script tag on every route change, so a fresh <script> element (with the new product’s productId) is added to the DOM each time the user navigates to a different product — this re-triggers the bind for the newly-rendered button.

Either way, make sure the render check from step 1 (tryon_status + swap_ready) runs again on each client-side navigation too, not just on the initial page load — a SPA that fetches product data once and caches it can otherwise show a stale button state after a merchant changes it.

API reference — full request/response schemas for every endpoint used across this guide.