Wix
Wix gives you no server-side product template to edit, so the two snippets from
Storefront integration can’t be pasted in as static HTML —
every product page is the same page, and the bootstrap script needs a different productId on each
one. The path that works is Velo: a custom element that renders
the button in the page’s own DOM, and page code that tells it which product it’s showing.
1. Sync products, using a Wix identifier as external_id
Section titled “1. Sync products, using a Wix identifier as external_id”Read your catalog with Wix Stores’ Query Products,
then create a matching TryOn product for each one. Wix REST calls authenticate with an API key from
the API Keys Manager plus your site id — note the key
goes in Authorization bare, with no Bearer prefix, unlike TryOn’s:
curl -X POST https://www.wixapis.com/stores/v3/products/query \ -H "Authorization: WIX_API_KEY" \ -H "wix-site-id: WIX_SITE_ID" \ -H "Content-Type: application/json" \ -d '{"query": {"filter": {"visible": {"$eq": true}}, "cursorPaging": {"limit": 100}}}'curl -X POST https://api.integration.tryonvirtual.com/v1/products \ -H "Authorization: Bearer tryon_sk_your_key" \ -H "Content-Type: application/json" \ -d '{ "title": "Classic Fit Shirt — Red, XL", "category": "clothes", "external_id": "SHIRT-XL-RED", "images": ["https://static.wixstatic.com/media/shirt-xl-red.jpg"] }'category is required and drives which
AI model processes the product — map it from your Wix collections or product type, it won’t come
across automatically. Once images finish processing and swap_ready turns true, PATCH the
product to enable try-on exactly as on any other platform; see
Product lifecycle for both calls.
For a one-time load you don’t have to script against the Wix API at all — export the catalog to CSV from the store dashboard and feed it through the bulk sync loop on the WooCommerce page.
Choosing the identifier
Section titled “Choosing the identifier”Whatever you store as external_id has to be a value the product page can read back in step 4.
Velo’s getProduct() hands you both sku and _id, so either works — with one wrinkle: in the V3
catalog, SKUs are a variant field, and Query Products
doesn’t return variant data,
so syncing by SKU means an extra per-product call to read it. Syncing by the Wix product id avoids
that. Either way the invariant is the same: the value you store as external_id must be the exact
value your page code reads back. See Using your own IDs.
2. Add a custom element
Section titled “2. Add a custom element”const SHOP_SLUG = 'YOUR_SHOP_SLUG';
class TryOnButton extends HTMLElement { static get observedAttributes() { return ['product-id']; }
attributeChangedCallback(name, previous, productId) { if (name !== 'product-id' || !productId || productId === previous) return; this.render(productId); }
render(productId) { // No shadow root on purpose — the button and the bootstrap script both // belong in the page's own DOM. this.innerHTML = '';
const button = document.createElement('button'); button.type = 'button'; button.textContent = 'Try On'; button.disabled = true; // until the bootstrap script has loaded button.addEventListener('click', () => window.openTryOn()); this.appendChild(button);
// One bootstrap script per product. Wix moves between products without a // full page load, so drop the previous one before loading the next. this.script?.remove(); const script = document.createElement('script'); script.async = true; script.src = 'https://widget.tryonvirtual.com/api/v1/tryon/scripts/embed/bootstrap.js' + `?shop_slug=${SHOP_SLUG}&productId=${encodeURIComponent(productId)}`; script.onload = () => { button.disabled = false; }; this.script = script; document.head.appendChild(script); }}
customElements.define('tryon-button', TryOnButton);Find YOUR_SHOP_SLUG in the panel under Settings → Shop.
Add it to the product page from the editor — Add Elements → Embed → Popular Embeds → Custom
Element in the Wix Editor, Add Elements → Embed & Social → Custom Element in Wix Studio — then
Choose Source → Velo file, pick the file, and enter tryon-button as the Tag Name. It has
to match the string in customElements.define() exactly. (Wix’s own walkthrough is
Add a Custom Element.)
Two details in that code are deliberate:
- The click handler calls
window.openTryOn()instead of usingdata-tryon-product-id. The automatic binding described in Storefront integration scans the page once, onDOMContentLoaded. This element renders later — after Velo has fetched the product and set the attribute — so its button was never scanned and would never be bound. Calling the global directly is the documented way out, the same one a client-routed app needs (see Custom storefront). - The old script tag is removed before a new one is added. Every bootstrap script defines the
same
window.openTryOnglobal, so a page carrying two of them offers try-on for whichever loaded last, not for both — try-on is one product per page.
3. Keep the API key in the backend
Section titled “3. Keep the API key in the backend”Page code and the custom element both run in the visitor’s browser, so an API key put in either one
is public — and a tryon_sk_ key can create and modify every product in your shop. Store it in the
Wix Secrets Manager (dashboard → Settings → Secrets Manager) and do the readiness check in a
backend web module instead:
import { Permissions, webMethod } from 'wix-web-module';import { getSecret } from 'wix-secrets-backend';import { fetch } from 'wix-fetch';
export const isTryOnReady = webMethod(Permissions.Anyone, async (externalId) => { const apiKey = await getSecret('tryon_api_key');
const response = await fetch( 'https://api.integration.tryonvirtual.com/v1/products' + `?external_id=${encodeURIComponent(externalId)}`, { headers: { Authorization: `Bearer ${apiKey}` } } ); if (!response.ok) return false;
const { products } = await response.json(); const product = products?.[0];
// Both gates, not either/or — see Storefront integration. return Boolean(product && product.tryon_status === 'active' && product.swap_ready);});The lookup goes through ?external_id= rather than /v1/products/{id} because the value the page
has on hand is your identifier, not TryOn’s UUID — REST paths only ever take the UUID
(Using your own IDs). And both fields are
checked because a product can be active with swap_ready: false, which opens an empty experience
for the customer — why both gates matter.
As written this calls the TryOn API on every product page view, which is fine for a small catalog.
For anything busier, mirror tryon_status and swap_ready into a Wix Data collection from a
scheduled sync job and have this function read that instead — same reasoning as
Storefront integration.
4. Wire the product page
Section titled “4. Wire the product page”In Dev Mode, open the Product Page and add its page code:
import { isTryOnReady } from 'backend/tryon.web';
$w.onReady(async () => { const product = await $w('#productPage1').getProduct(); const externalId = product.sku; // the value you stored as external_id in step 1
if (!externalId) return; if (!(await isTryOnReady(externalId))) return;
// The element renders nothing until it receives this attribute, so a product // that isn't ready simply never shows a button. $w('#customElement1').setAttribute('product-id', externalId);});#productPage1 and #customElement1 are Wix’s default element ids — confirm both in the editor’s
Properties panel, since a page with more than one of either will number them differently.
Because the readiness check gates the setAttribute() call, there’s no separate “only render when
ready” step here: an unready product never gets a product-id, and the element renders nothing.
Don’t use the HTML embed
Section titled “Don’t use the HTML embed”Wix’s Embed HTML element looks like a shortcut for pasting the two snippets, but it renders its
contents inside a cross-origin sandboxed iframe, which breaks try-on twice over. The experience is
confined to the embed box instead of covering the page, and webcam AR needs the iframe to carry
allow="camera" — a Permissions Policy delegation the parent page has to grant, which the Wix
editor gives you no way to set. The custom element above has neither problem: it runs in the page’s
own document.
Custom storefront — the platform-agnostic version of the same integration, plus notes for single-page apps.