API v1

Virtual try-on, in one call.

Send a photo of a person and a product image; get back a photorealistic image of the person wearing it. Use the REST API from your server, or drop the storefront SDK onto your product pages.

Quickstart

  1. Create an account and organisation at roopastra.com/enterprise.
  2. Buy a credit pack on the Billing page.
  3. Create a secret key (server) or a publishable key (storefront) on the API keys page.
Your first try-on
curl https://vton-api.roopastra.com/v1/tryons?wait=60 \
  -H "Authorization: Bearer rk_live_…" \
  -F model=atelier-pro -F resolution=2K -F category=full \
  -F person_image=@person.jpg \
  -F garment_image=@dress.jpg

With wait=60 the response waits until the image is ready (usually 10–15 seconds) and returns it with a signed result.url. Without wait you get 202 immediately; poll or use a webhook.

Authentication

Server calls use a secret key (rk_live_…) as a bearer token. Keep it on your server; it is shown once and can be revoked any time. Storefronts use a publishable key (pk_live_…) through the SDK; it is public by design and only works from the domains you list on it.

Authorization: Bearer rk_live_…

Create a try-on

POST /v1/tryons as multipart/form-data (recommended) or JSON. Each image may be a file, a public https URL, or (JSON only) base64.

FieldDescription
person_imagePhoto of the person (file). JPEG, PNG or WebP, up to 12 MB, at least 256 px per side. Or person_image_url.
garment_imageProduct photo (file), same rules. Or garment_image_url.
modelatelier-pro (default) or atelier-ultra.
resolution1K, 2K (default) or 4K.
categoryfull (default), top, bottom, jewellery or back_view.
webhook_urlOptional public https URL that receives tryon.completed / tryon.failed.
end_user_idOptional reference of yours (max 128 characters), echoed in usage exports.

Send an Idempotency-Key header to retry safely: repeating a request with the same key returns the original try-on and never charges twice. Credits are held when the try-on starts and charged only if an image is delivered; failures release them.

Results and polling

GET /v1/tryons/{id}?wait=25
{
  "id": "tryon_3f0c…",
  "object": "tryon",
  "status": "succeeded",          // queued | processing | succeeded | failed
  "model": "atelier-pro",
  "resolution": "2K",
  "category": "full",
  "credits": 1.0,
  "created_at": "2026-10-20T16:52:03Z",
  "finished_at": "2026-10-20T16:52:17Z",
  "result": {
    "url": "https://cdn.roopastra.com/r/…?Expires=…&Signature=…",
    "width": 1792, "height": 2400,
    "expires_at": "2026-10-20T18:00:00Z"
  },
  "error": null
}

result.url is a signed link valid for about an hour; request the try-on again for a fresh link. Results are kept for 30 days. GET /v1/credits returns your balance.

Models and credits

1 credit = one Atelier Pro try-on at 2K. Failed try-ons are free.

Model1K2K4K
Atelier Pro0.711.5
Atelier Ultra1.41.42.5

Atelier Pro is fast and great for most catalogues. Atelier Ultra gives the highest fidelity for detailed fabrics and jewellery.

Webhooks

When a try-on finishes, we POST { "event": "tryon.completed" | "tryon.failed", "data": TryOn } to its webhook_url, retrying with backoff for about a day until you answer 2xx. Verify the signature with your signing secret (API keys page):

Roopastra-Signature: t=<unix>,v1=<hex>
// Node.js
import crypto from 'node:crypto';

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = crypto.createHmac('sha256', secret)
    .update(parts.t + '.' + rawBody).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
# Python
import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return abs(time.time() - int(parts["t"])) < 300 and hmac.compare_digest(expected, parts["v1"])

Storefront SDK

One script tag adds a Try it on button under Add to cart on product pages. Shoppers upload a photo in a pop-up and see themselves in the product, then add it to the cart. Copy your snippet from the SDK page in the dashboard.

Install
<script src="https://cdn.roopastra.com/sdk/v1/roopastra.js"
        data-key="pk_live_…" async></script>

The SDK finds the product image from data-roopastra-product, the page's Product structured data, WooCommerce galleries or og:image, and the Add to cart button on Shopify, WooCommerce and most themes.

AttributePurpose
data-roopastra-productOn any element: the garment image URL. Or put it on the product <img>.
data-roopastra-product-idYour SKU or product ID, reported with each try-on.
data-roopastra-categoryfull, top, bottom, jewellery or back_view (skips the garment-type picker).
data-roopastra-buttonYour own element that opens try-on (use with Manual placement).
data-roopastra-add-to-cartYour Add to cart control, if it is not detected automatically.
data-roopastra-cardA product card in a catalogue grid; it gets the overlay Try it on button. Common Shopify, WooCommerce and BigCommerce themes are detected without it.
data-placement (script tag)auto, floating or manual: overrides the dashboard setting for this page.
JavaScript
Roopastra.open({ garmentImage: 'https://…/dress.jpg', productId: 'SKU-1', category: 'full' });
Roopastra.close();

document.addEventListener('roopastra:result', (e) => console.log(e.detail.id, e.detail.url));
document.addEventListener('roopastra:add-to-cart', (e) => analytics.track('tryon_add_to_cart'));

The button and try-on window design (colours, shape, font, wording and your logo), the model, resolution, daily credit cap and per-shopper hourly limit are set in the dashboard's SDK designer, never by the shopper's browser. On Shopify, install the Roopastra app and connect the store from the dashboard; the key is set up for you. Product images must come from one of the key's allowed domains, a common store CDN or an image host you add on the dashboard SDK page. For a pinned version with Subresource Integrity, use the file and hash in cdn.roopastra.com/sdk/v1/manifest.json.

Errors

Errors return JSON: { "error": { "code", "message", "request_id", "fields"? } }. Include the request_id when you contact support.

HTTPcodeMeaning
400validation_failed / invalid_imageFix the field named in fields.
401unauthorizedMissing, invalid or revoked key.
402insufficient_creditsBuy credits in the dashboard.
409conflictIdempotency-Key reused with a different request.
413payload_too_largeRequest body over 25 MB.
429rate_limitedSlow down; see the Retry-After header.
503service_unavailableTemporary; retry with backoff (use an Idempotency-Key).

A try-on that fails has status: "failed" and an error.code. Failed try-ons are never charged.

error.codeMeaning
safety_rejectedThe images could not be processed for content reasons.
invalid_imageAn image could not be read or is too small.
generation_failedThe image could not be generated after several attempts; try again.
generation_timeoutThe image took too long; try again.

Limits and data

  • 600 requests per minute per key; 120 try-on submissions per minute per organisation.
  • Input photos are deleted within 24 hours. Result images are kept for 30 days.
  • Uploaded photos are used only to create the requested try-on.
  • Credits stay valid for 12 months after your latest purchase.

Questions? info@roopastra.com