Documentation Index
Fetch the complete documentation index at: https://docs.zupertry.com/llms.txt
Use this file to discover all available pages before exploring further.
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
| Field | Type | Required | Description |
|---|
model_image_url | string | yes | Publicly accessible URL of the model photo (JPEG/PNG/WebP, max 10 MB) |
garment_image_url | string | yes | Publicly accessible URL of the garment photo (JPEG/PNG/WebP, max 10 MB) |
workspace_id | string | no | Workspace to associate the job with. Defaults to your first active workspace. |
webhook_url | string | no | URL 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"
}
| Field | Type | Description |
|---|
job_id | string | Use this to poll /v1/jobs/{jobId} |
status | string | Always pending on submit |
created_at | string | ISO 8601 UTC timestamp |
mode | string | live or test |
Errors
| Status | error.code | When |
|---|
| 401 | invalid_api_key | Key not found |
| 401 | api_key_revoked | Key has been revoked |
| 402 | insufficient_credits | Credit balance is 0 (live mode only) |
| 403 | account_suspended | Organisation is suspended |
| 404 | workspace_not_found | workspace_id not found or not in this org |
| 422 | validation_error | Missing fields, invalid URL, or unsupported image format |
| 429 | rate_limit_exceeded | Too many requests |
| 500 | internal_error | Server 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
| Param | Type | Description |
|---|
jobId | string | The 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
}
| Field | Type | Description |
|---|
job_id | string | |
status | string | pending | processing | completed | failed | cancelled | refunded |
workspace_id | string | |
mode | string | live or test |
output_url | string | null | Signed URL valid for 1 hour. null if not completed. |
credits_consumed | number | null | null until job completes |
error_code | string | null | Set on failure |
webhook_url | string | null | Webhook URL for this job |
webhook_status | string | null | pending | delivered | failed | retrying | null |
created_at | string | ISO 8601 UTC |
completed_at | string | null | |
duration_ms | number | null | Processing time in milliseconds |
Errors
| Status | error.code | When |
|---|
| 401 | invalid_api_key | Key not found |
| 401 | api_key_revoked | Key revoked |
| 403 | account_suspended | Org suspended |
| 403 | forbidden | Job belongs to a different org |
| 404 | job_not_found | Job 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
| Param | Type | Default | Description |
|---|
workspace_id | string | — | Filter to one workspace |
status | string | — | Filter by status: pending, processing, completed, failed, cancelled, refunded |
limit | integer | 20 | Page size, max 100 |
cursor | string | — | Pagination cursor from previous response |
Response 200
{
"jobs": [...],
"next_cursor": "eyJpZCI6ImpvYl94eXoiLCJ0IjoiMjAyNi0wMy0xOVQxMDowMDowMC4wMDBaIn0=",
"has_more": true
}
| Field | Type | Description |
|---|
jobs | array | Array of job objects (same shape as single job, without webhook fields) |
next_cursor | string | null | Pass as cursor param for the next page. null if no more results. |
has_more | boolean | Whether 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
| Param | Type | Description |
|---|
jobId | string | Job ID |
Response 200
{
"job_id": "job_abc123",
"status": "refunded",
"refunded_credits": 1
}
Errors
| Status | error.code | When |
|---|
| 401 | invalid_api_key | Key not found |
| 401 | api_key_revoked | Key revoked |
| 403 | forbidden | Job belongs to a different org |
| 404 | job_not_found | Job ID does not exist |
| 409 | job_not_cancellable | Job 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"
}
]
}
| Field | Type | Description |
|---|
id | string | Use as workspace_id when submitting jobs |
name | string | Display name |
status | string | active or archived |
created_at | string | ISO 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
}
| Field | Type | Description |
|---|
org_id | string | |
name | string | Organisation name |
plan | string | starter | growth | scale | enterprise |
region_code | string | IN, US, EU, or GLOBAL |
currency_code | string | INR, USD, or EUR |
credits | number | Current credit balance |
auto_reload_enabled | boolean | |
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 prefix | Mode | Credits | Vertex AI |
|---|
zt_live_sk_ | Live | Consumed | Real |
zt_test_sk_ | Test | Free | Mock 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.
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;
}