Developer Documentation

FashClick B2B API

Add photorealistic virtual try-on to your storefront, app, or pipeline. Upload a photo and a garment, get back a generated image in seconds.

Quickstart

Three steps to your first generated try-on:

  1. Create a FashClick account and grab a test API key from the API Keys page.
  2. Upload a person photo and a garment photo using the upload endpoints.
  3. Send the two returned IDs to /api/b2b/v1/tryon and poll the result (or subscribe to a webhook).

All calls go to:

Base URL
https://api.fashclick.io

Authentication

FashClick uses API keys sent via the X-Api-Key header (or Authorization: Bearer fck_... if you prefer). Every request to the B2B data plane must include exactly one key.

Keys look like fck_test_xxxxx... or fck_live_xxxxx.... Never expose them in client-side code — they belong on your server only.

curl https://api.fashclick.io/api/b2b/v1/me \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE"

Test mode vs Live mode

Every key carries an environment label. The same endpoints, but separated request logs, credits and webhooks.

Key prefixModeCharges creditsUse for
fck_test_TestYes (from your test wallet)Local development & CI
fck_live_LiveYes (from your live wallet)Production traffic

Account · GET /me

Confirms your API key is valid and returns the owning account's name, plan, and remaining credit balance. Useful as a "ping" during integration testing.

curl https://api.fashclick.io/api/b2b/v1/me \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE"

200 response:

{
  "accountName": "Acme Fashion",
  "email": "ops@acme.example",
  "planName": "Pro",
  "creditBalance": 408,
  "totalCredits": 565,
  "b2bApiEnabled": true
}

Upload a person image · POST /person-images

Multipart upload. The photo must be a JPG or PNG of one clearly visible person. The response gives you a personImageId you can re-use across many try-ons.

Person images are auto-deleted from FashClick storage 30 days after upload for GDPR compliance.

curl https://api.fashclick.io/api/b2b/v1/person-images \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE" \
  -F "file=@/path/to/person.jpg"

201 response:

{
  "personImageId": "5f9c9d7a-3b7b-4d6a-8c2e-9b8a5d4e3f12",
  "createdAtUtc": "2026-05-29T10:15:22Z",
  "expiresAtUtc": "2026-06-28T10:15:22Z"
}

Upload a garment image · POST /garment-images

Similar to person upload, but with a required category form field. The category tells the AI how to drape the garment.

categoryUse for
UpperBodyShirts, tops, jackets
LowerBodyTrousers, skirts, shorts
FullBodyDresses, jumpsuits, outfits
AccessoriesBags, hats, jewellery
curl https://api.fashclick.io/api/b2b/v1/garment-images \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE" \
  -F "file=@/path/to/shirt.jpg" \
  -F "category=UpperBody" \
  -F "title=Linen shirt — sky blue"

201 response:

{
  "garmentImageId": "a3b4c5d6-7e8f-4a9b-8c0d-1e2f3a4b5c6d",
  "category": "UpperBody",
  "title": "Linen shirt — sky blue",
  "createdAtUtc": "2026-05-29T10:18:01Z"
}

Single try-on · POST /tryon

One person + one garment → one image. Costs 1 credit. Use this for the common case (e.g. "show this customer wearing this product").

curl https://api.fashclick.io/api/b2b/v1/tryon \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
  "personImageId": "5f9c9d7a-3b7b-4d6a-8c2e-9b8a5d4e3f12",
  "garmentImageId": "a3b4c5d6-7e8f-4a9b-8c0d-1e2f3a4b5c6d",
  "metadata": "order:1234"
}'

202 response (the request is queued):

{
  "requestId": "11111111-2222-3333-4444-555555555555",
  "status": "queued",
  "totalCombinations": 1,
  "creditsReserved": 1,
  "estimatedSeconds": 35,
  "createdAtUtc": "2026-05-29T10:20:00Z",
  "links": {
    "status":  "/api/b2b/v1/tryon/11111111-2222-3333-4444-555555555555",
    "results": "/api/b2b/v1/tryon/11111111-2222-3333-4444-555555555555/results"
  }
}

Poll /tryon/{requestId}/results every few seconds until status is completed — or, much better, subscribe to the TryOn.Completedwebhook.

Multi try-on · POST /tryon/multi

One person + N garments → N images. Each garment produces its own image. Reserves N credits up-front; only successful images are actually charged. Up to 25 garments per request.

curl https://api.fashclick.io/api/b2b/v1/tryon/multi \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
  "personImageId": "5f9c9d7a-3b7b-4d6a-8c2e-9b8a5d4e3f12",
  "garments": [
    { "garmentImageId": "shirt-uuid" },
    { "garmentImageId": "trousers-uuid" },
    { "garmentImageId": "jacket-uuid" }
  ],
  "metadata": "order:1234"
}'

