ZukMe
← ZukMe APIAPI Docs · Flow 3 of 4

StyleMe for Your Own Inventory API

Connect your website, Shopify, catalog, or in-store inventory in real time, and dress every visiting customer in a complete, best-fit outfit pulled straight from it, turning window-shoppers into buyers. Want ZukMe's catalog instead? See StyleMe from ZukMe Marketplace.

Start here

Quickstart

Your catalog

PUT /inventory/products

StyleMe

style reading

1 credit

Inventory Try-On

rendered look

1 credit
  1. 1Create an API key in the API Dashboard, then contact our team to get credits added — a new key starts with none.
  2. 2Push your catalog to PUT /api/v1/inventory/products — a full replace, free.
  3. 3Submit a photo to POST /api/v1/styleme for a style reading.
  4. 4Submit that reading and a photo to POST /api/v1/inventory/tryon, then poll until done.
What the end user actually sees

User experience flow

A plain walkthrough of what your shopper experiences on your own site or app — no implementation detail, just the moments. You build the interface; each beat notes which call powers it.

1

(Merchant-side, before any shopper arrives) Your catalog is synced

Not something a shopper sees — your product list is pushed once, kept current whenever it changes. Everything matched below only ever comes from this list.

PUT /api/v1/inventory/products

2

Uploads or picks a photo

One clear photo of themselves — the only input this flow needs.

POST /api/v1/styleme

3

A short wait while their style is read

One photo in; a few seconds later, a personalized style profile out.

POST /api/v1/styleme (poll)

4

Sees their style read back to them (optional)

A style title, a palette, a few personality notes — however much of the reading you choose to surface before moving on.

5

Picks an occasion (optional)

"Client presentation," "Weekend brunch," whatever your UI offers — shapes which pieces from your catalog get shopped for next.

6

A short wait while an outfit is matched and rendered

Pieces from your own synced catalog — nowhere else — are matched to the reading and rendered onto their photo.

POST /api/v1/inventory/tryon

7

Sees themselves in the finished look

The rendered photo, plus every piece used broken out individually — name, price, your own product link. A category with nothing good in your catalog gets an AI-designed piece instead of being left empty, optionally paired with a real "shop similar" suggestion — see the Try-On response below.

8

Taps a piece to buy it

Every item links straight back to your own storefront — the whole point of syncing your own catalog.

Base URL

https://zukme.com

All 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. Syncing your catalog is always free. 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

FieldTypeDescription
400Bad RequestMissing or invalid fields in the request body.
401UnauthorizedMissing, malformed, or revoked API key.
402Payment RequiredCredits exhausted (code CREDITS_EXHAUSTED), with your current looks/bonus/total balance in the body.
404Not FoundjobId does not exist (expired, wrong id, or never submitted).
429Too Many RequestsRate limit exceeded for this key. See retryAfterSeconds.

Example 402 body:

{
  "error": "CREDITS_EXHAUSTED",
  "code": "CREDITS_EXHAUSTED",
  "looks": 0,
  "bonus": 0,
  "total": 0
}

Step 1 — Sync your catalog

A full replace: whatever you send becomes your entire matchable catalog. Free — no credits spent syncing.

PUT/api/v1/inventory/products
FieldTypeDescription
productsrequiredInventoryProduct[]Your full product list. Replaces whatever was previously synced, up to 5000 items.

The InventoryProduct object

