{"openapi":"3.1.0","info":{"title":"FitInView Try-On API","version":"1.0.0","description":"Generate photorealistic virtual try-ons via REST. Submit a person photo + 1-3 garment images and get back a composed result. Bearer-auth, pay-per-call. See the developer docs at https://api.fitinview.com/api/docs.","contact":{"email":"api@fitinview.com"},"termsOfService":"https://fitinview.com/terms"},"servers":[{"url":"https://api.fitinview.com","description":"Production"}],"tags":[{"name":"Try-On","description":"Submit and poll virtual try-on jobs"},{"name":"Account","description":"Inspect API key state and balance"}],"paths":{"/api/v1/tryon":{"post":{"tags":["Try-On"],"summary":"Submit a virtual try-on","description":"Submit a person photo + 1-3 garment images. Returns the result inline when `wait=true` (default, up to 90s) or a job id for polling when `wait=false`. Set `webhook_url` to receive a signed POST when the job terminates. Set the `Idempotency-Key` header to safely retry on network errors.","operationId":"createTryOn","security":[{"BearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Idempotency-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TryOnRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TryOnSubmitResponse"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Account ineligible (e.g., key revoked, fraud-flagged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded — see X-RateLimit-* / Retry-After headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error (credits not charged when this is returned before model call)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Upstream temporarily unavailable; retry with exponential backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Bad request (validation, malformed input, blocked URL)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Insufficient credits for the requested output size","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Idempotency-Key reused with a different request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Try-On"],"summary":"List recent try-on jobs","description":"Cursor-paginated list of jobs for this key (newest first). Optional `?status=` filter.","operationId":"listTryOnJobs","security":[{"BearerAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"enum":["queued","running","succeeded","failed"],"type":"string"},{"type":"null"}],"title":"Status Filter"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TryOnListResponse"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Account ineligible (e.g., key revoked, fraud-flagged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded — see X-RateLimit-* / Retry-After headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error (credits not charged when this is returned before model call)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Upstream temporarily unavailable; retry with exponential backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tryon/{job_id}":{"get":{"tags":["Try-On"],"summary":"Get try-on job status","description":"Poll a try-on job. The `result_url` is regenerated on every call (24h TTL).","operationId":"getTryOnJob","security":[{"BearerAuth":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TryOnJobStatus"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Account ineligible (e.g., key revoked, fraud-flagged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded — see X-RateLimit-* / Retry-After headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error (credits not charged when this is returned before model call)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Upstream temporarily unavailable; retry with exponential backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/account":{"get":{"tags":["Account"],"summary":"Get account credits and limits","operationId":"getAccount","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountResponse"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Account ineligible (e.g., key revoked, fraud-flagged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded — see X-RateLimit-* / Retry-After headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error (credits not charged when this is returned before model call)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Upstream temporarily unavailable; retry with exponential backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"BearerAuth":[]}]}},"/api/v1/credits/purchase":{"post":{"tags":["Account"],"summary":"Purchase API credits using a Stripe Shared Payment Token (agentic commerce)","description":"Accepts payments from AI agents via Stripe Shared Payment Tokens (SPTs). The agent must have been issued an SPT scoped to FitInView by the user. On `succeeded`, credits are added to the authenticated API key immediately. On `processing` or `requires_action`, credits are granted via the `payment_intent.succeeded` webhook once the payment finalizes.","operationId":"purchaseCreditsWithSPT","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseWithSPTRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseWithSPTResponse"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Account ineligible (e.g., key revoked, fraud-flagged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded — see X-RateLimit-* / Retry-After headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error (credits not charged when this is returned before model call)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Upstream temporarily unavailable; retry with exponential backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid pack slug or SPT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Payment failed (declined, insufficient funds, etc.)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"PaymentIntent already processed for this token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"BearerAuth":[]}]}}},"components":{"schemas":{"PurchaseWithSPTRequest":{"properties":{"pack_slug":{"type":"string","title":"Pack Slug","description":"Dev pack slug: demo, starter, pro, scale, or enterprise"},"shared_payment_granted_token":{"type":"string","maxLength":200,"minLength":4,"title":"Shared Payment Granted Token","description":"Stripe Shared Payment Token (`spt_...`) granted to the agent by the user"}},"type":"object","required":["pack_slug","shared_payment_granted_token"],"title":"PurchaseWithSPTRequest"},"TryOnRequest":{"properties":{"person_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"format":"uri","title":"Person Url","description":"HTTPS URL of the person photo. Mutually exclusive with `person_image_b64`."},"person_image_b64":{"anyOf":[{"type":"string","maxLength":14000000},{"type":"null"}],"title":"Person Image B64","description":"Base64-encoded person image (no data: prefix). Max ~10 MB decoded. Mutually exclusive with `person_url`."},"garment_urls":{"items":{"type":"string","format":"uri"},"type":"array","maxItems":3,"title":"Garment Urls","description":"0-3 HTTPS garment image URLs. Combined with `garment_images_b64` must total 1-3."},"garment_images_b64":{"items":{"type":"string"},"type":"array","maxItems":3,"title":"Garment Images B64","description":"0-3 base64 garment images. Each max ~10 MB decoded."},"output_size":{"type":"string","enum":["1K","2K","4K"],"title":"Output Size","description":"Output resolution. Cost: 1K=8 credits, 2K=12, 4K=20.","default":"1K"},"style_hint":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Style Hint","description":"Short style direction (deprecated — use `prompt`). Ignored when `prompt` is set."},"prompt":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Prompt","description":"Custom instruction. APPENDED to the default 'try on these garments' text — system safety + identity-preservation rules still apply."},"wait":{"type":"boolean","title":"Wait","description":"Sync mode: block up to 90s for the result. `false` returns immediately with a `job_id` to poll.","default":true},"negative_prompt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Negative Prompt","description":"What NOT to include in the image (appended as 'avoid: ...' direction)."},"webhook_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"format":"uri","title":"Webhook Url","description":"HTTPS URL to receive a signed POST when the job reaches a terminal state. Signature header: X-FitInView-Signature: sha256=<hex>"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Arbitrary key/value pairs (max 8 keys, 256 chars each) returned unchanged on the response and on poll. Use this to correlate jobs to your own user/order IDs."},"seed":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":0.0},{"type":"null"}],"title":"Seed","description":"Optional seed for reproducible-ish output. Returned in the response."}},"additionalProperties":true,"type":"object","title":"TryOnRequest","description":"Submit a virtual try-on. Provide EITHER `person_url` OR `person_image_b64` (not both), AND 1-3 garments.","examples":[{"garment_urls":["https://example.com/shirt.jpg"],"metadata":{"order_id":"ord_42","user":"u_99"},"output_size":"1K","person_url":"https://example.com/me.jpg","wait":true},{"garment_urls":["https://example.com/shirt.jpg","https://example.com/jeans.jpg"],"output_size":"2K","person_url":"https://example.com/me.jpg","prompt":"Place the person on a sunny beach at golden hour. Keep face and pose unchanged.","seed":1234,"wait":false}]},"ErrorResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Human-readable error description"}},"type":"object","required":["detail"],"title":"ErrorResponse"},"TryOnJobStatus":{"properties":{"job_id":{"type":"string","pattern":"^job_[a-zA-Z0-9]+$","title":"Job Id"},"status":{"type":"string","enum":["queued","running","succeeded","failed"],"title":"Status"},"result_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Result Url","description":"Fresh signed URL (regenerated on each poll)"},"result_width":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Result Width","description":"Result image width in pixels"},"result_height":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Result Height","description":"Result image height in pixels"},"result_bytes":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Result Bytes","description":"Result image size in bytes"},"result_content_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Result Content Type","description":"Always `image/png` on success"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed","description":"Seed used for generation"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Echoes the request `metadata`"},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"cost_credits":{"type":"integer","minimum":0.0,"title":"Cost Credits"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date-time","title":"Created At","description":"ISO 8601 UTC"},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date-time","title":"Completed At","description":"ISO 8601 UTC, null until terminal state"}},"type":"object","required":["job_id","status","cost_credits"],"title":"TryOnJobStatus"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"TryOnSubmitResponse":{"properties":{"job_id":{"type":"string","pattern":"^job_[a-zA-Z0-9]+$","title":"Job Id","description":"Job identifier (matches `^job_[a-zA-Z0-9]+$`)"},"status":{"type":"string","enum":["queued","running","succeeded","failed"],"title":"Status"},"result_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Result Url","description":"Presigned PNG URL (24h TTL). Only on success in sync mode."},"cost_credits":{"type":"integer","minimum":0.0,"title":"Cost Credits","description":"Credits charged for this submission"},"credits_remaining":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Credits Remaining","description":"Balance remaining on this key after this call"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed","description":"Seed used for this generation (echoes the request seed if provided)"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Echoes the request `metadata`"},"test_mode":{"type":"boolean","title":"Test Mode","description":"True when the call was served by a `fiv_test_*` key (no charge, stock image)","default":false},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Set when sync mode hit the 90s timeout"}},"type":"object","required":["job_id","status","cost_credits"],"title":"TryOnSubmitResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"AccountResponse":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"credits_remaining":{"type":"integer","title":"Credits Remaining"},"rate_limit_per_min":{"type":"integer","title":"Rate Limit Per Min"},"total_calls":{"type":"integer","title":"Total Calls"},"tryon_cost_credits":{"additionalProperties":true,"type":"object","title":"Tryon Cost Credits","description":"Cost map by output size, e.g. {\"1K\":8,\"2K\":12,\"4K\":20}"}},"type":"object","required":["name","credits_remaining","rate_limit_per_min","total_calls","tryon_cost_credits"],"title":"AccountResponse"},"TryOnListResponse":{"properties":{"jobs":{"items":{"$ref":"#/components/schemas/TryOnJobStatus"},"type":"array","title":"Jobs"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Pass as `?cursor=` on the next call. Null when no more pages."}},"type":"object","required":["jobs"],"title":"TryOnListResponse"},"PurchaseWithSPTResponse":{"properties":{"payment_intent_id":{"type":"string","title":"Payment Intent Id"},"status":{"type":"string","title":"Status","description":"Stripe PaymentIntent status (succeeded, processing, requires_action, etc.)"},"pack_slug":{"type":"string","title":"Pack Slug"},"credits_added":{"type":"integer","minimum":0.0,"title":"Credits Added"},"credits_remaining":{"type":"integer","minimum":0.0,"title":"Credits Remaining"},"next_action":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Next Action","description":"Stripe next_action object if status=requires_action"}},"type":"object","required":["payment_intent_id","status","pack_slug","credits_added","credits_remaining"],"title":"PurchaseWithSPTResponse"}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"fiv_live_xxx","description":"Get a key from https://fitinview.com/developers"}}},"security":[{"BearerAuth":[]}]}