Skip to content

Product lifecycle

A product becomes try-on-capable in three steps, and they have to happen in this order.

Request
curl -X POST https://api.integration.tryonvirtual.com/v1/products \
-H "Authorization: Bearer tryon_sk_your_key" \
-H "Content-Type: application/json" \
-d '{"title": "Aviator Sunglasses", "category": "eyewear"}'

category drives which AI model processes the product — it’s required on create and can’t be left unset later. Valid values: eyewear, watch, shoes, jewelry, clothes, bag, luggage.

Request
curl -X POST https://api.integration.tryonvirtual.com/v1/products/{id}/images \
-H "Authorization: Bearer tryon_sk_your_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://yourstore.com/images/aviator.jpg"}'

This is the step that actually triggers processing — a product with no images can never become ready, no matter how you set mode or tryon_enabled. You can also pass images directly in the create call in step 1; either way works. See Images for URL vs. multipart upload, size/format limits, and per-image failure codes.

Request
curl -X PATCH https://api.integration.tryonvirtual.com/v1/products/{id} \
-H "Authorization: Bearer tryon_sk_your_key" \
-H "Content-Type: application/json" \
-d '{"mode": "swap", "tryon_enabled": true}'

Enabling requires a category (already set in step 1) and a mode. Changing mode on a product that already has try-on enabled turns it off again — you’ll need a fresh tryon_enabled: true after switching modes.

swap_ready on the product reports whether the images have finished processing for AI try-on. It’s independent of tryon_status — you can enable try-on before processing finishes, and swap_ready will flip to true on its own once the pipeline catches up.

Poll GET /v1/products/{id} every few seconds after adding images; processing is usually done in under a minute.

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

If swap_ready stays false for more than a few minutes, the images likely failed rather than being slow:

  • Check the images_failed array returned by the original POST /v1/products (or the error response of the POST /v1/products/{id}/images call you used) — a URL that timed out, wasn’t reachable, wasn’t actually an image, or was over 15 MB is reported there, not silently retried.
  • Look at the product’s images array — if it’s empty, none of your URLs succeeded and there’s nothing for the pipeline to process. Add a working image URL to retry.
  • See Images for the full list of per-image error codes and what each one means.

These are two independent gates, and the storefront button should only render when both are satisfied:

Field Meaning
tryon_status Whether you’ve turned try-on on for this product (active / inactive) — set via PATCH
swap_ready Whether the AI engine has finished processing the product’s images

A product can be active with swap_ready: false (you enabled it before processing finished) — rendering the button in that state opens an empty experience for the customer. Always check both before you decide whether to show the button when generating your product pages — see the storefront snippet in the quickstart.

swap (AI photo swap) is the mode you can drive end-to-end through this API — create, add images, enable, done.

mode: realtime is the AR experience, and it requires a 3D asset. 3D assets are managed in the merchant panel (upload or AI generation) — this API can’t attach one. Enabling realtime mode requires an active 3D asset: PATCH {"mode": "realtime", "tryon_enabled": true} returns 400 validation_error (“Active 3D asset required for real-time mode”) if none is usable. If the product has exactly one eligible 3D asset, it’s activated automatically and the enable succeeds — otherwise, activate one in the panel first and check active_asset_id on the product (non-null means one is active) before you PATCH.

Every product has an id we generate — store it as the join key if that’s easiest for your integration. Or set external_id to your own SKU or database id at create time (or via a later PATCH):

Request
curl -X POST https://api.integration.tryonvirtual.com/v1/products \
-H "Authorization: Bearer tryon_sk_your_key" \
-H "Content-Type: application/json" \
-d '{"title": "Aviator Sunglasses", "category": "eyewear", "external_id": "SKU-1234"}'

external_id is unique within your shop. Once it’s set you can:

  • Filter with GET /v1/products?external_id=SKU-1234
  • Use SKU-1234 directly as PRODUCT_ID in the storefront snippet instead of our id — your templates never need to know our ids at all

external_id is read-only for shops on the Shopify platform (Shopify sync owns that mapping there) — this field is for WooCommerce, custom platforms, and other non-Shopify shops.

Images — URL vs. multipart upload, limits, and error codes.