Clothing AI Try-On — API Reference
This document covers the digital-twin and virtual-fitting endpoints of the WEARFITS GenAI API. For 3D shoe generation (/api/v1/shoe-3d) refer to the separate Shoe-3D API Reference.
Base Information
| Property | Value |
|---|---|
| Production base URL | https://api.wearfits.com |
| OpenAPI spec | api.wearfits.com/doc |
| Interactive docs (Swagger UI) | api.wearfits.com/reference |
| Authentication header | X-API-Key |
All /api/* routes require the X-API-Key header on every request.
Authentication error codes
Authentication failures include a machine-readable error.code field. When an account-scoped credential can no longer be verified, the API returns HTTP 401 with ACCOUNT_TOKEN_INVALID. Discard that credential, obtain a new one, and retry the request.
Do not branch on the human-readable error message. Other API-key authentication failures continue to use HTTP_ERROR and are not fixed by replacing an account-scoped credential.
Bot Protection for Clothing Requests
Protected browser-facing clothing requests accept either a single-use X-Turnstile-Token or a valid signed verification pass. A successful verification pass is valid for one hour from verification, is bound to the originating client network, and is not extended by later use. Preserve cookies between requests when relying on the pass.
Successful protected responses expose the remaining pass lifetime in the X-Wearfits-Verification-TTL response header, in seconds. If the pass expires or the client network changes, perform a new verification. The pass supplements X-API-Key authentication; it does not replace it.
Image Input Formats
Every image field in the API accepts one of two formats:
HTTP/HTTPS URL — a publicly accessible URL pointing to a hosted image.
Base64 Data URL — an inline base64-encoded image embedded directly in the JSON body.
Supported MIME types: image/png, image/jpeg, image/jpg, image/gif, image/webp, image/svg+xml.
Available Poses
The poseId field accepts the following values across all endpoints that support posing:
| poseId | Description |
|---|---|
standing_arms_down |
Neutral standing, arms at sides |
man_pose |
Male standing pose |
girl_pose |
Female standing pose (default) |
shoe_girl_pose |
Female pose optimized for footwear display |
Body Measurement Ranges
When supplying bodyMeasurements, values must fall within these validation bounds. At least two measurements are required.
| Field | Unit | Min | Max |
|---|---|---|---|
height |
cm | 140 | 210 |
chest |
cm | 70 | 160 |
waist |
cm | 55 | 170 |
hip |
cm | 70 | 170 |
inseam |
cm | 60 | 100 |
These are validation bounds, not a guarantee that every value has an exact reference match. Values within the bounds may be matched to the nearest available body shape, so the generated result can represent an extrapolative match.
Core Endpoints
POST /api/v1/digital-twin
Generate a photorealistic digital twin (avatar) for use in virtual try-on sessions. This is an asynchronous operation — the endpoint returns a jobId which you poll via GET /api/v1/jobs/{jobId} to retrieve the digitalTwinId on completion.
Input Modes
| Mode | Required fields | How it works |
|---|---|---|
| Photo Mode | faceImage + bodyPhotoUrl |
SAM-3D extracts the body mesh automatically from the full-body photo |
| Size Mode (measurements) | faceImage + bodyMeasurements |
Nearest-neighbour body mask lookup from the BodyM dataset |
| Size Mode (clothing size) | faceImage + clothingSize |
Simplified size input; converted internally to measurements |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
faceImage |
string | Yes | URL or Base64 Data URL of the user's face |
bodyPhotoUrl |
string | Photo Mode | URL or Base64 Data URL of full-body photo |
bodyMeasurements |
object | Size Mode | Body dimensions object — see ranges above |
bodyMeasurements.height |
number | — | Height in cm |
bodyMeasurements.chest |
number | — | Chest circumference in cm |
bodyMeasurements.waist |
number | — | Waist circumference in cm |
bodyMeasurements.hip |
number | — | Hip circumference in cm |
bodyMeasurements.inseam |
number | — | Inseam length in cm |
clothingSize |
object | Size Mode | Simplified size input |
clothingSize.height |
number | — | Height in cm |
clothingSize.size |
string | — | Standard clothing size (e.g. "M", "L", "XL") |
gender |
string | Recommended | "male" or "female" |
poseId |
string | No | Target pose — see Available Poses above |
Response
Once the job reaches completed status, GET /api/v1/jobs/{jobId} returns:
Example — Photo Mode
curl -X POST https://api.wearfits.com/api/v1/digital-twin \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"faceImage": "https://example.com/user-face.jpg",
"bodyPhotoUrl": "https://example.com/user-body.jpg",
"gender": "female",
"poseId": "girl_pose"
}'
Example — Size Mode (measurements)
curl -X POST https://api.wearfits.com/api/v1/digital-twin \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"faceImage": "https://example.com/user-face.jpg",
"bodyMeasurements": {
"height": 170,
"chest": 88,
"waist": 70,
"hip": 96
},
"gender": "female"
}'
Example — Size Mode (clothing size)
curl -X POST https://api.wearfits.com/api/v1/digital-twin \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"faceImage": "https://example.com/user-face.jpg",
"clothingSize": {
"height": 175,
"size": "M"
},
"gender": "male"
}'
GET /api/v1/digital-twin/{id}
Verify that a digitalTwinId still exists in cache. Useful when persisting a twin ID in client storage (cookies, localStorage) to confirm it is still available before initiating a try-on session without regenerating the avatar.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | The digitalTwinId to validate |
Response — valid twin
Response — expired or unknown twin
HTTP 404 or { "valid": false }.
Caching policy: Digital twins are cached for 30 days from generation. After expiry, a new twin must be generated via POST /api/v1/digital-twin.
Example
POST /api/v1/virtual-fitting
The primary virtual try-on pipeline. Applies one or more garments onto a digital twin and returns a photorealistic composite image. The request may complete immediately when a reusable result is available. Otherwise, it is asynchronous and returns a jobId with status: "queued" for polling.
You can either supply a pre-generated digitalTwinId (recommended for returning users) or pass inline body/face data to generate a twin on-the-fly within the same request.
Request Body
Identity — choose one approach:
| Field | Type | Description |
|---|---|---|
digitalTwinId |
string | ID of a previously generated digital twin (preferred) |
faceImage |
string | URL or Base64; used with inline body data |
photoUrl |
string | Full-body photo URL or Base64 (Photo Mode inline) |
bodyMeasurements |
object | Body dimensions (Size Mode inline) |
clothingSize |
object | Simplified size (Size Mode inline) |
Garments — at least one garment field is required:
| Field | Type | Description |
|---|---|---|
topGarment |
string | [string, string] | Single image URL/Base64, or a pair [front, back] |
bottomGarment |
string | [string, string] | Bottom garment image(s) |
fullBodyGarment |
string | [string, string] | Full-body garment (e.g. dress, jumpsuit) |
shoes |
string | [string, string] | Shoe image(s) |
Presentation options:
| Field | Type | Description |
|---|---|---|
gender |
string | "male" or "female" |
poseId |
string | Target pose — see Available Poses above |
Response — queued
A completed fitting can also be returned directly from the submission request. When inline body data creates or reuses a digital twin, the response may include digitalTwinId:
{
"success": true,
"digitalTwinId": "twin_xyz789",
"jobId": "job_def456",
"status": "completed",
"results": [
{
"url": "https://api.wearfits.com/files/result.png"
}
]
}
Use the returned results directly when status is completed; do not make an additional poll. Only poll GET /api/v1/jobs/{jobId} when the submission response has status: "queued". A completed response may also include digitalTwinId when inline body data produced or reused a twin.
Example — fitting with pre-generated twin
curl -X POST https://api.wearfits.com/api/v1/virtual-fitting \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"digitalTwinId": "twin_xyz789",
"topGarment": "https://example.com/shirt-front.jpg",
"bottomGarment": "https://example.com/trousers.jpg",
"gender": "male",
"poseId": "man_pose"
}'
Example — fitting with front and back garment images
curl -X POST https://api.wearfits.com/api/v1/virtual-fitting \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"digitalTwinId": "twin_xyz789",
"topGarment": [
"https://example.com/jacket-front.jpg",
"https://example.com/jacket-back.jpg"
],
"gender": "female",
"poseId": "girl_pose"
}'
Example — inline body data (no pre-generated twin)
curl -X POST https://api.wearfits.com/api/v1/virtual-fitting \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"faceImage": "https://example.com/user-face.jpg",
"bodyMeasurements": {
"height": 168,
"waist": 68,
"hip": 94
},
"fullBodyGarment": "https://example.com/dress.jpg",
"gender": "female"
}'
POST /api/v1/size-fitting
Estimate clothing sizes from the private body profile associated with a digital twin. This is a synchronous, non-billable calculation.
Only digital twins created from bodyMeasurements or clothingSize are eligible. Photo-mode and other direct-photo twins return available: false.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
digitalTwinId |
string | Yes | ID of an eligible digital twin |
products |
array | Yes | Products to evaluate against their body-measurement size charts |
products[].id |
string | Yes | Product identifier |
products[].category |
string | Yes | top, bottom, or fullBody |
products[].sizeChart |
object | Yes | Versioned body-measurement chart |
sizeChart.version |
number | Yes | Must be 1 |
sizeChart.basis |
string | Yes | Must be "body"; provide body ranges, not garment dimensions |
sizeChart.unit |
string | Yes | "cm" or "in" |
sizeChart.sizes |
array | Yes | One or more size rows, with unique labels |
sizes[].label |
string | Yes | Display label such as "S", "M", or "L" |
sizes[].measurements |
object | Yes | Optional ranges for height, chest, waist, hip, and inseam |
measurements.<dimension>.min |
number | Yes when dimension is supplied | Lower bound of the body range |
measurements.<dimension>.max |
number | Yes when dimension is supplied | Upper bound; must be greater than or equal to min |
Every size row must share at least one relevant dimension for the product category. The relevant dimensions are chest and waist for top, waist, hip, and inseam for bottom, and chest, waist, hip, and height for fullBody. Ranges supplied in inches are converted before evaluation.
Example
curl -X POST https://api.wearfits.com/api/v1/size-fitting \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"digitalTwinId": "twin_xyz789",
"products": [{
"id": "shirt-1",
"category": "top",
"sizeChart": {
"version": 1,
"basis": "body",
"unit": "cm",
"sizes": [
{
"label": "S",
"measurements": {
"chest": { "min": 84, "max": 92 },
"waist": { "min": 68, "max": 76 }
}
},
{
"label": "M",
"measurements": {
"chest": { "min": 92, "max": 100 },
"waist": { "min": 76, "max": 84 }
}
}
]
}
}]
}'
The HTTP 200 response reports whether sizing is available and, when it is, returns estimated recommendations with per-dimension fit, loose, or tight statuses and bounded scores. A profile that cannot be used for sizing returns available: false. Treat missing optional response fields as unavailable rather than as errors.
Job Management Endpoints
GET /api/v1/jobs/{jobId}
Poll the status and retrieve results of any asynchronous job (digital twin generation, virtual fitting, etc.). For digital-twin and virtual-fitting jobs, progress.stage reports the lifecycle stage while the job is running; progress.percentage is omitted until completion, when it is 100. Render indeterminate progress while the percentage is absent rather than treating the stage as an estimated countdown.
Job-status responses include Cache-Control: no-store, so clients should not reuse cached polling responses. This prevents HTTP cache reuse but does not guarantee immediate visibility if status storage is still propagating. Client-side polling should also disable its request cache, for example with cache: "no-store" in fetch.
For digital-twin and virtual-fitting jobs, poll at a one-second start-to-start cadence without overlapping requests, for up to 600 attempts (approximately ten minutes). If a status request fails, retry after two seconds. Other tools can continue using a two-second response-to-start interval. Stop polling as soon as the job reaches a terminal status.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
jobId |
string | Job ID returned by a submission endpoint |
Job Status Values
| Status | Meaning |
|---|---|
queued |
Job is waiting in the processing queue |
validating |
Input data is being validated |
processing |
AI models are generating the result |
uploading |
Output is being uploaded to storage |
completed |
Job finished successfully; result is populated |
failed |
Job failed; error field describes the cause |
Response — in-progress
For virtual-fitting and digital-twin jobs, the percentage is absent while processing:
Response — completed virtual fitting
{
"jobId": "job_def456",
"status": "completed",
"progress": 100,
"result": {
"imageUrl": "https://cdn.wearfits.com/results/job_def456/output.jpg"
}
}
Response — completed digital twin
{
"jobId": "job_abc123",
"status": "completed",
"progress": 100,
"result": {
"digitalTwinId": "twin_xyz789"
}
}
Example — polling loop (bash)
JOB_ID="job_def456"
while true; do
# Keep polling one second start-to-start without overlapping requests.
SECONDS=0
RESPONSE=$(curl -s "https://api.wearfits.com/api/v1/jobs/$JOB_ID" \
-H "X-API-Key: your_api_key_here")
STATUS=$(echo "$RESPONSE" | jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then
echo "$RESPONSE" | jq .
break
fi
WAIT=$((1 - SECONDS))
if [ "$WAIT" -gt 0 ]; then
sleep "$WAIT"
fi
done
Example — polling loop (JavaScript)
async function pollJob(jobId, apiKey, intervalMs = 1000) {
const url = `https://api.wearfits.com/api/v1/jobs/${jobId}`;
while (true) {
const pollStartedAt = performance.now();
const res = await fetch(url, {
cache: 'no-store',
headers: { 'X-API-Key': apiKey },
});
const job = await res.json();
if (job.status === 'completed') {
return job.result;
}
if (job.status === 'failed') {
throw new Error(`Job failed: ${job.error}`);
}
// Start the next request one second after this request started.
const waitMs = Math.max(0, intervalMs - (performance.now() - pollStartedAt));
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}
DELETE /api/v1/jobs/{jobId}
Cancel a running or queued job immediately.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
jobId |
string | ID of the job to cancel |
Response
HTTP 200 on successful cancellation.
Example
Secondary Endpoints
The following endpoints provide standalone or utility functionality. They follow the same authentication and job-polling patterns as the core endpoints.
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/tryon/clothing |
Standalone clothing try-on (without a full digital twin pipeline) |
| POST | /api/v1/tryon/shoes |
Standalone shoe try-on |
| POST | /api/v1/generate |
Generate images from a text prompt |
| POST | /api/v1/pose-transfer |
Transfer the pose of one person onto another |
Recommended Integration Flow
The following pattern covers the typical e-commerce use case: a user visits a product page, generates a personal avatar once, then tries on multiple garments using that avatar.
1. User uploads face photo (+ body photo OR measurements OR clothing size)
│
▼
2. POST /api/v1/digital-twin
│
▼
3. Poll GET /api/v1/jobs/{jobId} ──► status: completed
│
▼ result.digitalTwinId
4. Store digitalTwinId client-side (cookie / localStorage)
│
On subsequent visits:
5. GET /api/v1/digital-twin/{id} ──► valid: true (skip step 1–4)
│
▼
6. POST /api/v1/virtual-fitting { digitalTwinId, topGarment, … }
│
├── status: completed ──► use returned results
│
└── status: queued
│
▼
7. Poll GET /api/v1/jobs/{jobId} ──► status: completed
│
▼ result.imageUrl / results[].url
8. Display try-on image
Webhook Signatures
If you configure a webhook URL to receive job completion notifications, each request is signed so you can verify its authenticity.
Signing algorithm: HMAC-SHA256
Secret derivation: hex( SHA-256( your_api_key ) )
Verification steps:
-
Derive the signing secret:
-
Sort the JSON body keys alphabetically and serialize to a canonical JSON string.
-
Compute the HMAC-SHA256 of the canonical JSON string using the signing secret.
-
Compare your computed signature against the value in the
X-Webhook-Signaturerequest header.
Python verification example:
import hashlib
import hmac
import json
def verify_webhook(payload: bytes, received_signature: str, api_key: str) -> bool:
# Derive signing secret from API key
signing_secret = hashlib.sha256(api_key.encode()).hexdigest()
# Canonical form: parse and re-serialize with sorted keys
body = json.loads(payload)
canonical = json.dumps(body, sort_keys=True, separators=(',', ':'))
# Compute expected signature
expected = hmac.new(
signing_secret.encode(),
canonical.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received_signature)
Node.js verification example:
const crypto = require('crypto');
function verifyWebhook(rawBody, receivedSignature, apiKey) {
// Derive signing secret from API key
const signingSecret = crypto
.createHash('sha256')
.update(apiKey)
.digest('hex');
// Canonical form: parse and re-serialize with sorted keys
const body = JSON.parse(rawBody);
const sorted = JSON.stringify(body, Object.keys(body).sort());
// Compute expected signature
const expected = crypto
.createHmac('sha256', signingSecret)
.update(sorted)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
Webhook Delivery and Retries
Webhook delivery is at least once, not exactly once. Duplicate notifications are possible, so make the webhook handler idempotent and deduplicate using the job ID or another stable event identifier.
WEARFITS retries webhook enqueue and delivery failures. If a completed job is redelivered, the retry delivers the notification and performs idempotent completion bookkeeping; it does not regenerate the fitting or digital twin.
For a cache-hit fitting, WEARFITS enqueues the webhook before returning success. If the webhook cannot be enqueued, the request returns HTTP 500 and neither usage counter is charged. Retry the request safely; a successful retry reuses the cached result.
Rate Limits
Rate limits are enforced per API key.
| Endpoint category | Limit |
|---|---|
| Job submission | 50 requests / minute |
| Job status polling | 500 requests / minute |
| File downloads | 300 requests / minute |
| Health check | 5 000 requests / minute |
Limits are 10× higher when using localhost or the DEV_API_KEY (development environment only).
If you exceed a limit, the API returns HTTP 429 Too Many Requests. Implement exponential back-off in your polling loop to stay within the job-status limit during high-concurrency scenarios.