Skip to content
View Markdown

2D to 3D Generator — API Reference

Base URL: https://api.wearfits.com

Interactive documentation: - OpenAPI Spec: api.wearfits.com/doc - Swagger UI: api.wearfits.com/reference - Shoe AR Integration Guide: tryon.wearfits.com/docs/integration-shoes


Authentication

All /api/* routes require the X-API-Key header. API keys are created and managed at dash.wearfits.com.

X-API-Key: your_api_key

Requests without a valid key return HTTP 401.

Browser-based WEARFITS flows also protect shoe generation, file upload, and job requests with Turnstile verification. The hosted application manages this automatically: successful verification sets a signed, HttpOnly pass cookie bound to the browser's network. The pass is valid for a fixed one hour from verification (use does not extend it) and can authorize later protected requests from that same network without another Turnstile check. A request with a Turnstile token uses that token and can mint a fresh pass; if a pass expires or is rejected, the application retries with fresh verification. Keep cookies enabled for these flows. Protected responses expose the remaining pass lifetime in X-Wearfits-Verification-TTL; a browser-protected request with neither a valid token nor pass returns HTTP 403. The verification pass does not replace X-API-Key for direct API integrations.


Image Input Formats

All images fields across the API accept two formats:

  1. URLs (HTTP/HTTPS) — standard web URLs pointing to publicly accessible hosted images.
  2. Base64 Data URLs — inline base64-encoded images embedded directly in the request body.
  3. Format: data:image/<format>;base64,<data>
  4. Supported image formats: png, jpeg, jpg, gif, webp, svg+xml

Endpoints

POST /api/v1/shoe-3d

Generate a high-quality 3D shoe model from one or more 2D product photos. If a model-specific failure occurs, WEARFITS automatically retries with an alternate supported model, when available, for up to four attempts total before marking the job failed. Clients generally do not need to resubmit for an individual model outage; poll the job and handle a final failed status if all attempts are unsuccessful. The request is accepted immediately and processed asynchronously. Use GET /api/v1/jobs/{jobId} to poll for results.

Returns: 202 Accepted with a jobId.

Request body

Field Type Required Description
images string[] Yes List of shoe photo URLs or base64 data URLs. Provide multiple angles for best results.
glbInput string No URL to an existing GLB file to use as the base for transformation or texture enhancement instead of generating from scratch. For large GLBs, upload the file with POST /api/v1/files/upload and pass the returned url here.
options object No Processing options (see below).
options.models string[] No Optional list of supported model identifiers for generation. The service uses its default selection when omitted.
options.modelName string No Optional 1–50 character display name for the model uploaded to WEARFITS.
options.uploadToWearFits boolean No After generation, automatically upload the model to WEARFITS AR.
options.sessionId string No Optional WEARFITS session identifier for ownership. When supplied, it takes precedence over the account associated with the API credential and is the only ownership claim used for the upload.
options.enhanceTexture boolean No Run an AI texture enhancement pass after 3D generation. Gap filling and baked ambient occlusion are enabled by default during this pass unless you opt out with the options below.
options.gapFillReproject boolean No Re-project texture gaps during texture enhancement. Defaults to true when enhanceTexture is enabled; set to false to opt out.
options.aoBake string No Bake ambient occlusion into the base-color texture during texture enhancement. Defaults to "baked" when enhanceTexture is enabled. Accepted values are "baked" and "off"; set to "off" to opt out.
options.aoStrength number No Baked ambient-occlusion strength from 0 to 1. Defaults to 0.7 when AO baking is enabled.
options.correctImage boolean No Apply professional packshot correction to the input photos before 3D generation.
options.productType string No Hint for the generation model. One of: shoe, other, or auto (default).
options.longestSideCm number No For bags and other non-shoe products, the real length of the longest horizontal side (width or depth, never the height), from 5 to 150 cm with 0.1 cm precision. Applies to productType: "other" and to "auto" when it resolves to a non-shoe; ignored for shoes.
options.refineLogo boolean No Run a second AI pass focused specifically on fixing logos and brand text on the generated model. Non-fatal: if the logo pass fails, the pipeline continues with first-pass results.
options.genaiQuality string No Controls texture enhancement quality and render count. "default" uses 4 views (3-minute timeout). "high" uses 6 views (5-minute timeout). Defaults to "high" when enhanceTexture is enabled.

When enhanceTexture is enabled, gap filling and baked ambient occlusion run by default. Set gapFillReproject: false and/or aoBake: "off" to disable either step. These options apply only during texture enhancement; explicitly enabling gap filling or AO baking with enhanceTexture: false returns HTTP 400.

For bags and other non-shoe products, options.longestSideCm sets the real length of the longest horizontal side (width or depth, never the height). Values must be between 5 and 150 cm and are stored with 0.1 cm precision. Out-of-range values return HTTP 400 before a job is created. If the value is omitted, the try-on keeps the model's own scale when it is plausible for a bag (between 10 and 100 cm across) and shows it at 40 cm otherwise. For productType: "other", submissions are never deduplicated and never reuse a WEARFITS link; each creates its own job and model.

Who the uploaded model belongs to

When options.uploadToWearFits is true, the generated model is assigned to exactly one WEARFITS owner:

  1. The account associated with the API credential (automatic). If the credential resolves to a WEARFITS account, that account's library receives the model. No additional field is required.
  2. options.sessionId (explicit). Use this when your frontend already has a WEARFITS user session and the model should be linked to that session.

options.sessionId takes precedence. When it is present, it is the only ownership claim used for the upload; the account associated with the credential is not used. Do not provide conflicting ownership claims.

If neither an account association nor a session is available, the model is placed in the integration's default WEARFITS library rather than linked to an individual session.

Example request

curl -X POST https://api.wearfits.com/api/v1/shoe-3d \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "images": [
      "https://example.com/shoe-side.jpg",
      "https://example.com/shoe-front.jpg",
      "https://example.com/shoe-back.jpg"
    ],
    "options": {
      "uploadToWearFits": true,
      "enhanceTexture": true,
      "correctImage": true,
      "productType": "shoe",
      "refineLogo": false,
      "genaiQuality": "high"
    }
  }'

Example request with base64 image

curl -X POST https://api.wearfits.com/api/v1/shoe-3d \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "images": [
      "data:image/jpeg;base64,/9j/4AAQSkZJRgAB..."
    ],
    "options": {
      "productType": "shoe"
    }
  }'

Example request with existing GLB

curl -X POST https://api.wearfits.com/api/v1/shoe-3d \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "images": [
      "https://example.com/shoe-packshot.jpg"
    ],
    "glbInput": "https://example.com/base-model.glb",
    "options": {
      "enhanceTexture": true,
      "uploadToWearFits": true
    }
  }'

When options.uploadToWearFits is enabled, the final GLB uploaded to WEARFITS must be no larger than 20 MB. The service attempts to compress oversized output before uploading it. If the result still exceeds the limit, the job fails instead of uploading the model. Use a smaller or simpler source model, or disable the WEARFITS upload.

Upload failures use these codes:

Code Meaning Recommended action
attribution_unavailable WEARFITS temporarily could not resolve the attributed account. Retry the request or job after the temporary service issue clears.
invalid_user_id The supplied session or account owner identifier is not valid. Correct the ownership value and submit a new job.
attribution_conflict The upload contains conflicting ownership claims. Use one ownership method; options.sessionId takes precedence when supplied.
file_too_large The final GLB remains above the 20 MB WEARFITS upload ceiling after compression. Use a smaller or simpler model, or omit uploadToWearFits.

Response — 202 Accepted

{
  "jobId": "job_01HX3Z7K2MFVBN9Q4RPTDY6WCE"
}

POST /api/v1/files/upload

Upload a large binary file (such as a source .glb model) before triggering the shoe-3d pipeline. Use this endpoint when your GLB exceeds the inline payload size limit for the shoe-3d request body.

The endpoint returns a temporary hosted URL. Pass the returned url unchanged as glbInput in a subsequent POST /api/v1/shoe-3d call.

Request

Content-Type: multipart/form-data

Field Type Required Description
file file Yes The binary file to upload (e.g., .glb).

Example request

curl -X POST https://api.wearfits.com/api/v1/files/upload \
  -H "X-API-Key: your_api_key" \
  -F "file=@base-model.glb"

Response — 200 OK

{
  "url": "https://storage.example.com/uploads/base-model.glb"
}

Use the returned url as the glbInput value when calling POST /api/v1/shoe-3d.


GET /api/v1/jobs/{jobId}

Poll the status and retrieve the final results of an asynchronous job submitted via POST /api/v1/shoe-3d or any other job-producing endpoint. Responses include Cache-Control: no-store; the hosted application proxy preserves this header, so do not cache job-status responses. Proxied responses also include X-Content-Type-Options: nosniff. These headers prevent browser/intermediary cache reuse and MIME sniffing, although a newly written status may still take a short time to become visible.

Some non-shoe job types expose lifecycle stages without measured provider progress. For virtual-fitting and digital-twin, progress.percentage is absent until completion, when it is 100; render an indeterminate spinner while it is absent.

Path parameters

Parameter Type Description
jobId string Job identifier returned by the submission endpoint.

Example request

curl https://api.wearfits.com/api/v1/jobs/job_01HX3Z7K2MFVBN9Q4RPTDY6WCE \
  -H "X-API-Key: your_api_key"

Response — 200 OK (in progress)

{
  "jobId": "job_01HX3Z7K2MFVBN9Q4RPTDY6WCE",
  "status": "processing",
  "progress": 42
}

Response — 200 OK (completed)

{
  "jobId": "job_01HX3Z7K2MFVBN9Q4RPTDY6WCE",
  "status": "completed",
  "progress": 100,
  "result": {
    "glbUrl": "https://assets.wearfits.com/models/shoe.glb",
    "viewerUrl": "https://app.wearfits.com/viewer?object=abc123",
    "previewGrid": "https://assets.wearfits.com/previews/shoe-grid.png"
  }
}

Generated GLB and preview assets use assets.wearfits.com. Treat returned asset URLs as opaque and use the returned glbUrl unchanged when passing the model to downstream integrations, including Fittingbox; assets.wearfits.com is an accepted WEARFITS asset host.

Response — 200 OK (failed)

{
  "jobId": "job_01HX3Z7K2MFVBN9Q4RPTDY6WCE",
  "status": "failed",
  "error": "Generation failed: no valid shoe silhouette detected in input images."
}

For a shoe-3d job that ends with error.code: "CACHE_STATE_INVALIDATED", submit a new request. The job is terminal (error.retryable: false) and has no usable result.

Job status values

Status Description
queued Job has been accepted and is waiting for a processing slot.
validating Input images and parameters are being validated.
processing 3D generation and optional AI passes are running.
uploading Generated model is being uploaded to storage and, if requested, to the AR platform.
completed Processing is complete. The result object contains output URLs.
failed An unrecoverable error occurred. See the error field for details.

Result fields (on completion)

Field Description
glbUrl Direct URL to the generated GLB 3D model file. Generated GLB assets use assets.wearfits.com; pass the returned URL unchanged to downstream integrations.
viewerUrl URL to preview the model in the WEARFITS 3D viewer.
previewGrid URL to a composite preview image showing the model from multiple angles. Generated preview assets use assets.wearfits.com.

Poll every 5–10 seconds. Typical processing times are 2–5 minutes depending on enabled options and queue depth. Do not poll more frequently than every 2 seconds to stay within rate limits.


DELETE /api/v1/jobs/{jobId}

Cancel a running job. The job is terminated immediately. Jobs that have already reached completed or failed status cannot be cancelled.

Path parameters

Parameter Type Description
jobId string Job identifier to cancel.

Example request

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

Response — 200 OK

{
  "cancelled": true
}

POST /api/v1/digitization/batches

Submit multiple shoes for bulk 3D digitization processing. This is a secondary endpoint intended for high-volume batch workflows. Jobs submitted via this endpoint are placed in a lower-priority queue (DIGITIZATION_QUEUE) separate from the real-time JOB_QUEUE used by POST /api/v1/shoe-3d.

Example request

curl -X POST https://api.wearfits.com/api/v1/digitization/batches \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "images": ["https://example.com/shoe-a-side.jpg"],
        "options": { "productType": "shoe", "uploadToWearFits": true }
      },
      {
        "images": ["https://example.com/shoe-b-side.jpg"],
        "options": { "productType": "shoe", "uploadToWearFits": true }
      }
    ]
  }'

Use GET /api/v1/jobs/{jobId} to track each item's progress individually.

Each digitization item may include longestSideCm for a bag or other non-shoe product. It is the real length of the longest horizontal side (width or depth, never the height), from 5 to 150 cm with 0.1 cm precision. An out-of-range value returns HTTP 400 for the whole batch, and the field is ignored for shoes. If omitted, the try-on keeps the model's own scale when it is plausible for a bag (between 10 and 100 cm across) and shows it at 40 cm otherwise.


POST /api/v1/wearfits/models/{modelId}/size

Set or remove the real size of a published bag or other non-shoe model. Use the modelId from wearfitsTryOn.modelId on a completed shoe-3d job or digitization batch item. The call is idempotent and changes only a model owned by the account associated with the API key.

Request body

Field Type Required Description
longestSideCm number \| null Yes Set the real length of the model's longest horizontal side (width or depth, never the height), from 5 to 150 cm with 0.1 cm precision. Send null to remove the stored size; the try-on then keeps the model's own scale when it is plausible for a bag (between 10 and 100 cm across) and shows it at 40 cm otherwise.

Example request

curl -X POST https://api.wearfits.com/api/v1/wearfits/models/abc123/size \\
  -H "X-API-Key: your_api_key" \\
  -H "Content-Type: application/json" \\
  -d '{"longestSideCm":32.5}'

Response — 200 OK

{
  "success": true,
  "data": {
    "modelId": "abc123",
    "longestSideCm": 32.5
  }
}

Webhook Signatures

If your integration is configured to receive webhook notifications, each webhook payload is signed with HMAC-SHA256 using a secret derived from your API key.

Signing secret: SHA-256(your_api_key) (hex-encoded string).

Verification steps

  1. Compute the signing secret:

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

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

    import json
    canonical = json.dumps(payload, sort_keys=True, separators=(',', ':'))
    

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

    import hmac
    signature = hmac.new(
        signing_secret.encode(),
        canonical.encode(),
        hashlib.sha256
    ).hexdigest()
    

  4. Compare the computed signature with the value in the X-Webhook-Signature request header. Use a constant-time comparison to avoid timing attacks:

    import hmac
    is_valid = hmac.compare_digest(signature, request.headers['X-Webhook-Signature'])
    


Rate Limits

Limits apply per credential per minute for endpoint limits.

Endpoint category Limit
Job submission (POST) 50 requests/min
Job status (GET) and published-model size updates (POST) 500 requests/min
File downloads 300 requests/min
Health check 5000 requests/min

Requests that exceed an endpoint limit return HTTP 429 Too Many Requests.

These endpoint limits are approximate and apply per credential (or per IP when unauthenticated) and endpoint; they are not a global account quota. An additional shared per-IP limit applies before authentication to API requests: 6,000 reads, 1,200 writes, 600 uploads, and 3,000 result-file requests per 60 seconds. Multiple users behind the same proxy or NAT can share this allowance. The IP counters are approximate by location, so they are not a strict worldwide quota. The plain health check, static documentation, and CORS preflight are outside this additional limit.

An early IP-limit rejection returns HTTP 429 with Retry-After: 60 and X-Request-ID; a temporary limit-service failure can return HTTP 503 with X-Request-ID. X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset describe endpoint limits when available, but are not guaranteed on early 429 or 503 responses. Respect Retry-After when present.

Note: Rate limits are 10x higher when using localhost or the development API key (DEV_API_KEY). This elevated limit applies to local development environments only and is not active in production.


Full Integration Example

The following example demonstrates a complete shoe-3d workflow: uploading a large GLB base file, submitting the generation job, polling until completion, and handling the result.

# Step 1: Upload a large base GLB (if applicable)
UPLOAD_RESPONSE=$(curl -s -X POST https://api.wearfits.com/api/v1/files/upload \
  -H "X-API-Key: your_api_key" \
  -F "file=@base-model.glb")

GLB_URL=$(echo $UPLOAD_RESPONSE | jq -r '.url')

# Step 2: Submit the shoe-3d generation job
JOB_RESPONSE=$(curl -s -X POST https://api.wearfits.com/api/v1/shoe-3d \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d "{
    \"images\": [\"https://example.com/shoe-side.jpg\"],
    \"glbInput\": \"$GLB_URL\",
    \"options\": {
      \"uploadToWearFits\": true,
      \"enhanceTexture\": true,
      \"genaiQuality\": \"high\"
    }
  }")

JOB_ID=$(echo $JOB_RESPONSE | jq -r '.jobId')
echo "Job submitted: $JOB_ID"

# Step 3: Poll for job completion
while true; do
  STATUS_RESPONSE=$(curl -s "https://api.wearfits.com/api/v1/jobs/$JOB_ID" \
    -H "X-API-Key: your_api_key")

  STATUS=$(echo $STATUS_RESPONSE | jq -r '.status')
  PROGRESS=$(echo $STATUS_RESPONSE | jq -r '.progress')
  echo "Status: $STATUS ($PROGRESS%)"

  if [ "$STATUS" = "completed" ]; then
    GLB_URL=$(echo $STATUS_RESPONSE | jq -r '.result.glbUrl')
    VIEWER_URL=$(echo $STATUS_RESPONSE | jq -r '.result.viewerUrl')
    echo "Done! GLB: $GLB_URL"
    echo "Viewer: $VIEWER_URL"
    break
  elif [ "$STATUS" = "failed" ]; then
    ERROR=$(echo $STATUS_RESPONSE | jq -r '.error')
    echo "Job failed: $ERROR"
    break
  fi

  sleep 10
done