Skip to main content

API Reference

Base URL: https://app.zupertry.com All /v1/* endpoints are authenticated with an API key:
Authorization: Bearer zt_live_sk_YOUR_KEY
Use a test key (zt_test_sk_...) for development — jobs are processed with a mock output and no credits are consumed.

POST /v1/tryon

Submit a virtual try-on job. Returns immediately with a job ID. The job is processed asynchronously. Request body
FieldTypeRequiredDescription
model_image_urlstringyesPublicly accessible URL of the model photo (JPEG/PNG/WebP, max 10 MB)
garment_image_urlstringyesPublicly accessible URL of the garment photo (JPEG/PNG/WebP, max 10 MB)
workspace_idstringnoWorkspace to associate the job with. Defaults to your first active workspace.
webhook_urlstringnoURL to receive the job result. Overrides the org-level default webhook URL.
Response 202
{
  "job_id": "job_abc123",
  "status": "pending",
  "created_at": "2026-03-19T10:00:00.000Z",
  "mode": "live"
}
FieldTypeDescription
job_idstringUse this to poll /v1/jobs/{jobId}
statusstringAlways pending on submit
created_atstringISO 8601 UTC timestamp
modestringlive or test
Errors
Statuserror.codeWhen
401invalid_api_keyKey not found
401api_key_revokedKey has been revoked
402insufficient_creditsCredit balance is 0 (live mode only)
403account_suspendedOrganisation is suspended
404workspace_not_foundworkspace_id not found or not in this org
422validation_errorMissing fields, invalid URL, or unsupported image format
429rate_limit_exceededToo many requests
500internal_errorServer error
Example
curl -X POST https://app.zupertry.com/v1/tryon \
  -H "Authorization: Bearer zt_live_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model_image_url": "https://cdn.example.com/model.jpg",
    "garment_image_url": "https://cdn.example.com/shirt.jpg",
    "webhook_url": "https://your-app.com/webhooks/zupertry"
  }'

GET /v1/jobs/

Get a single job by ID. Path parameters
ParamTypeDescription
jobIdstringThe job ID returned from POST /v1/tryon
Response 200
{
  "job_id": "job_abc123",
  "status": "completed",
  "workspace_id": "ws_xyz",
  "mode": "live",
  "output_url": "https://storage.googleapis.com/zupertry-outputs/...",
  "credits_consumed": 1,
  "webhook_url": "https://your-app.com/webhooks/zupertry",
  "webhook_status": "delivered",
  "created_at": "2026-03-19T10:00:00.000Z",
  "completed_at": "2026-03-19T10:00:05.231Z",
  "duration_ms": 5231
}
FieldTypeDescription
job_idstring
statusstringpending | processing | completed | failed | cancelled | refunded
workspace_idstring
modestringlive or test
output_urlstring | nullSigned URL valid for 1 hour. null if not completed.
credits_consumednumber | nullnull until job completes
error_codestring | nullSet on failure
webhook_urlstring | nullWebhook URL for this job
webhook_statusstring | nullpending | delivered | failed | retrying | null
created_atstringISO 8601 UTC
completed_atstring | null
duration_msnumber | nullProcessing time in milliseconds
Errors
Statuserror.codeWhen
401invalid_api_keyKey not found
401api_key_revokedKey revoked
403account_suspendedOrg suspended
403forbiddenJob belongs to a different org
404job_not_foundJob ID does not exist
Example
curl https://app.zupertry.com/v1/jobs/job_abc123 \
  -H "Authorization: Bearer zt_live_sk_YOUR_KEY"

GET /v1/jobs

List jobs for your account, newest first. Query parameters
ParamTypeDefaultDescription
workspace_idstringFilter to one workspace
statusstringFilter by status: pending, processing, completed, failed, cancelled, refunded
limitinteger20Page size, max 100
cursorstringPagination cursor from previous response
Response 200
{
  "jobs": [...],
  "next_cursor": "eyJpZCI6ImpvYl94eXoiLCJ0IjoiMjAyNi0wMy0xOVQxMDowMDowMC4wMDBaIn0=",
  "has_more": true
}
FieldTypeDescription
jobsarrayArray of job objects (same shape as single job, without webhook fields)
next_cursorstring | nullPass as cursor param for the next page. null if no more results.
has_morebooleanWhether more results exist
Example
# First page
curl "https://app.zupertry.com/v1/jobs?limit=50&status=completed" \
  -H "Authorization: Bearer zt_live_sk_YOUR_KEY"

# Next page
curl "https://app.zupertry.com/v1/jobs?limit=50&cursor=eyJpZCI6ImpvYl94eXoifQ==" \
  -H "Authorization: Bearer zt_live_sk_YOUR_KEY"

DELETE /v1/jobs/

Cancel a pending job and refund the reserved credit. Only jobs with status: pending can be cancelled. Once a job moves to processing, it cannot be stopped. Path parameters
ParamTypeDescription
jobIdstringJob ID
Response 200
{
  "job_id": "job_abc123",
  "status": "refunded",
  "refunded_credits": 1
}
Errors
Statuserror.codeWhen
401invalid_api_keyKey not found
401api_key_revokedKey revoked
403forbiddenJob belongs to a different org
404job_not_foundJob ID does not exist
409job_not_cancellableJob status is not pending
Example
curl -X DELETE https://app.zupertry.com/v1/jobs/job_abc123 \
  -H "Authorization: Bearer zt_live_sk_YOUR_KEY"

GET /v1/workspaces

List all active workspaces for your account. Response 200
{
  "workspaces": [
    {
      "id": "ws_xyz",
      "name": "Default",
      "status": "active",
      "created_at": "2026-03-19T10:00:00.000Z"
    }
  ]
}
FieldTypeDescription
idstringUse as workspace_id when submitting jobs
namestringDisplay name
statusstringactive or archived
created_atstringISO 8601 UTC
Example
curl https://app.zupertry.com/v1/workspaces \
  -H "Authorization: Bearer zt_live_sk_YOUR_KEY"

GET /v1/account

Return account info: org name, plan, credit balance, and region. Response 200
{
  "org_id": "org_def456",
  "name": "Acme Corp",
  "plan": "growth",
  "region_code": "IN",
  "currency_code": "INR",
  "credits": 347,
  "auto_reload_enabled": true
}
FieldTypeDescription
org_idstring
namestringOrganisation name
planstringstarter | growth | scale | enterprise
region_codestringIN, US, EU, or GLOBAL
currency_codestringINR, USD, or EUR
creditsnumberCurrent credit balance
auto_reload_enabledboolean
Example
curl https://app.zupertry.com/v1/account \
  -H "Authorization: Bearer zt_live_sk_YOUR_KEY"

Authentication

All /v1/* endpoints use API key authentication:
Authorization: Bearer <api_key>
Key prefixModeCreditsVertex AI
zt_live_sk_LiveConsumedReal
zt_test_sk_TestFreeMock output
Manage your API keys in the console under Settings → API Keys.

Rate limits

Default rate limit: 60 requests per minute per organisation. If exceeded, the API returns:
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Check the Retry-After header."
  }
}
Check the Retry-After response header for the number of seconds to wait. Contact sales for higher limits.

Pagination

List endpoints use cursor-based pagination. The cursor is opaque — do not decode or construct it manually.
async function getAllJobs(apiKey) {
  const jobs = [];
  let cursor = null;

  do {
    const url = new URL('https://app.zupertry.com/v1/jobs');
    url.searchParams.set('limit', '100');
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    const data = await res.json();

    jobs.push(...data.jobs);
    cursor = data.next_cursor;
  } while (cursor);

  return jobs;
}