Dressroom API
Turn any wardrobe, product photos, or links into a digital wardrobe. The AI finds the fashion pieces in your images and arranges them, whether it's for a person, a family, or your store. A quick way to upload your products or build out inventory. Direct REST endpoints; you build the UI.
Quickstart
Add pieces
detect, scrape, or upload
Save
PUT inventory
- 1Create an API key in the API Dashboard, then contact our team to get credits added.
- 2Every call takes an
externalUserId— your own id for the end user whose wardrobe you're building. Same id, same wardrobe, every time. - 3Add pieces with
detect,scrape, orupload, let your UI accept/reject, thenPUT inventoryto save.
That's the whole Dressroom API — building and maintaining a wardrobe. Styling a look from it is a separate feature (the same StyleMe Try-On step ZukMe Marketplace and Your Own Inventory use); see Styling from this wardrobe.
User experience flow
A plain walkthrough of what your user experiences in your own UI — no implementation detail. Each beat notes which call powers it.
Building a wardrobe
Sees their wardrobe
Empty the first time, with an "Add New" entry point.
→ GET /api/v1/dressroom/inventory
Picks how to add a piece
Scan a photo with AI, paste a link, or upload and tag by hand — your UI, three calls.
Scans or pastes
Drops a photo, or pastes a product/shop link. Comes back as a list of drafts — photo, name, category — nothing saved yet.
→ POST /api/v1/dressroom/detect or /scrape
Optionally cleans up the photo
A raw crop or scraped photo can be rough — re-renders it as a clean studio product shot before saving. Entirely optional; skip it and save the original photo instead.
→ POST /api/v1/dressroom/generate
Reviews, then saves
Accepts or rejects each draft in your UI; saving writes the accepted ones to the wardrobe.
→ PUT /api/v1/dressroom/inventory
Or uploads by hand
One photo, typed-in details, saved immediately — no review step, no AI.
→ POST /api/v1/dressroom/upload
Styling a look from the saved wardrobe is a separate feature — see Styling from this wardrobe.
Base URL
https://zukme.comAll endpoints below are relative to this base URL.
Authentication
Every request needs your API key as a bearer token — create and manage keys in the API Dashboard. The raw key is shown once; only its hash is stored.
Authorization: Bearer zk_live_<your key>Missing, malformed, or revoked key → 401:
{ "error": "Invalid or missing API key. Pass it as \"Authorization: Bearer zk_live_...\"." }Rate limits
30 requests per minute per key (submit and poll both count). Over the limit → 429:
{ "error": "Rate limit exceeded.", "retryAfterSeconds": 12 }Credits
Each successful submission costs 1 credit, deducted at submit and refunded automatically if generation fails. Polling is always free. Detect and scrape cost 1 credit each; upload is free. Studio photo (optional) costs 1 credit per piece. A new API key starts with 0 credits — API and embed-widget usage spends from your account's bonus credit balance only, never your app subscription's monthly quota. Contact our team to get bonus credits added before building against this in production. Check your balance in the API Dashboard.
Errors
| Field | Type | Description |
|---|---|---|
400 | Bad Request | Missing or invalid fields in the request body. |
401 | Unauthorized | Missing, malformed, or revoked API key. |
402 | Payment Required | Credits exhausted (code CREDITS_EXHAUSTED), with your current looks/bonus/total balance in the body. |
404 | Not Found | jobId does not exist (expired, wrong id, or never submitted). |
429 | Too Many Requests | Rate limit exceeded for this key. See retryAfterSeconds. |
Example 402 body:
{
"error": "CREDITS_EXHAUSTED",
"code": "CREDITS_EXHAUSTED",
"looks": 0,
"bonus": 0,
"total": 0
}Step 1 — Add pieces
Every call below takes externalUserId (required) and an optional dressroomId, for a user with more than one wardrobe. Same pair → same wardrobe, always.
Detect
One photo — closet, rack, or flat-lay. Finds, names, and crops every garment.
Scrape
One link — product, category, or shop page. Every plausible product photo becomes a draft.
Upload
One photo, one piece, tagged by hand. Saved immediately, no review step.
/api/v1/dressroom/detect| Field | Type | Description |
|---|---|---|
externalUserIdrequired | string | See above. |
dressroomId | string | Optional. |
imageBase64required | string | One photo. Up to ~10 garments detected. |
{ "ok": true, "drafts": [ { "id": "...", "name": "Cream Wool Blazer", "category": "outerwear", "imageUrl": "https://…" } ] }/api/v1/dressroom/scrapeSame drafts shape as detect| Field | Type | Description |
|---|---|---|
externalUserIdrequired | string | See above. |
dressroomId | string | Optional. |
urlrequired | string | A product, category, or shop page. |
/api/v1/dressroom/upload| Field | Type | Description |
|---|---|---|
externalUserIdrequired | string | See above. |
dressroomId | string | Optional. |
imageBase64required | string | One photo. |
categoryrequired | string | Required. |
name, description, price, link | string | Optional. |
{ "ok": true, "product": { "id": "...", "name": "...", "category": "outerwear", "imageUrl": "https://…" } }Neither detect nor scrape saves anything — save what your UI keeps with PUT inventory below.
Optional — Studio photo
Generate
Re-renders an existing piece's photo as a clean, high-detail studio product shot — same garment, better photo.
Takes an imageUrl already returned by detect, scrape, or upload and re-renders it — useful for a rough crop from a busy wardrobe photo, or a low-quality scraped product shot. Entirely optional: skip it and save the original photo with PUT inventory instead. Async: submit, then poll, same pattern as every other generation call.
/api/v1/dressroom/generateSubmit| Field | Type | Description |
|---|---|---|
externalUserIdrequired | string | See above. |
dressroomId | string | Optional. |
imageUrlrequired | string | A photo already returned by detect, scrape, or upload. |
name, category, description | string | Optional — improves the result by telling the model what it’s looking at. |
{ "ok": true, "jobId": "..." }
// poll: { "action": "poll", "jobId": "..." }
// → { "ok": true, "status": "done", "imageUrl": "https://..." }The returned imageUrl replaces the original for that piece — pass it along instead when you PUT inventory to save.
Step 2 — Save & read
/api/v1/dressroom/inventory?externalUserId=…&dressroomId=…/api/v1/dressroom/inventoryFull replace, up to 50 pieces| Field | Type | Description |
|---|---|---|
externalUserIdrequired | string | Body field on PUT, query param on GET. |
dressroomId | string | Optional. |
productsrequired | array | PUT only — the full wardrobe. Replaces whatever was there. |
{ "ok": true, "count": 4, "products": [ { "id": "p1", "name": "Linen blazer", "category": "outerwear", "imageUrl": "https://…" } ] }products in the response is the true saved state, not an echo of what you sent — an item with an unreachable image is dropped silently, so always read this back rather than assume.
Styling from this wardrobe
Not part of building a wardrobe — this is the same StyleMe Try-On step ZukMe Marketplace and Your Own Inventory use, sourced from this wardrobe instead of a catalog. Input: a StyleMe reading and a photo. Output: a rendered look, matched from the wardrobe you just saved. Async: submit, then poll.
/api/v1/dressroom/tryonSubmit| Field | Type | Description |
|---|---|---|
externalUserIdrequired | string | Whose wardrobe to style from. |
dressroomId | string | Optional. |
readingrequired | StyleMeReading | From POST /api/v1/styleme. |
photoBase64required | string | Photo to render the outfit onto. |
occasion | string | Optional, shapes which pieces are picked. |
bodyType | string | Optional override. |
callbackUrl | string | Optional webhook. See Callbacks below. |
{ "ok": true, "jobId": "..." }
// poll: { "action": "poll", "jobId": "..." }
// → { "ok": true, "status": "done", "imageUrl": "https://...", "outfitBreakdown": [...] }Each outfitBreakdown entry has a type: "matched" is a real piece from their wardrobe, worn exactly as photographed. A category with nothing good in their wardrobe gets "invented" instead of being left empty — an AI-designed piece, optionally paired with a real bestMatch "shop similar" suggestion that is never the piece actually rendered.
Callbacks (webhooks)
dressroom/tryon accept an optional callbackUrl on submit. When the job finishes (success or failure), we POST the same payload a completed poll would return — in addition to, never instead of, polling. Best-effort delivery with no retries, so keep polling as your source of truth.
// what we POST to callbackUrl on success:
{ "jobId": "...", "status": "done", "imageUrl": "https://...", "outfitBreakdown": [...] }
// on failure:
{ "jobId": "...", "status": "error", "error": "..." }callbackUrl must be a public http(s) URL — localhost and private network addresses are rejected.
Full example
Building the wardrobe — the Dressroom API itself:
const KEY = process.env.ZUKME_API_KEY;
const BASE = "https://zukme.com/api/v1/dressroom";
const externalUserId = "customer-42";
async function call(path, body) {
const res = await fetch(`${BASE}${path}`, {
method: "POST",
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ externalUserId, ...body }),
});
return res.json();
}
// 1. Scan a photo
const { drafts } = await call("/detect", { imageBase64 });
// 2. Your UI shows drafts for accept/reject — say the user kept all of them
// 3. Save
const { products } = await fetch(`${BASE}/inventory`, {
method: "PUT",
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ externalUserId, products: drafts }),
}).then((r) => r.json());Styling from it — a separate call into the StyleMe Try-On step (see StyleMe reading for how to get a reading first):
const submitted = await call("/tryon", { reading, photoBase64, occasion: "Weekend brunch" });
let result;
while (true) {
result = await call("/tryon", { action: "poll", jobId: submitted.jobId });
if (result.status === "done" || result.status === "error") break;
await new Promise((r) => setTimeout(r, 2500));
}
console.log(result); // { ok: true, status: "done", imageUrl: "...", outfitBreakdown: [...] }Building against a different source?