Try-On API
Base URL https://tryon.vidrip.app/api/v1. JSON in, JSON out. Secret keys (vk_test_… / vk_live_…) go in Authorization: Bearer from your server. The publishable key (vp_…) is for the widget on your pages.
Photo try-on
Give us a photo of the shopper and the product, get back an image of them wearing it. Jobs are asynchronous: you get a 202 with an id, then a webhook or a poll.
POST /api/v1/tryons
Authorization: Bearer vk_test_…
Idempotency-Key: order-123-tee
{ "person_image": "https://…/shopper.jpg",
"garment_id": "gar_…", // or "garment_image": "https://…/tee.jpg", "category": "tops"
"quality": "balanced" } // performance | balanced | quality
202 { "id": "…", "status": "queued", "eta_seconds": 12, … }
GET /api/v1/tryons/:id → { "status": "done", "image_url": "https://…(signed, 7 days)", "cost_cents": 8 }
GET /api/v1/jobs/:id → { "status", "position", "eta_seconds", "result" }Errors are { error, code }. Images that fail moderation return 422 with person_image_rejected or garment_image_rejected.
Garments
Register products once so sessions and try-ons can reference them by id. A SKU is your natural key: posting it again updates the product.
POST /api/v1/garments
{ "sku": "TEE-001", "name": "Boxy tee", "category": "tops",
"images": [{ "url": "https://…/front.jpg", "view": "front" }, { "url": "https://…/back.jpg", "view": "back" }],
"product_url": "https://store.example/tee" }
→ 201 garment
GET /api/v1/garments?sku=TEE-001
PATCH /api/v1/garments/:id { "status": "disabled" }Every garment also gets a 3D build. We classify the photos, turn an on-model photo into a flat product shot, cut it out, build the matching template on a stock body (womenswear 168 cm, menswear 180 cm), texture it from your photos, and grade it from your size chart. It takes under a minute. Twelve templates cover tees, tanks, long sleeves, sweatshirts, shirts, jackets, dresses, skirts, trousers, shorts, leggings and swimsuits. Accessories get status: "unsupported" and keep working in photo try-on.
GET /api/v1/garments/:id →
{ …, "model": { "status": "ready", "rev": 2, "template": "tee", "fabric": "jersey", "body": "male",
"glb_url": "https://tryon.vidrip.app/api/assets/garments/…/garment.glb",
"preview_url": "…/preview.jpg", "flat_image_url": "…/flat.png",
"base_size": "M", "sizes": [{ "size": "M", "ease_cm": { "chest": 1.8 } }, …],
"review_status": "pending" } }
PATCH /api/v1/garments/:id
{ "overrides": { "template": "shirt", "fabric": "woven", "fit": "relaxed", "sleeve": "long", "body": "female" },
"size_chart": { "unit": "in", "measurements_of": "body",
"sizes": [{ "size": "S", "chest": 36 }, { "size": "M", "chest": 39 }, { "size": "L", "chest": 42 }] },
"review": "approved", // approved | rejected | pending
"rebuild": true } // overrides, a size chart or a new image rebuild anywayThe GLB is the garment on its stock body, on the same 127-joint skeleton as avatars, so you can pose both together. Send build_3d: false on create to skip a build. Size charts can give body measurements (the person who fits each size) or garment measurements (the item laid flat, doubled). Either way the chart sets how much room each size leaves on the stock body.
Avatars (3D body)
One full-body photo becomes the shopper's body: a rigged 3D model in a neutral pose, scaled to their height, with tape-style measurements in centimetres. Best results come from a photo facing the camera, whole body in frame, arms a little away from the sides, in fitted clothes. Send height_cm whenever you have it. Without it the height is the model's estimate, and every measurement scales with it.
POST /api/v1/avatars
{ "person_image": "https://…/shopper-full-body.jpg",
"height_cm": 178, // optional, 120–230
"consent": true, // required: the shopper agreed to a body scan
"external_ref": "customer-881", // optional, your id for the shopper
"retain_days": 30 } // optional, 1–365 (default 30)
202 { "id": "…", "object": "avatar", "status": "queued", "eta_seconds": 15, … }
GET /api/v1/avatars/:id →
{ "status": "ready", "height_cm": 178, "height_source": "provided",
"measurements": { "height": 178, "chest": 104.5, "waist": 88, "hips": 101, "inseam": 81,
"neck": 39.5, "thigh": 58, "shoulder_width": 46.5, "arm_length": 59 },
"glb_url": "https://…/body.glb (signed, 1 hour)",
"quality": { "full_body": true, "people": 1, "warnings": [] }, "expires_at": "…" }
GET /api/v1/avatars?external_ref=customer-881
DELETE /api/v1/avatars/:id // erases the model, measurements and any photo nowThe GLB is glTF 2.0 in metres, y up, facing +z: one skinned mesh on Meta's MHR skeleton (127 joints, such as l_uparm and c_spine2), about 1.3 MB. It loads in three.js, Babylon or model-viewer, and you can pose it by rotating bones. A body scan is biometric data, so send consent: true only when the shopper agreed to it. The photo is deleted within 24 hours. The avatar is deleted at expires_at, or at once with DELETE. Measurements come from one photo and are accurate to a few centimetres. Loose clothing reads larger.
In the hosted page the shopper opens 3D body, or you open the page there directly with data-view="body" or VidripTryOn.open({ view: 'body', onAvatar }). The shopper agrees to the scan in the page itself, and onAvatar receives { avatarId, measurements } when their body is ready.
Live mirror
In the hosted page, Live mirror opens the shopper's camera with the garment on them in real time. Body tracking (MediaPipe Pose) runs on their device; the camera feed is never uploaded. The garment is their own draped fit when they have a 3D body, or the stock-body build stretched to their shoulders until they do. Their arms hide the garment where they cross in front of it. Take a photo sends that one frame to the photo try-on; Use this view for my 3D body starts a body scan from it, with the usual consent. It needs a camera and a browser with WebGL; it works inside the widget's frame (which already allows the camera).
Hosted sessions and the widget
A session is a hosted try-on page for one shopper and a set of garments. Mint it from your server with the secret key, or let the widget mint it in the browser with the publishable key (only from origins you allowed in the dashboard).
POST /api/v1/sessions
{ "skus": ["TEE-001"], "return_url": "https://store.example/cart", "metadata": { "cart": "c_9" } }
→ 201 { "id", "url": "https://tryon.vidrip.app/try/…", "embed_url": "…/embed/…", "expires_at" }
<script src="https://tryon.vidrip.app/widget.js" data-key="vp_…" data-skus="TEE-001"></script>
// or
VidripTryOn.open({ skus: ['TEE-001'], onAddToBag: (e) => cart.add('TEE-001', e.size) }); // e.size from the 3D fitting roomWebhooks
Set an endpoint in the dashboard; you get a signing secret once. Each delivery carries a vidrip-signature: t=…,v1=… header: HMAC-SHA256 of `${t}.${body}` with your secret. Reject anything older than five minutes. Retries back off over a day.
| Event | When |
|---|---|
tryon.completed / tryon.failed | A try-on finished. Payload is the try-on object. |
avatar.ready / avatar.failed | A body finished. Payload is the avatar object, with measurements. |
avatar.deleted | An avatar was erased: { id, reason }, where reason is api, shopper or expired. |
fit.ready / fit.failed | A fit's 3D model finished. Payload is the fit, with size advice. |
garment.ready | A garment was registered or updated. |
garment.model_ready / garment.model_failed / garment.model_unsupported | A garment's 3D build finished. Payload is the garment, with model. |
session.converted | The shopper tapped "Add to bag" in the hosted page: { session_id, garment_id, sku, size, metadata }. |
ping | Sent from the dashboard to test your endpoint. |
Fits and size advice
Put a garment on a shopper's avatar. The size advice comes back in the response: for every size in your chart, how much room it leaves at the chest, waist and hips on this body, compared with the room the design intends and how much the fabric gives. The 3D model of the garment in that size on their body follows a few seconds later, with a fit map. Without size we fit the recommended one. The same avatar, garment build and size return the cached fit.
POST /api/v1/fits
{ "avatar_id": "…", "garment_id": "…", "size": "M" } // size optional
202 { "id": "…", "object": "fit", "status": "queued", "size": "L",
"recommended_size": "L", "reason": "L is snug at the waist and hips; XL is loose across the chest.",
"sizes": [ { "size": "M", "fit": "tight", "note": "M is tight at the hips, snug across the chest and waist",
"regions": { "chest": { "room_cm": 3.2, "fit": "snug" }, … } }, … ] }
GET /api/v1/fits/:id → { …, "status": "ready", "glb_url": "https://…(signed, 1 hour)" }fit is one of tight, snug, good or loose; room_cm is circumference, negative when the garment is smaller than the body. The GLB is the shopper's body wearing the garment on the shared skeleton. Each garment vertex carries a _STRAIN attribute (room in cm) for drawing a fit map. The garment is draped on their body by a cloth simulation: it starts around the body and settles under gravity toward the garment as made in that size, so a small size stretches (and reads tight in _STRAIN) and a big one hangs and folds. A fit is deleted with its avatar. Live stores only fit garments whose 3D build you approved.