202 response:

{
  "requestId": "22222222-3333-4444-5555-666666666666",
  "status": "queued",
  "totalCombinations": 3,
  "creditsReserved": 3,
  "estimatedSeconds": 65,
  "createdAtUtc": "2026-05-29T10:21:00Z",
  "links": {
    "status":  "/api/b2b/v1/tryon/22222222-3333-4444-5555-666666666666",
    "results": "/api/b2b/v1/tryon/22222222-3333-4444-5555-666666666666/results"
  }
}

With multi try-on you'll often want streaming results. Subscribe to TryOn.ResultReady and you'll get one webhook per image as it finishes, instead of waiting for the whole request.

Batch try-on · POST /tryon/batch

Submit many independent try-on items in one call. Each item maps to one B2BTryOnRequest internally. Up to 100 items per batch, each item up to 25 garments.

All items share the same batchId so you can either:

  • Wait for the Batch.Completed webhook (one event at the end), or
  • React to each TryOn.Completed webhook as items finish, plus a Batch.Progress snapshot after every one.
curl https://api.fashclick.io/api/b2b/v1/tryon/batch \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "personImageId": "person-1-uuid",
      "garments": [{ "garmentImageId": "shirt-uuid" }]
    },
    {
      "personImageId": "person-2-uuid",
      "garments": [
        { "garmentImageId": "dress-uuid" },
        { "garmentImageId": "jacket-uuid" }
      ]
    }
  ],
  "metadata": "campaign:spring-2026"
}'

202 response:

{
  "batchId": "33333333-4444-5555-6666-777777777777",
  "status": "queued",
  "totalItems": 2,
  "totalCombinations": 3,
  "creditsReserved": 3,
  "estimatedSeconds": 65,
  "createdAtUtc": "2026-05-29T10:23:00Z",
  "items": [
    {
      "requestId": "...",
      "personImageId": "person-1-uuid",
      "totalCombinations": 1,
      "links": { "status": "...", "results": "..." }
    },
    {
      "requestId": "...",
      "personImageId": "person-2-uuid",
      "totalCombinations": 2,
      "links": { "status": "...", "results": "..." }
    }
  ],
  "links": { "status": "/api/b2b/v1/tryon/batch/33333333-..." }
}

Status & results

Two read endpoints per request, plus a batch-level status endpoint:

EndpointReturns
GET /tryon/{requestId}Aggregate status, counts, credits charged
GET /tryon/{requestId}/resultsArray of per-combination results with image URLs
GET /tryon/batch/{batchId}Batch-level roll-up plus a list of child requests
curl https://api.fashclick.io/api/b2b/v1/tryon/REQUEST_ID/results \
  -H "X-Api-Key: fck_test_YOUR_KEY_HERE"

200 response:

{
  "status": "completed",
  "results": [
    {
      "resultId": "...",
      "garmentImageId": "shirt-uuid",
      "status": "completed",
      "imageUrl": "https://cdn.fashclick.io/.../shirt-result.jpg",
      "durationMs": 9342,
      "completedAtUtc": "2026-05-29T10:23:42Z"
    }
  ]
}

Webhooks — setup & signing

Polling works, but webhooks are faster and cheaper. Create an endpoint in your Developer dashboard — we'll POST event payloads there with HMAC-SHA256 signatures so you can verify they came from FashClick.

Each request carries three headers:

HeaderMeaning
X-FashClick-EventThe event type, e.g. TryOn.Completed
X-FashClick-TimestampUnix epoch seconds of delivery
X-FashClick-SignatureHex HMAC-SHA256 of timestamp + "." + body using your signing secret

Verify a signature:

# Verification is server-side only — not a curl one-liner.
# See the JavaScript / Python / C# tabs.

Failed deliveries are retried with exponential backoff: 30s, 2m, 10m, 1h, 6h. After the fifth failure the delivery is abandoned and the endpoint is flagged in the dashboard.

Webhook event reference

Subscribe to whichever events your integration cares about. They split into three families: per-request terminal, batch roll-up, and streaming progress.

EventFires when
TryOn.CompletedA request finishes — all combinations succeeded
TryOn.PartiallyCompletedA request finishes — some succeeded, some failed
TryOn.FailedA request finishes — all combinations failed
TryOn.ResultReadyOne image inside a multi or batch request finishes (streaming)
Batch.CompletedLast item of a batch finishes, all items succeeded
Batch.PartiallyCompletedLast item of a batch finishes, mixed outcomes
Batch.FailedLast item of a batch finishes, every item failed
Batch.ProgressOne child request of a batch finishes (streaming)
Credits.LowWallet balance drops below your low-balance threshold
Credits.ExhaustedWallet balance hits zero
ApiKey.RotatedAn API key was rotated in the dashboard