FieldTypeDescription
idrequiredstringYour own stable product id (e.g. SKU).
namerequiredstringProduct name.
imageUrlrequiredstringPublicly fetchable product photo URL.
pricestringDisplay price, e.g. "USD 84".
descriptionstringFree text that helps matching quality.
categorystringFree text, e.g. "top", "shoes" (helps matching quality, doesn't need to be an exact match).
linkstringProduct page URL on your own site.

Response:

{ "ok": true, "count": 128 }
GET/api/v1/inventory/products

Returns whatever is currently synced, to confirm a push landed:

{ "ok": true, "products": [ { "id": "sku-001", "name": "Tailored Wool Blazer", ... } ] }

Step 2 — StyleMe reading

The personalized style reading Step 3 needs. Full field reference: StyleMe reading fields.

curl https://zukme.com/api/v1/styleme -X POST \
  -H "Authorization: Bearer zk_live_..." -H "Content-Type: application/json" \
  -d '{ "photoBase64": "<base64>" }'
# → { "ok": true, "jobId": "a91c...", "status": "processing" }
# poll with { "action": "poll", "jobId": "a91c..." }
# → { "ok": true, "status": "done", "reading": { ... } }

Step 3 — Inventory Try-On

Matches the best piece per garment category from your synced inventory and renders them onto the photo. Async: submit, then poll.

POST/api/v1/inventory/tryon
FieldTypeDescription
readingrequiredStyleMeReadingThe reading object returned by POST /api/v1/styleme.
photoBase64requiredstringBase64-encoded photo of the person to render the outfit onto.
occasionstringOptional occasion (e.g. "Client presentation") that shapes which pieces are shopped for.
bodyTypestringOptional body type override, e.g. "M", "Petite". Defaults to the reading's own inferred bodyType.
callbackUrlstringOptional webhook. See Callbacks below.

Response (submitted):

{ "ok": true, "jobId": "d81f...", "status": "processing" }
POST/api/v1/inventory/tryon

Response when done:

{
  "ok": true,
  "status": "done",
  "imageUrl": "https://...",
  "outfitBreakdown": [
    {
      "category": "top",
      "type": "matched",
      "productId": "sku-001",
      "productName": "Tailored Wool Blazer",
      "imageUrl": "https://...",
      "price": "USD 220",
      "source": "inventory"
    },
    {
      "category": "shoes",
      "type": "invented",
      "reason": "Your catalog had nothing in this category for the look.",
      "brief": "Sleek black ankle boots with a low block heel.",
      "bestMatch": {
        "productId": "cj-9182",
        "productName": "Leather Block-Heel Boot",
        "shopName": "Studio Nine",
        "imageUrl": "https://...",
        "price": "USD 96",
        "clickUrl": "https://...",
        "explanation": "Closest real match for the designed silhouette."
      }
    }
  ]
}

Every entry has a type."matched" is a real piece from your synced catalog, worn exactly as photographed."invented" means your catalog had nothing good for that category — the piece worn in the photo was designed by the AI instead of left empty, and (when available) bestMatch is a real, purchasable product that resembles it, offered purely as a "shop similar" suggestion — it is never the piece actually rendered on the photo.

Callbacks (webhooks)

runway-edit and inventory/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

curl
# 1. Sync your catalog once (Step 1)
curl https://zukme.com/api/v1/inventory/products -X PUT \
  -H "Authorization: Bearer zk_live_..." -H "Content-Type: application/json" \
  -d '{ "products": [ { "id": "sku-001", "name": "Tailored Wool Blazer", "imageUrl": "https://...", "category": "outerwear" } ] }'

# 2. Get a StyleMe reading (Step 2), then submit a try-on
curl https://zukme.com/api/v1/inventory/tryon -X POST \
  -H "Authorization: Bearer zk_live_..." -H "Content-Type: application/json" \
  -d '{ "reading": { /* from POST /api/v1/styleme */ }, "photoBase64": "<base64>", "occasion": "Client presentation" }'
# → { "ok": true, "jobId": "d81f...", "status": "processing" }

# 3. Poll until done
curl https://zukme.com/api/v1/inventory/tryon -X POST \
  -H "Authorization: Bearer zk_live_..." -H "Content-Type: application/json" \
  -d '{ "action": "poll", "jobId": "d81f..." }'
# → { "ok": true, "status": "done", "imageUrl": "https://...", "outfitBreakdown": [...] }
Node.js
const KEY = process.env.ZUKME_API_KEY;
const BASE = "https://zukme.com/api/v1";

async function call(path, body) {
  const res = await fetch(`${BASE}${path}`, {
    method: path.endsWith("/products") ? "PUT" : "POST",
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  return res.json();
}

await call("/inventory/products", { products: myCatalog });

const submitted = await call("/inventory/tryon", { reading, photoBase64, occasion: "Client presentation" });

let result;
while (true) {
  result = await call("/inventory/tryon", { action: "poll", jobId: submitted.jobId });
  if (result.status === "done" || result.status === "error") break;
  await new Promise((r) => setTimeout(r, 2500));
}

console.log(result);

ZukMe Global Network

North America

United States 7901 4th St N, Suite 300, St Petersburg, Florida 33702, United States

+1 850 696 6297

International Offices

Ghana: GS-0168-9885, Attah Mills Street, Opp. Downtown Pub, Old Barrier, Accra, Ga South, Greater Accra

Rwanda: KK 734 St, Kigali, Rwanda

+233(0)505807777

© 2026 ZukMe LLC. All rights reserved.