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.
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:
- URLs (HTTP/HTTPS) — standard web URLs pointing to publicly accessible hosted images.
- Base64 Data URLs — inline base64-encoded images embedded directly in the request body.
- Format:
data:image/<format>;base64,<data> - 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:
- 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.
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
}
}'
WEARFITS upload limit and related errors
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
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
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)
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. |
Recommended polling interval
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
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
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
-
Compute the signing secret:
-
Sort the webhook JSON body keys alphabetically and serialize to a canonical string:
-
Compute the HMAC-SHA256 of the canonical string using the signing secret:
-
Compare the computed signature with the value in the
X-Webhook-Signaturerequest header. Use a constant-time comparison to avoid timing attacks:
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
localhostor 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