# Virtual Try-On API -- LLM Agent Integration Instructions **Base URL:** `https://vton.kinvyt.com` **Full API Reference:** `https://vton.kinvyt.com/public-api-reference.md` **OpenAPI spec:** `https://vton.kinvyt.com/openapi/v1.json` **Interactive docs:** `https://vton.kinvyt.com/scalar/v1` This document provides integration patterns, decision rules, and worked examples for autonomous agent use. Read this file first; consult the full API reference for complete field specs and error code tables. --- ## System Overview This is a multi-tenant virtual try-on API. You submit a photo of a person and a photo of a garment; the system returns an AI-generated image of the person wearing the garment. Key facts: - Jobs are **asynchronous** -- submission returns a `jobId`; you must poll for the result. - Jobs are billed in **credits**. Credits are deducted only when a job **completes successfully**. Failed jobs are free. - Quality levels: `Standard` (15 credits, ~5s), `Enhanced` (25 credits, ~30s), `Premium` (45 credits, ~90s). Tiers can be disabled by the operator — a request for a disabled tier is rejected with `400 TIER_UNAVAILABLE`. Omit `Quality` to use the first available tier. - Output image URLs are **SAS URLs** valid for **24 hours** from job completion. --- ## Getting Your API Keys Keys are self-service from the dashboard — there is no paid plan or manual provisioning gate, and your first project includes **50 trial credits**. Keys are **not** issued automatically; you generate each one yourself. 1. **Sign in.** Go to `https://vton.kinvyt.com/dashboard/login` and click **Sign in with Google**. 2. **Create a project.** On first sign-in you are prompted to create your first project (this grants the 50 trial credits). To add more later, use **New Project** in the left sidebar. A "project" is your tenant — keys, credits, and usage are all scoped to it. 3. **Select the project** from the selector in the top bar (only needed if you have more than one). 4. **Open API Keys.** Click **API Keys** in the left sidebar (`/dashboard/keys`). 5. **Generate the keys you need.** Each card has a **Generate** button (and **Regenerate** once one exists): - **Generate Secret Key** → `sk_live_…` — for your backend. This is the one most integrations need. - **Generate Master Key** → `mk_live_…` — only if you will rotate keys or manage settings via the API. - **Generate Publishable Key** → `pk_live_…` — only for browser/mobile session flows. 6. **Copy immediately.** Each key is shown **exactly once**. After that only its prefix is displayed. **Regenerate** creates a new key and **immediately invalidates the old one**, so rotate deliberately. > Can't see the **API Keys** page? Make sure you have selected a project in the top bar — the page shows "No project selected" until you do. If you have no projects yet, create one first (step 2). For most integrations you only need the **Secret Key** — use it as `sk_live_xxxx` in the examples below. --- ## Recommended Integration Pattern For most use cases, **server-side integration is the recommended approach**. Your `sk_` key stays on your server, credits are managed centrally, and you have full control over job submission and polling. Use the browser/session-token flow only when you need to accept image uploads directly from end-user browsers without routing them through your backend. --- ## Authentication Four credential types exist. Choose based on where your code runs: | Credential | Prefix | Where to use | |------------|--------|-------------| | Secret Key | `sk_live_` | Server-side code only. Submit jobs, poll status, read credits and usage | | Master Key | `mk_live_` | Server-side code only. Rotate keys, update tenant settings, revoke sessions | | Publishable Key | `pk_live_` | Browser / mobile only. Create session tokens | | Session Token | `sess_` | Browser / mobile. Submit jobs and poll status within a session | **Never expose `sk_` or `mk_` in client-side code.** These keys authenticate all operations for your tenant. ### Sending credentials Secret key or master key -- `X-Api-Key` header: ``` X-Api-Key: sk_live_xxxx ``` Session token -- `Authorization` header: ``` Authorization: Bearer sess_xxxx ``` Publishable key (session creation only) -- `X-Api-Key` header: ``` X-Api-Key: pk_live_xxxx ``` --- ## Core Workflow: Server-Side Try-On Use this pattern when your code runs on a backend server with access to the `sk_` key. ### Step 1 -- Submit the job ``` POST /tryon Content-Type: multipart/form-data X-Api-Key: sk_live_xxxx PersonImage: GarmentImage: Quality: Standard IdempotencyKey: ``` Response `202 Accepted`: ```json { "jobId": "3f1a2b4c-...", "status": "Pending" } ``` Store `jobId`. If `IdempotencyKey` was provided and a matching job already exists, the same `jobId` is returned -- no duplicate job is created. ### Step 2 -- Poll for the result ``` GET /jobs/{jobId} X-Api-Key: sk_live_xxxx ``` Poll every **2-3 seconds**. Stop when `status` is `Completed` or `Failed`. Response `200 OK`: ```json { "jobId": "3f1a2b4c-...", "status": "Completed", "quality": "Standard", "outputImageUrl": "https://...", "outputImageUrls": ["https://..."], "errorMessage": null, "createdAt": "...", "updatedAt": "...", "processingDurationMs": 7500, "creditsUsed": 15 } ``` - `Completed` -> use `outputImageUrl` (or `outputImageUrls[0]`). Valid for 24 hours. - `Failed` -> `errorMessage` describes the failure. No credits were charged. --- ## Core Workflow: Frontend / Browser Try-On Use this pattern when try-on is triggered from a browser or mobile app. The goal is to never expose the `sk_` key to clients. ### Step 1 -- Create a session token (server or browser) The session token is credit-capped and short-lived (5 minutes by default). You can create it server-side and pass it to the frontend, or create it directly in the browser using the `pk_` key. ``` POST /sessions X-Api-Key: pk_live_xxxx Origin: https://your-app.com X-Captcha-Token: ``` Response `201 Created`: ```json { "sessionToken": "sess_xxxx", "expiresAt": "...", "allowedCredits": 3, "remainingCredits": 3 } ``` ### Step 2 -- Submit the try-on job using the session token ``` POST /tryon Content-Type: multipart/form-data Authorization: Bearer sess_xxxx PersonImage: GarmentImage: ``` ### Step 3 -- Poll for the result Same as the server-side flow: ``` GET /jobs/{jobId} Authorization: Bearer sess_xxxx ``` ### Session limits A session expires when any of the following occurs: - TTL elapses (default 5 minutes, configurable 2-10 minutes) - Credit cap is exhausted (default 3 credits per session) - All sessions are revoked by the tenant via `POST /tenant/sessions/revoke-all` --- ## Error Handling ### Decision rules | Condition | Action | |-----------|--------| | HTTP 401 `UNAUTHORIZED` | Stop. Credential is missing, wrong type, or has been rotated | | HTTP 402 `CREDITS_EXHAUSTED` | Stop. Purchase more credits before retrying | | HTTP 402 `SPENDING_LIMIT_EXCEEDED` | Stop. Monthly budget is exhausted | | HTTP 402 `NO_PAYMENT_METHOD` | Stop. A payment method must be saved via the dashboard first | | HTTP 403 `ACCESS_DENIED` | Stop. The key type does not have permission for this endpoint | | HTTP 403 `ORIGIN_NOT_ALLOWED` | Stop. The `Origin` header is not in the tenant's allowed origins list | | HTTP 403 `CAPTCHA_REQUIRED` | Obtain a Cloudflare Turnstile token and retry | | HTTP 403 `CAPTCHA_FAILED` | Obtain a fresh Cloudflare Turnstile token and retry once | | HTTP 404 `JOB_NOT_FOUND` | Stop. The job does not exist or belongs to a different tenant | | HTTP 429 `RATE_LIMITED` | Wait 60 seconds, then retry | | HTTP 429 `DAILY_PUBLIC_LIMIT_REACHED` | Stop for the day. The tenant's daily session credit cap is exhausted | | HTTP 503 `SERVICE_UNAVAILABLE` | Wait for the number of seconds in the `Retry-After` response header, then retry | | Job status `Failed` | Read `errorMessage`. No credits were charged. Retry if transient, stop if permanent | ### Error response shape All errors return: ```json { "errorCode": "SOME_CODE", "message": "Human-readable description." } ``` Credit and spending errors include extra fields: ```json { "errorCode": "CREDITS_EXHAUSTED", "message": "...", "currentBalance": 0 } { "errorCode": "SPENDING_LIMIT_EXCEEDED", "message": "...", "currentSpending": 1500.0, "limit": 1000.0 } ``` --- ## Credit Management ### Check balance ``` GET /credits/status X-Api-Key: sk_live_xxxx ``` Returns `creditBalance`, `totalCreditsUsed`, and `remainingEstimates` (how many jobs remain per quality level). ### Purchase credits programmatically Requires a saved payment method (set up via the dashboard). Check first: ``` GET /credits/payment-method X-Api-Key: sk_live_xxxx ``` If `hasSavedPaymentMethod` is `true`, list packages and purchase: ``` GET /credits/packages X-Api-Key: sk_live_xxxx ``` ``` POST /credits/purchase Content-Type: application/json X-Api-Key: sk_live_xxxx { "packageId": "pack_500" } ``` Credits are added **asynchronously** after Razorpay confirms payment. The purchase response only confirms the charge was initiated. --- ## Spending Limits ``` GET /spending/status X-Api-Key: sk_live_xxxx ``` Key field: `effectiveRemaining` = `min(creditBalance, remainingBudget)`. This is the actual number of credits you can spend right now. If `spendingLimit` is `null`, there is no monthly cap. --- ## Usage & Billing ``` GET /usage?from=2024-01-01&to=2024-01-31&granularity=daily X-Api-Key: sk_live_xxxx GET /usage/billing?from=2024-01-01&to=2024-01-31&period=monthly X-Api-Key: sk_live_xxxx ``` Both endpoints default to the last 30 days when `from`/`to` are omitted. --- ## Tenant Self-Management These operations require the master key (`mk_`). ### Rotate a key ``` POST /tenant/rotate-key?type=sk X-Api-Key: mk_live_xxxx ``` The old key is **immediately invalidated**. The new key is returned once in plain text -- store it immediately. `type` values: `sk` (secret key), `pk` (publishable key). Master key rotation requires the system admin. ### View and update settings ``` GET /tenant/settings X-Api-Key: mk_live_xxxx PUT /tenant/settings Content-Type: application/json X-Api-Key: mk_live_xxxx { "allowedOrigins": ["https://your-app.com"], "sessionCreditCap": 3, "sessionTtlSeconds": 300, "dailyPublicCreditCap": 500, "maxSessionsPerMinute": 100, "requireCaptcha": true } ``` All `PUT` fields are optional -- only provided fields are updated. ### Revoke all sessions ``` POST /tenant/sessions/revoke-all X-Api-Key: mk_live_xxxx ``` Immediately invalidates every active session token. In-flight requests using those tokens will receive HTTP 401. --- ## Idempotency `POST /tryon` accepts an `IdempotencyKey` field (multipart string). If a job with the same key already exists for the tenant, the existing job is returned -- no new job is created and no credits are reserved. Use this to safely retry failed network requests without double-charging. --- ## Rate Limits | Scope | Limit | |-------|-------| | Session creation per IP | 10 / minute | | Session creation per tenant | 100 / minute (configurable) | | Daily public credit cap | 500 credits/day (configurable) | On HTTP 429 with no `Retry-After` header, wait at least 60 seconds before retrying. --- ## Key Operational Notes - Images must be **JPEG or PNG**, maximum **25 MB** total per request. - Output SAS URLs expire **24 hours** after job completion. Download and store the image if you need it beyond that window. - `Standard` quality completes in **5-8 seconds**. `Enhanced`/`Premium` may take up to **2 minutes**. Adjust your polling timeout accordingly. - Do not hardcode a single quality tier. Tiers may be disabled by the operator; a disabled tier returns `400 TIER_UNAVAILABLE` whose message lists the currently available tiers. If you need to know availability up front, omit `Quality` (uses the first available tier) or handle `TIER_UNAVAILABLE` by retrying with a listed tier. - Failed jobs appear with `status: Failed` and a non-null `errorMessage`. They are terminal -- the same job will not recover. Submit a new job to retry. - The API returns `camelCase` JSON for all responses. - All timestamps are **ISO 8601 UTC**. --- ## Reference Full endpoint documentation, all request/response fields, and complete error code tables: `https://vton.kinvyt.com/public-api-reference.md`