Skip to content
View Markdown

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.

X-API-Key: your_api_key_here

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.

data:image/<format>;base64,<base64-encoded-data>

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

{
  "jobId": "job_abc123",
  "estimatedTime": 30
}

Once the job reaches completed status, GET /api/v1/jobs/{jobId} returns:

{
  "status": "completed",
  "result": {
    "digitalTwinId": "twin_xyz789"
  }
}

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

{
  "valid": true
}

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

curl https://api.wearfits.com/api/v1/digital-twin/twin_xyz789 \
  -H "X-API-Key: your_api_key_here"

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

{
  "success": true,
  "jobId": "job_def456",
  "status": "queued",
  "estimatedProcessingTime": 20
}

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:

{
  "jobId": "job_abc123",
  "status": "processing",
  "progress": {
    "stage": "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

curl -X DELETE https://api.wearfits.com/api/v1/jobs/job_def456 \
  -H "X-API-Key: your_api_key_here"

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

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:

  1. Derive the signing secret:

    import hashlib
    signing_secret = hashlib.sha256(api_key.encode()).hexdigest()
    

  2. Sort the JSON body keys alphabetically and serialize to a canonical JSON string.

  3. Compute the HMAC-SHA256 of the canonical JSON string using the signing secret.

  4. Compare your computed signature against the value in the X-Webhook-Signature request 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.