TryOn.Completed payload

Fires once per request when all its images are done. The results array contains every generated image so you don't need a follow-up /results call.

{
  "event": "TryOn.Completed",
  "deliveredAtUtc": "2026-05-29T10:24:08Z",
  "data": {
    "requestId": "...",
    "batchId": null,
    "personImageId": "person-1-uuid",
    "mode": "multiple_garments",
    "status": "completed",
    "totalCombinations": 3,
    "successCount": 3,
    "failedCount": 0,
    "creditsCharged": 3,
    "creditsRemaining": 405,
    "completedAtUtc": "2026-05-29T10:24:07Z",
    "results": [
      {
        "resultId": "...",
        "garmentImageId": "shirt-uuid",
        "status": "completed",
        "imageUrl": "https://cdn.fashclick.io/.../shirt.jpg",
        "failureCode": null,
        "failureMessage": null,
        "completedAtUtc": "2026-05-29T10:23:42Z"
      },
      { "...": "two more results" }
    ]
  },
  "metadata": "order:1234"
}

Streaming progress webhooks

For long-running multi or batch try-ons, two extra events let you render results as they arrive:

TryOn.ResultReady

Fires every time one image inside a multi or batch request lands a terminal state. Skipped for plain single try-ons (the final TryOn.Completed already has the same info).

{
  "event": "TryOn.ResultReady",
  "data": {
    "requestId": "...",
    "batchId": null,
    "mode": "multiple_garments",
    "result": {
      "resultId": "...",
      "personImageId": "person-1-uuid",
      "garmentImageId": "shirt-uuid",
      "status": "completed",
      "imageUrl": "https://cdn.fashclick.io/.../shirt.jpg",
      "failureCode": null,
      "failureMessage": null,
      "completedAtUtc": "2026-05-29T10:23:42Z"
    },
    "progress": {
      "totalCombinations": 25,
      "completedCount": 3,
      "failedCount": 0,
      "remainingCount": 22
    },
    "creditsRemaining": 397
  }
}

Batch.Progress

Fires after each child request in a batch finishes, with batch-level counters. Perfect for a real-time progress bar.

{
  "event": "Batch.Progress",
  "data": {
    "batchId": "...",
    "request": {
      "requestId": "...",
      "mode": "single",
      "status": "completed",
      "totalCombinations": 1,
      "successCount": 1,
      "failedCount": 0,
      "creditsCharged": 1
    },
    "progress": {
      "totalItems": 10,
      "completedItems": 4,
      "partiallyCompletedItems": 0,
      "failedItems": 0,
      "remainingItems": 6,
      "totalCombinations": 10,
      "successCount": 4,
      "failedCount": 0
    },
    "creditsRemaining": 396
  }
}

Errors

Validation errors return HTTP 400 with a structured body. Auth errors are 401; plan/permission errors are 403; rate-limited requests are 429.

Example: insufficient credits

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "statusCode": 400,
  "message": "Validation failed.",
  "errors": {
    "credits": [
      "Insufficient credits for this batch. This batch needs 15 credits, your wallet has 10. You are short by 5. Top up your wallet and resubmit."
    ]
  }
}

Example: unknown person image

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "statusCode": 400,
  "message": "Validation failed.",
  "errors": {
    "personImageId": [
      "Person image not found or not owned by this account."
    ]
  }
}

Credits & billing

Each successful image charges one credit. Failed images are free. Credits come from your subscription plan or one-off credit packs — top up from the My Plan page.

Every TryOn.* and Batch.* webhook includes creditsCharged and creditsRemaining, so your backend can reconcile balances without an extra API call.

Changelog

2026-05-29
Streaming progress webhooks. Added TryOn.ResultReady and Batch.Progress so you can render images one at a time instead of waiting for the whole multi/batch request.
2026-05-28
Multi try-on endpoint. Split /tryon into a strict single endpoint (one person + one garment) and a new /tryon/multi for the N-garment case. The webhook payload now includes a mode field plus the full results[] array with image URLs.
2026-05-27
Batch roll-up events. Added Batch.Completed, Batch.PartiallyCompleted, and Batch.Failed. Insufficient-credit errors now include the exact shortfall.

Ready to integrate?

Sign up to grab a test key. No card required.

Get an API key →