# Documentation (/docs)
# DrapeItOn docs [#drapeiton-docs]
DrapeItOn lets fashion brands and e-commerce platforms run **virtual try-on** in their own product: submit a shopper photo and a garment image, then receive a result image asynchronously.
This site covers **how** to integrate and **why** the API is shaped the way it is. Field-level request and response schemas live in the live API reference.
| Resource | What it is |
| ---------------------------------------------------------------------- | ---------------------------------------------------------- |
| **[Quickstart](/docs/getting-started/quickstart)** | End-to-end tutorial: first try-on request to result |
| **[Guides](/docs/guides/authentication)** | Task-oriented how-tos (auth, uploads, webhooks, errors, …) |
| **[Concepts](/docs/concepts/overview)** | Core ideas: accounts, generations, credits, categories |
| **[API Reference](https://api.drapeiton.com/docs)** | Interactive OpenAPI / Scalar |
| **[API Reference (Markdown)](https://api.drapeiton.com/reference.md)** | OpenAPI rendered as Markdown for LLMs / non-JS clients |
| **[llms.txt](/llms.txt)** | Machine-readable docs index for LLMs |
| **[llms-full.txt](/llms-full.txt)** | Full product docs text for LLMs |
## Where to start [#where-to-start]
1. Have an invite-only business account and an API key from the dashboard.
2. Follow the [Quickstart](/docs/getting-started/quickstart).
3. Use the [API Reference](https://api.drapeiton.com/docs) when you need exact request or response fields.
Access is **invite-only beta** (no self-serve signup). Request access from the contact address in [Introduction](/docs/getting-started/introduction).
# Categories (/docs/concepts/categories)
# Categories [#categories]
Every generation requires a **category** so the try-on model can treat the garment correctly.
## Allowed values [#allowed-values]
| Value | Use for |
| ------------ | ------------------------------------------------------ |
| `tops` | Shirts, tops, jackets, and similar upper-body garments |
| `bottoms` | Pants, skirts, shorts, and similar lower-body garments |
| `one-pieces` | Dresses, jumpsuits, and similar full-body garments |
These are the only accepted values on `POST /v1/generations`. Other strings fail validation.
## Why it matters [#why-it-matters]
Passing the wrong category can degrade visual quality. Choose the category that best matches how the garment is worn on the body, not the product department name alone.
## Related [#related]
* [Submitting generations](/docs/guides/submitting-generations)
* [API Reference](https://api.drapeiton.com/docs)
# Credits (/docs/concepts/credits)
# Credits [#credits]
**Credits** are the unit of try-on usage on DrapeItOn.
## New accounts [#new-accounts]
When a business account is created, it receives **10 credits** to start with.
## Usage [#usage]
When `POST /v1/generations` is **accepted**, **one credit** is reserved for that job.
* Check remaining balance with `GET /v1/credits` (`balance` and `currency: "credits"`).
* If balance is insufficient, the API rejects the request with an insufficient-credits error (see [Errors and retries](/docs/guides/errors-and-retries)).
## Buying more credits [#buying-more-credits]
You **cannot** purchase or top up credits from the dashboard yet. During invite-only beta, request additional credits by contacting us at .
## Refunds [#refunds]
If a generation **fails terminally**, the credit reserved for that job is **returned** to your balance. Successful jobs consume the reserved credit.
## Related [#related]
* [Quickstart](/docs/getting-started/quickstart)
* [API Reference — Credits](https://api.drapeiton.com/docs)
# Generations (/docs/concepts/generations)
# Generations [#generations]
A **generation** is one virtual try-on job: person image + garment image + category → result image (or failure).
## Submitting [#submitting]
`POST /v1/generations` accepts the job and returns **202** with an `id` and `status: "submitted"`. Work continues asynchronously.
Required fields (see [API Reference](https://api.drapeiton.com/docs) for full schema):
* `person_image_url` — public URL of the shopper / model photo
* `garment_image_url` — public URL of the garment
* `category` — `tops`, `bottoms`, or `one-pieces`
Optional: `end_user_id`, `seed`, and quality-related parameters documented in the reference.
## Customer-facing statuses [#customer-facing-statuses]
When you poll `GET /v1/generations/{id}` or list jobs, `status` is one of:
| Status | Meaning for clients |
| ------------ | ----------------------------------------------------------------------- |
| `submitted` | Accepted; not finished yet |
| `queued` | Waiting to run |
| `processing` | Actively running (includes internal retry phases collapsed for clients) |
| `succeeded` | Finished successfully; use `output_url` |
| `failed` | Finished unsuccessfully; see `error` |
Do not depend on internal pipeline names. Treat anything other than `succeeded` or `failed` as **in progress** and keep polling or wait for a webhook.
## Delivery of results [#delivery-of-results]
1. **Poll** — `GET /v1/generations/{id}` until terminal status.
2. **Webhook** — receive `generation.completed` or `generation.failed` if configured.
How-to: [Handling results](/docs/guides/handling-results), [Webhooks](/docs/guides/webhooks).
## Retries and failures [#retries-and-failures]
If a try-on fails transiently, the platform may **retry processing automatically**. The number of processing attempts is environment-specific and is **not** documented as a fixed public limit here.
When a job ends in terminal failure, clients see `status: "failed"` (poll) or `generation.failed` (webhook). Credits reserved for that job are returned to your balance. See [Credits](/docs/concepts/credits) and [Errors and retries](/docs/guides/errors-and-retries).
## Listing jobs [#listing-jobs]
`GET /v1/generations` returns a paginated history for your account (optional `status` filter). Field-level details: [API Reference](https://api.drapeiton.com/docs).
# Overview (/docs/concepts/overview)
# Concepts overview [#concepts-overview]
This page defines the main ideas you will see across the Integration API and dashboard. Implementation details of hosting and internal systems are omitted on purpose.
## Business account (User) [#business-account-user]
A **business account** is your brand or platform’s DrapeItOn account. It owns API keys, credits, webhook configuration, and optional end-user (shopper) limits.
Accounts are **invite-only beta**; there is no public self-registration. See [Introduction](/docs/getting-started/introduction) to request access.
## API key [#api-key]
An **API key** authenticates Integration API requests. Create keys in the dashboard. The full secret is shown **once** at creation; later you only see a masked form. You may have up to **10** active keys.
Send the secret with each Integration API request as `X-API-Key` or `Authorization: Bearer`. Details: [Authentication](/docs/guides/authentication).
If an administrator **blocks** the account, programmatic try-on and dashboard access are disabled until unblocked.
## End-user (shopper) [#end-user-shopper]
An **end-user** is a shopper on *your* site or app. They do not create a DrapeItOn account. You may pass an optional `end_user_id` on generation requests so the platform can enforce per-shopper cooldown and monthly limits you configure in the dashboard. See [End-user limits](/docs/guides/end-user-limits).
## Generation [#generation]
A **generation** (try-on job) takes a person image URL, a garment image URL, and a garment **category**, then produces a result image (or a failure). Jobs are asynchronous: you receive a job id immediately, then poll or wait for a webhook.
Statuses and lifecycle: [Generations](/docs/concepts/generations).
## Credit [#credit]
A **credit** is the unit of try-on usage. Submitting a generation reserves **one credit**. Balance is available via `GET /v1/credits`. Credits reserved for jobs that fail terminally are returned to your balance. See [Credits](/docs/concepts/credits).
## Webhook [#webhook]
A **webhook** is an HTTPS endpoint *you* host. When a generation finishes, DrapeItOn can POST an event (`generation.completed` or `generation.failed`) so you do not have to poll. Configure URLs and secrets in the dashboard. See [Webhooks](/docs/guides/webhooks).
## Integration API vs dashboard [#integration-api-vs-dashboard]
| | Integration API | Dashboard |
| ----------- | -------------------------------------------------------------- | ----------------------------------- |
| Auth | API key | Email / password session |
| Typical use | Production try-on traffic | Keys, webhooks, limits, usage views |
| Docs | [API Reference](https://api.drapeiton.com/docs) + these guides | Product UI |
## Related [#related]
* [Introduction](/docs/getting-started/introduction) — product positioning
* [Quickstart](/docs/getting-started/quickstart) — first request
# Introduction (/docs/getting-started/introduction)
# Introduction [#introduction]
DrapeItOn is a **virtual try-on API** for fashion brands and e-commerce platforms. Your shoppers provide a photo; you provide a garment image; the platform returns a try-on result you can show in your storefront, app, or pipeline.
## Who it is for [#who-it-is-for]
* Brands and retailers embedding try-on in product pages or fitting flows
* Platforms building fashion tooling that needs try-on as a backend capability
You integrate with a **server-side API key**. End shoppers do not hold DrapeItOn accounts.
## Integration API vs dashboard [#integration-api-vs-dashboard]
| Surface | Purpose |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Integration API** | Programmatic try-on: upload, submit jobs, poll results, balance, webhooks. Authenticated with an **API key**. Documented at [api.drapeiton.com/docs](https://api.drapeiton.com/docs). |
| **Dashboard** | Account administration: create API keys, configure webhooks, view usage and credits, set shopper limits. Sign in with email/password. |
This documentation site focuses on **integration**. Use the dashboard for key and webhook setup; use the Integration API for production traffic.
## Access [#access]
### After you have an account [#after-you-have-an-account]
1. Sign in to the dashboard.
2. Create an API key (the secret is shown **once**).
3. Continue with the [Quickstart](/docs/getting-started/quickstart).
## Next [#next]
* [Quickstart](/docs/getting-started/quickstart) — first try-on, step by step
* [Concepts overview](/docs/concepts/overview) — domain vocabulary
* [API Reference](https://api.drapeiton.com/docs) — OpenAPI schemas
# Quickstart (/docs/getting-started/quickstart)
# Quickstart [#quickstart]
This tutorial walks through one complete try-on: obtain a public URL for a shopper photo, submit a generation, then fetch the result.
**Prerequisites**
* An invite-only beta business account (request access via [Introduction](/docs/getting-started/introduction) — no self-serve signup)
* An **API key** created in the dashboard (copy the secret when it is shown — it is not shown again)
* A person (shopper) image file and a garment image that is already reachable over HTTPS
Base URL for the Integration API:
```text
https://api.drapeiton.com
```
Replace `YOUR_API_KEY` and file paths below with your own values.
***
## 1. Authenticate [#1-authenticate]
Every Integration API request must include your API key, either as:
```http
X-API-Key: YOUR_API_KEY
```
or:
```http
Authorization: Bearer YOUR_API_KEY
```
The examples use `X-API-Key`.
***
## 2. Get a presigned upload URL (person image) [#2-get-a-presigned-upload-url-person-image]
Shopper photos are often not on a public CDN. Request a short-lived upload URL, then `PUT` the image bytes.
```bash
curl -sS -X POST "https://api.drapeiton.com/v1/uploads/presigned" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content_type":"image/jpeg"}'
```
Example response:
```json
{
"upload_url": "https://…",
"public_url": "https://…"
}
```
Upload the file (same `Content-Type` as requested):
```bash
curl -sS -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/jpeg" \
--data-binary @./person.jpg
```
Save `public_url` as `PERSON_URL` for the next step.
You will pass a **garment** image as a normal HTTPS URL (for example your product CDN). Both `person_image_url` and `garment_image_url` accept any publicly reachable URL; presign is only required when you need the platform to host the bytes. See [Uploading images](/docs/guides/uploading-images).
***
## 3. Submit a generation [#3-submit-a-generation]
```bash
curl -sS -X POST "https://api.drapeiton.com/v1/generations" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"person_image_url": "PERSON_URL",
"garment_image_url": "https://cdn.example.com/products/shirt.jpg",
"category": "tops"
}'
```
Successful submit returns **202** with a job id and initial status:
```json
{
"id": "job_…",
"status": "submitted",
"created_at": "…"
}
```
One **credit** is reserved when the job is accepted. See [Credits](/docs/concepts/credits).
`category` must be one of: `tops`, `bottoms`, `one-pieces`.
***
## 4. Get the result [#4-get-the-result]
### Option A — Poll [#option-a--poll]
```bash
curl -sS "https://api.drapeiton.com/v1/generations/JOB_ID" \
-H "X-API-Key: YOUR_API_KEY"
```
Poll until `status` is `succeeded` or `failed`. On success, use `output_url` for the result image. On failure, inspect `error`.
Statuses and recommended client behavior: [Handling results](/docs/guides/handling-results).
### Option B — Webhook [#option-b--webhook]
If you configured a webhook URL in the dashboard, the platform POSTs `generation.completed` or `generation.failed` when the job finishes. Verify the signature before trusting the body. See [Webhooks](/docs/guides/webhooks) and the [API Reference — Webhooks](https://api.drapeiton.com/docs).
***
## 5. Check balance (optional) [#5-check-balance-optional]
```bash
curl -sS "https://api.drapeiton.com/v1/credits" \
-H "X-API-Key: YOUR_API_KEY"
```
***
## Next steps [#next-steps]
* [Authentication](/docs/guides/authentication)
* [Webhooks](/docs/guides/webhooks)
* [Errors and retries](/docs/guides/errors-and-retries)
* [API Reference](https://api.drapeiton.com/docs)
# Authentication (/docs/guides/authentication)
# Authentication [#authentication]
The Integration API authenticates **business accounts** with an **API key**. Do not use dashboard session cookies for try-on traffic.
## Create an API key [#create-an-api-key]
1. Sign in to the dashboard (invite-only account).
2. Open API keys and create a key.
3. Copy the secret **immediately**. It is shown **once**; later list views only show a masked form.
4. You may hold up to **10** active keys. Revoke unused keys in the dashboard.
## Send the key on every request [#send-the-key-on-every-request]
Use either header (equivalent):
```http
X-API-Key: YOUR_API_KEY
```
```http
Authorization: Bearer YOUR_API_KEY
```
Example:
```bash
curl -sS "https://api.drapeiton.com/v1/credits" \
-H "X-API-Key: YOUR_API_KEY"
```
Missing or invalid keys receive an unauthorized error in the [standard error envelope](/docs/guides/errors-and-retries).
## Blocked accounts [#blocked-accounts]
If an administrator blocks your account, Integration API try-on and dashboard access are refused until the account is unblocked.
## Security tips [#security-tips]
* Store keys only on trusted servers; never ship production keys in mobile or browser apps that end users can extract.
* Rotate keys by creating a new key, deploying it, then revoking the old one.
* Prefer separate keys for staging and production if you use multiple environments.
## Related [#related]
* [Quickstart](/docs/getting-started/quickstart)
* [API Reference](https://api.drapeiton.com/docs)
# End-user limits (/docs/guides/end-user-limits)
# End-user limits [#end-user-limits]
Optional controls let you rate-limit **shoppers** (end-users) without creating DrapeItOn accounts for them.
## `end_user_id` [#end_user_id]
On `POST /v1/generations`, you may send:
```json
"end_user_id": "your-stable-shopper-id"
```
* Optional string, max length **100**
* Opaque to DrapeItOn — use your own customer or session id
* If omitted, cooldown and monthly limits for shoppers do not apply to that request
## Dashboard settings [#dashboard-settings]
In the dashboard (limits / settings), you can configure for your account:
| Control | Effect |
| ----------------- | ------------------------------------------------------------------------------------ |
| **Cooldown** | Minimum seconds between successive try-on **submissions** for the same `end_user_id` |
| **Monthly limit** | Maximum try-ons allowed per `end_user_id` per calendar month (or unlimited) |
Exact UI labels may vary; values are stored on the business account and enforced on Integration API submit when `end_user_id` is present.
## API errors [#api-errors]
| Situation | Code (typical) |
| --------------------- | ----------------------------------- |
| Cooldown still active | `COOLDOWN_ACTIVE` (HTTP 429) |
| Monthly cap reached | `MONTHLY_LIMIT_EXCEEDED` (HTTP 429) |
Handle these in your shopper UX (wait / upgrade / block further tries). See [Errors and retries](/docs/guides/errors-and-retries).
## Related [#related]
* [Submitting generations](/docs/guides/submitting-generations)
* [Concepts overview](/docs/concepts/overview)
# Errors and retries (/docs/guides/errors-and-retries)
# Errors and retries [#errors-and-retries]
## Error envelope [#error-envelope]
Failed Integration API responses use a consistent JSON body:
```json
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"status": 400,
"details": [{ "path": "field", "message": "…" }]
}
}
```
`details` is optional (used for validation issues). Always branch on `error.code` / HTTP status in clients; do not parse free-text `message` as a contract.
## Common codes [#common-codes]
| Code | Typical HTTP | When |
| ------------------------ | ------------ | ---------------------------------------------- |
| `UNAUTHORIZED` | 401 | Missing or invalid API key |
| `VALIDATION_ERROR` | 400 | Invalid or parameters failed validation |
| `NOT_FOUND` | 404 | Resource missing (e.g. unknown generation id) |
| `INSUFFICIENT_CREDITS` | 402 | Not enough credits to accept a generation |
| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests for this API key |
| `COOLDOWN_ACTIVE` | 429 | Shopper cooldown (when using `end_user_id`) |
| `MONTHLY_LIMIT_EXCEEDED` | 429 | Shopper monthly cap (when using `end_user_id`) |
| `FORBIDDEN` | 403 | Not allowed (e.g. blocked account contexts) |
| `SERVICE_UNAVAILABLE` | 503 | Temporary inability to complete the request |
| `INTERNAL_ERROR` | 500 | Unexpected server error |
Additional codes may appear; treat unknown codes as generic failures.
## Rate limiting [#rate-limiting]
Integration routes are **rate-limited per API key** and split into separate buckets by HTTP method class:
* **Read routes** (`GET`, `HEAD`): Sized for polling job status and fetching resources.
* **Write routes** (`POST`, `DELETE`, etc.): Separate tighter budget reserved for state-changing operations and GPU generation triggers.
Exhausting your budget on read routes will not block write requests, and vice versa. When limited, responses use `RATE_LIMIT_EXCEEDED` (HTTP 429) and include standard rate-limiting headers:
* `X-RateLimit-Limit` — Max capacity for the route class
* `X-RateLimit-Remaining` — Capacity remaining in the current window
* `X-RateLimit-Reset` — Unix timestamp (seconds) when the window resets
* `Retry-After` — Exact seconds to wait before retrying
Published numeric limits are environment-specific and respect the response headers when present.
## Generation processing [#generation-processing]
If try-on processing fails transiently, the platform may **retry the job automatically**. Do not assume a fixed number of processing attempts in client logic unless you have a separate public SLA.
When a job ends as failed, poll/webhook surfaces `failed` / `generation.failed`, and reserved credits are returned. See [Credits](/docs/concepts/credits).
## Webhook delivery retries [#webhook-delivery-retries]
Outbound webhooks to **your** URL are retried with backoff on network errors and most non-2xx responses. Certain client HTTP statuses stop delivery retries immediately (`400`, `401`, `403`, `404`, `410`, `501`). Details: [Webhooks](/docs/guides/webhooks) and [API Reference — Webhooks](https://api.drapeiton.com/docs).
## Client recommendations [#client-recommendations]
1. Retry idempotent GETs with backoff on `503` / network errors.
2. Do not blindly retry `POST /v1/generations` without your own idempotency strategy — each accept reserves a credit.
3. For webhooks, process idempotently and return 2xx after durable accept.
## Related [#related]
* [API Reference](https://api.drapeiton.com/docs)
# Handling results (/docs/guides/handling-results)
# Handling results [#handling-results]
Generations are **asynchronous**. After `POST /v1/generations` returns **202**, wait for a terminal status before showing a result to a shopper.
## Customer-facing statuses [#customer-facing-statuses]
| Status | Client action |
| ------------ | ------------------------------------------------------------ |
| `submitted` | In progress — wait |
| `queued` | In progress — wait |
| `processing` | In progress — wait |
| `succeeded` | Use `output_url` |
| `failed` | Read `error.code` / `error.message`; do not use `output_url` |
## Option A — Polling [#option-a--polling]
```bash
curl -sS "https://api.drapeiton.com/v1/generations/JOB_ID" \
-H "X-API-Key: YOUR_API_KEY"
```
Poll on an interval appropriate for your UX (for example every few seconds) until `status` is `succeeded` or `failed`. Back off if you poll aggressively; the API is rate-limited per API key (see [Errors and retries](/docs/guides/errors-and-retries)).
On success, fields of interest include `output_url`, `category`, and timestamps. On failure, `error` is present.
## Option B — Webhooks [#option-b--webhooks]
If a webhook is configured for the account, the platform POSTs when a job finishes:
* `generation.completed` — success (`data.status` is `succeeded`, includes `output_url`)
* `generation.failed` — failure (`data.status` is `failed`, includes `error`)
Verify signatures and return **2xx** quickly. Full payload schemas and delivery behavior: [Webhooks](/docs/guides/webhooks) and [API Reference — Webhooks](https://api.drapeiton.com/docs).
## Combining both [#combining-both]
Common pattern: register a webhook for production, and use polling as a fallback (e.g. timeout if no webhook, or for tools/scripts).
## Related [#related]
* [Generations concept](/docs/concepts/generations)
* [Submitting generations](/docs/guides/submitting-generations)
# Submitting generations (/docs/guides/submitting-generations)
# Submitting generations [#submitting-generations]
## Create a job [#create-a-job]
```bash
curl -sS -X POST "https://api.drapeiton.com/v1/generations" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"person_image_url": "https://…/person.jpg",
"garment_image_url": "https://…/garment.jpg",
"category": "tops",
"end_user_id": "shopper-optional-id"
}'
```
### Required fields [#required-fields]
| Field | Description |
| ------------------- | ---------------------------------- |
| `person_image_url` | Public URL of the person image |
| `garment_image_url` | Public URL of the garment image |
| `category` | `tops`, `bottoms`, or `one-pieces` |
### Optional fields [#optional-fields]
| Field | Description |
| --------------------------------- | --------------------------------------------------------------------- |
| `end_user_id` | Your shopper id (string, max 100). Enables cooldown / monthly limits. |
| `seed` | Integer seed for reproducibility (range documented in API reference) |
| `num_timesteps`, `guidance_scale` | Quality-related parameters (defaults and bounds in API reference) |
Full schemas: [API Reference](https://api.drapeiton.com/docs).
### Response [#response]
**202 Accepted** with:
```json
{
"id": "job_…",
"status": "submitted",
"created_at": "…"
}
```
One **credit** is reserved on accept. Continue with [Handling results](/docs/guides/handling-results).
## List jobs [#list-jobs]
```bash
curl -sS "https://api.drapeiton.com/v1/generations?limit=20&page=1" \
-H "X-API-Key: YOUR_API_KEY"
```
Optional query: `status` filter using customer-facing statuses (`submitted`, `queued`, `processing`, `succeeded`, `failed`). Pagination fields are documented in the API reference.
## Get one job [#get-one-job]
```bash
curl -sS "https://api.drapeiton.com/v1/generations/JOB_ID" \
-H "X-API-Key: YOUR_API_KEY"
```
## Related [#related]
* [Uploading images](/docs/guides/uploading-images)
* [Categories](/docs/concepts/categories)
* [End-user limits](/docs/guides/end-user-limits)
# Uploading images (/docs/guides/uploading-images)
# Uploading images [#uploading-images]
A generation requires two **publicly reachable** image URLs:
| Field | Meaning |
| ------------------- | -------------------------- |
| `person_image_url` | Shopper / model photograph |
| `garment_image_url` | Garment product image |
Both are required on `POST /v1/generations` and must be valid URLs. The API does **not** require either URL to come from DrapeItOn hosting.
## Presigned upload (`POST /v1/uploads/presigned`) [#presigned-upload-post-v1uploadspresigned]
Use this when you need the platform to host image bytes and give you a stable `public_url`.
**Contract (enforced by the API):**
1. `POST /v1/uploads/presigned` with JSON body `{ "content_type": "…" }`.
2. Allowed `content_type` values: `image/png`, `image/jpeg`, `image/jpg`, `image/webp`.
3. Response includes:
* `upload_url` — perform an HTTP **PUT** of the raw file bytes here
* `public_url` — use this URL later as `person_image_url` or `garment_image_url`
4. The PUT must use the **same** `Content-Type` you requested.
There is **no** separate “person” vs “garment” upload endpoint. The same presign + PUT flow produces a public URL you may assign to either generation field.
### Example [#example]
```bash
# 1) Request URLs
RESP=$(curl -sS -X POST "https://api.drapeiton.com/v1/uploads/presigned" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content_type":"image/jpeg"}')
UPLOAD_URL=$(echo "$RESP" | jq -r .upload_url)
PUBLIC_URL=$(echo "$RESP" | jq -r .public_url)
# 2) Upload bytes
curl -sS -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/jpeg" \
--data-binary @./person.jpg
# 3) Use $PUBLIC_URL as person_image_url (or garment_image_url) on submit
```
## When to use which source [#when-to-use-which-source]
| Situation | Approach |
| ----------------------------------------------- | -------------------------------------------------------------- |
| Shopper photo only on the device / your backend | Presign → PUT → use `public_url` as `person_image_url` |
| Garment already on your product CDN | Pass that HTTPS URL as `garment_image_url` directly |
| You want a platform-hosted URL for a garment | Same presign endpoint; use `public_url` as `garment_image_url` |
| Both images already public | Skip presign; pass both URLs on submit |
## Limits [#limits]
Supported MIME types for **presign** are listed above. Keep images reasonably sized for e-commerce use; use formats the presign endpoint accepts.
## Related [#related]
* [Quickstart](/docs/getting-started/quickstart)
* [Submitting generations](/docs/guides/submitting-generations)
* [API Reference — Uploads](https://api.drapeiton.com/docs)
# Webhooks (/docs/guides/webhooks)
# Webhooks [#webhooks]
Webhooks notify **your** HTTPS endpoint when a generation finishes, so you do not have to poll continuously.
## Setup [#setup]
1. In the dashboard, add a webhook URL (HTTPS recommended).
2. Store the webhook **secret** shown for that endpoint (used for signature verification).
3. Ensure your endpoint accepts `POST` and returns **2xx** after you have accepted the event.
Webhook URLs are not configured via the Integration API; use the dashboard.
## Events [#events]
| `X-DrapeItOn-Event` / `event_type` | Meaning |
| ---------------------------------- | ------------------------ |
| `generation.completed` | Try-on succeeded |
| `generation.failed` | Try-on failed terminally |
Envelope shape (customer body after delivery):
* `job_id`
* `user_id`
* `event_type`
* `data` — job snapshot (includes `output_url` on success, `error` on failure)
**Exact field schemas** for both events: [API Reference — Webhooks](https://api.drapeiton.com/docs). Prefer that page as the schema source of truth.
## Headers [#headers]
| Header | Purpose |
| ----------------------- | ------------------------------------------------------------------------- |
| `Content-Type` | `application/json` |
| `X-DrapeItOn-Event` | Event name |
| `X-DrapeItOn-Signature` | `sha256=` HMAC of the **raw request body** using your webhook secret |
| `User-Agent` | `DrapeItOn-Webhook/1.0` |
## Verify the signature [#verify-the-signature]
Compute HMAC-SHA256 over the **raw body bytes** with your webhook secret, hex-encode the digest, and compare to the value after `sha256=` in `X-DrapeItOn-Signature`. Use a constant-time comparison.
Example (Node.js):
```js
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyDrapeItOnSignature(rawBody, signatureHeader, secret) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const match = /^sha256=([a-f0-9]+)$/i.exec(signatureHeader || "");
if (!match) return false;
const a = Buffer.from(expected, "hex");
const b = Buffer.from(match[1], "hex");
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}
```
Reject requests that fail verification.
## Delivery behavior [#delivery-behavior]
* Acknowledge with **HTTP 2xx** as soon as you have safely accepted the event (process heavy work asynchronously if needed).
* Failed deliveries are **retried automatically with backoff**. Exact retry counts and timing may change; the [API Reference — Webhooks](https://api.drapeiton.com/docs) description states the current public detail.
* Delivery **stops without further retries** if your endpoint responds with one of: **`400`**, **`401`**, **`403`**, **`404`**, **`410`**, **`501`**.
* Other non-2xx responses and network failures are treated as retryable until the platform stops retrying.
* Deliveries may be **at-least-once** — make handlers idempotent on `job_id` + `event_type`.
## Related [#related]
* [Handling results](/docs/guides/handling-results)
* [API Reference](https://api.drapeiton.com/docs)