# DrapeItOn Integration API

- **OpenAPI Version:** `3.1.0`
- **API Version:** `1.0.0`

Programmatic API for **User** (business) integrations authenticated with an **ApiKey**.

Typical flow:

1. `POST /v1/uploads/presigned` — upload the **End-User** person image (garment URLs are usually already public on your site)
2. `POST /v1/generations` — submit a try-on job (1 **Credit**)
3. `GET /v1/generations/{id}` — poll, **or** receive a **Webhook** (`generation.completed` / `generation.failed`)
4. `GET /v1/credits` — check remaining balance

This reference covers the **Integration API** only (not the Dashboard API under `/v1/dashboard/*`). Product guides: <https://drapeiton.com/docs> — Markdown reference for LLMs: <https://api.drapeiton.com/reference.md>

## Servers

- **URL:** `https://api.drapeiton.com`
  - **Description:** Production

## Operations

### List generations

- **Method:** `GET`
- **Path:** `/v1/generations`
- **Tags:** Generations

Paginated **Generation** history for the authenticated business account.

#### Parameters

##### `limit`

- **In:** `query`

Page size (1–100, default 20)

`integer`, default: `20` — Page size (1–100, default 20)

##### `page`

- **In:** `query`

Page number (1-based, default 1)

`integer`, default: `1` — Page number (1-based, default 1)

##### `status`

- **In:** `query`

Filter by generation status

**All of:**

`string`, possible values: `"submitted", "queued", "processing", "succeeded", "failed"` — Client-facing generation lifecycle status

— Filter by generation status

#### Responses

##### Status: 200 Paginated list of generations

###### Content-Type: application/json

- **`data` (required)**

  `array`

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`id` (required)**

    `string` — Generation / job id

  - **`status` (required)**

    `string`, possible values: `"submitted", "queued", "processing", "succeeded", "failed"` — Client-facing generation lifecycle status

  - **`attempt`**

    `integer` — Current attempt number

  - **`category`**

    `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

  - **`completed_at`**

    `string | null`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`end_user_id`**

    `string | null`

  - **`error`**

    `object`

    - **`code` (required)**

      `string` — Machine-readable error code

    - **`message` (required)**

      `string` — Human-readable error message

  - **`garment_image_url`**

    `string`, format: `uri` — Public HTTPS image URL

  - **`inference_time_ms`**

    `integer | null` — Inference duration in milliseconds when available

  - **`max_attempts`**

    `integer` — Maximum attempts before permanent failure

  - **`output_url`**

    `string | null`, format: `uri` — Result image URL when status is succeeded

  - **`person_image_url`**

    `string`, format: `uri` — Public HTTPS image URL

- **`pagination` (required)**

  `object`

  - **`limit` (required)**

    `integer`

  - **`page` (required)**

    `integer`

  - **`total_count` (required)**

    `integer`

  - **`total_pages` (required)**

    `integer`

**Example:**

```json
{
  "data": [
    {
      "id": "job_a1b2c3d4",
      "status": null,
      "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
      "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
      "category": null,
      "output_url": "https://images.example.com/results/job_a1b2c3d4.png",
      "error": null,
      "attempt": 1,
      "max_attempts": 3,
      "inference_time_ms": 4200,
      "created_at": "2026-03-15T12:00:00.000Z",
      "completed_at": "2026-03-15T12:00:08.120Z",
      "end_user_id": "shopper_42"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total_count": 48,
    "total_pages": 3
  }
}
```

##### Status: 400 Request validation failed

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 401 Missing or invalid API key

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 500 Unexpected server error

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

### Submit a generation

- **Method:** `POST`
- **Path:** `/v1/generations`
- **Tags:** Generations

Submit a virtual try-on **Generation**. Reserves **1 credit**, accepts the job, and returns `202` with status `submitted`. Poll `GET /v1/generations/{id}` or receive an outbound **Webhook** when the job finishes (`generation.completed` / `generation.failed` — see Webhooks in this reference).

**Images:** `person_image_url` is usually a `public_url` from `POST /v1/uploads/presigned` (shopper photo). `garment_image_url` is typically an existing public HTTPS URL on your storefront; only use the upload endpoint for garments if you need a platform-hosted URL.

#### Request Body

##### Content-Type: application/json

- **`category` (required)**

  `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

- **`garment_image_url` (required)**

  `string`, format: `uri` — Public URL of the garment image (usually your storefront CDN)

- **`person_image_url` (required)**

  `string`, format: `uri` — Public URL of the shopper / person photo

- **`end_user_id`**

  `string` — Optional shopper id for per-end-user limits and cooldown

- **`guidance_scale`**

  `number`, default: `1.5` — Guidance scale (default 1.5)

- **`num_timesteps`**

  `integer`, default: `30` — Diffusion steps (default 30)

- **`seed`**

  `integer` — Optional RNG seed for reproducible results

**Example:**

```json
{
  "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
  "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
  "category": "tops",
  "end_user_id": "shopper_42",
  "seed": 42,
  "num_timesteps": 30,
  "guidance_scale": 1.5
}
```

#### Responses

##### Status: 202 Generation accepted

###### Content-Type: application/json

- **`created_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

- **`id` (required)**

  `string`

- **`status` (required)**

  `string`, possible values: `"submitted"`

**Example:**

```json
{
  "id": "job_a1b2c3d4",
  "status": "submitted",
  "created_at": "2026-03-15T12:00:00.000Z"
}
```

##### Status: 400 Request validation failed

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 401 Missing or invalid API key

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 402 Not enough credits to submit a generation

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 429 Rate limit exceeded, shopper cooldown active, or monthly limit exceeded

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 500 Unexpected server error

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 503 Service temporarily unavailable — retry with backoff

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

### Get a generation

- **Method:** `GET`
- **Path:** `/v1/generations/{id}`
- **Tags:** Generations

Poll status and result for a single **Generation** by id.

#### Parameters

##### `id` required

- **In:** `path`

Generation / job id

`string` — Generation / job id

#### Responses

##### Status: 200 Generation details

###### Content-Type: application/json

- **`created_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

- **`id` (required)**

  `string` — Generation / job id

- **`status` (required)**

  `string`, possible values: `"submitted", "queued", "processing", "succeeded", "failed"` — Client-facing generation lifecycle status

- **`attempt`**

  `integer` — Current attempt number

- **`category`**

  `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

- **`completed_at`**

  `string | null`, format: `date-time` — ISO-8601 timestamp (UTC)

- **`end_user_id`**

  `string | null`

- **`error`**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

- **`garment_image_url`**

  `string`, format: `uri` — Public HTTPS image URL

- **`inference_time_ms`**

  `integer | null` — Inference duration in milliseconds when available

- **`max_attempts`**

  `integer` — Maximum attempts before permanent failure

- **`output_url`**

  `string | null`, format: `uri` — Result image URL when status is succeeded

- **`person_image_url`**

  `string`, format: `uri` — Public HTTPS image URL

**Example:**

```json
{
  "id": "job_a1b2c3d4",
  "status": "succeeded",
  "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
  "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
  "category": "tops",
  "output_url": "https://images.example.com/results/job_a1b2c3d4.png",
  "error": {
    "code": "INFERENCE_FAILED",
    "message": "Try-on could not be completed for this image pair."
  },
  "attempt": 1,
  "max_attempts": 3,
  "inference_time_ms": 4200,
  "created_at": "2026-03-15T12:00:00.000Z",
  "completed_at": "2026-03-15T12:00:08.120Z",
  "end_user_id": "shopper_42"
}
```

##### Status: 401 Missing or invalid API key

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 404 Generation not found

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 500 Unexpected server error

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

### Get credit balance

- **Method:** `GET`
- **Path:** `/v1/credits`
- **Tags:** Credits

Returns the remaining **Credit** balance for the authenticated business account.

#### Responses

##### Status: 200 Current balance

###### Content-Type: application/json

- **`balance` (required)**

  `integer` — Remaining credits on the business account

- **`currency` (required)**

  `string`, possible values: `"credits"`

**Example:**

```json
{
  "balance": 240,
  "currency": "credits"
}
```

##### Status: 401 Missing or invalid API key

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 500 Unexpected server error

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

### Create a presigned upload URL (person image)

- **Method:** `POST`
- **Path:** `/v1/uploads/presigned`
- **Tags:** Uploads

Request a short-lived PUT URL to upload an image for use with the Integration API.

**Primary use:** upload the **End-User (shopper)** person / model photo. Shopper images are usually private and not already on a public CDN, so you obtain a `public_url` here and pass it as `person_image_url` on `POST /v1/generations`.

**Garment images:** typically already public on the **User** (business) storefront or CDN — pass that HTTPS URL as `garment_image_url` without uploading. You *may* still use this endpoint for garments if you need a platform-hosted URL.

Flow: `PUT` the raw image bytes to `upload_url` (same `Content-Type`), then use `public_url` in the generation request.

#### Request Body

##### Content-Type: application/json

- **`content_type` (required)**

  `string`, possible values: `"image/png", "image/jpeg", "image/jpg", "image/webp"` — MIME type of the image you will PUT

**Example:**

```json
{
  "content_type": "image/jpeg"
}
```

#### Responses

##### Status: 200 Presigned upload target and public URL

###### Content-Type: application/json

- **`public_url` (required)**

  `string`, format: `uri` — Stable public URL to use as person\_image\_url (or garment\_image\_url) after upload

- **`upload_url` (required)**

  `string`, format: `uri` — Short-lived PUT URL — send the raw image bytes here

**Example:**

```json
{
  "upload_url": "https://upload.example.com/input/usr_01/abc123.jpg?X-Amz-Signature=…",
  "public_url": "https://images.example.com/input/usr_01/abc123.jpg"
}
```

##### Status: 400 Request validation failed

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 401 Missing or invalid API key

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 429 Rate limit exceeded, shopper cooldown active, or monthly limit exceeded

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 500 Unexpected server error

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

##### Status: 503 Service temporarily unavailable — retry with backoff

###### Content-Type: application/json

- **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

## Webhooks

### generation.completed

- **Method:**`POST`
- **Path:**`/webhooks/generationCompleted`
- **Tags:** Webhooks

POSTed to your configured webhook URL when a **Generation** succeeds.

Envelope: `job_id`, `user_id`, `event_type: "generation.completed"`, and `data` (try-on result including `output_url`).

### Headers

- `Content-Type: application/json`
- `X-DrapeItOn-Event`: `generation.completed` or `generation.failed`
- `X-DrapeItOn-Signature`: `sha256=<hex>` — HMAC-SHA256 of the **raw body** using your webhook secret
- `User-Agent: DrapeItOn-Webhook/1.0`

### Delivery & retries

Respond with **2xx** quickly to acknowledge receipt.

Failed deliveries are **retried up to 5 times** with **exponential backoff** (minimum delay **0.5 seconds** between attempts).

**No further retries** if your endpoint returns one of these HTTP statuses: `400`, `401`, `403`, `404`, `410`, `501`.

**Retried** (up to the limit above) when:

- The request fails with a **network error** (DNS failure, connection refused, TLS error, timeout), or
- Your endpoint returns any other **non-2xx** status (e.g. `408`, `429`, `500`, `502`, `503`, `504`).

Configure destination URLs in the dashboard (not via this Integration API).

### generation.failed

- **Method:**`POST`
- **Path:**`/webhooks/generationFailed`
- **Tags:** Webhooks

POSTed to your configured webhook URL when a **Generation** fails (terminal failure).

Envelope: `job_id`, `user_id`, `event_type: "generation.failed"`, and `data` with `status: "failed"`, `output_url: null`, and `error: { code, message }`.

### Headers

- `Content-Type: application/json`
- `X-DrapeItOn-Event`: `generation.completed` or `generation.failed`
- `X-DrapeItOn-Signature`: `sha256=<hex>` — HMAC-SHA256 of the **raw body** using your webhook secret
- `User-Agent: DrapeItOn-Webhook/1.0`

### Delivery & retries

Respond with **2xx** quickly to acknowledge receipt.

Failed deliveries are **retried up to 5 times** with **exponential backoff** (minimum delay **0.5 seconds** between attempts).

**No further retries** if your endpoint returns one of these HTTP statuses: `400`, `401`, `403`, `404`, `410`, `501`.

**Retried** (up to the limit above) when:

- The request fails with a **network error** (DNS failure, connection refused, TLS error, timeout), or
- Your endpoint returns any other **non-2xx** status (e.g. `408`, `429`, `500`, `502`, `503`, `504`).

Configure destination URLs in the dashboard (not via this Integration API).

## Schemas

### WebhookGenerationCompleted

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`attempt` (required)**

    `integer`

  - **`category` (required)**

    `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

  - **`completed_at` (required)**

    `string`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`created_at` (required)**

    `string`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`end_user_id` (required)**

    `string | null`

  - **`garment_image_url` (required)**

    `string`, format: `uri` — Public HTTPS image URL

  - **`id` (required)**

    `string`

  - **`inference_time_ms` (required)**

    `integer`

  - **`max_attempts` (required)**

    `integer`

  - **`output_url` (required)**

    `string`, format: `uri`

  - **`person_image_url` (required)**

    `string`, format: `uri` — Public HTTPS image URL

  - **`status` (required)**

    `string`, possible values: `"succeeded"`

* **`event_type` (required)**

  `string`, possible values: `"generation.completed"`

* **`job_id` (required)**

  `string`

* **`user_id` (required)**

  `string`

**Example:**

```json
{
  "job_id": "job_a1b2c3d4",
  "user_id": "usr_01HXYZ",
  "event_type": "generation.completed",
  "data": {
    "id": "job_a1b2c3d4",
    "status": "succeeded",
    "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
    "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
    "category": null,
    "output_url": "https://images.example.com/results/job_a1b2c3d4.png",
    "attempt": 1,
    "max_attempts": 3,
    "inference_time_ms": 4200,
    "created_at": "2026-03-15T12:00:00.000Z",
    "completed_at": "2026-03-15T12:00:08.120Z",
    "end_user_id": "shopper_42"
  }
}
```

### WebhookGenerationCompletedData

- **Type:**`object`

* **`attempt` (required)**

  `integer`

* **`category` (required)**

  `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

* **`completed_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

* **`created_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

* **`end_user_id` (required)**

  `string | null`

* **`garment_image_url` (required)**

  `string`, format: `uri` — Public HTTPS image URL

* **`id` (required)**

  `string`

* **`inference_time_ms` (required)**

  `integer`

* **`max_attempts` (required)**

  `integer`

* **`output_url` (required)**

  `string`, format: `uri`

* **`person_image_url` (required)**

  `string`, format: `uri` — Public HTTPS image URL

* **`status` (required)**

  `string`, possible values: `"succeeded"`

**Example:**

```json
{
  "id": "job_a1b2c3d4",
  "status": "succeeded",
  "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
  "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
  "category": "tops",
  "output_url": "https://images.example.com/results/job_a1b2c3d4.png",
  "attempt": 1,
  "max_attempts": 3,
  "inference_time_ms": 4200,
  "created_at": "2026-03-15T12:00:00.000Z",
  "completed_at": "2026-03-15T12:00:08.120Z",
  "end_user_id": "shopper_42"
}
```

### GarmentCategory

- **Type:**`string`

Garment category for the try-on model

**Example:**

### WebhookGenerationFailed

- **Type:**`object`

* **`data` (required)**

  `object`

  - **`attempt` (required)**

    `integer`

  - **`category` (required)**

    `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

  - **`completed_at` (required)**

    `string`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`created_at` (required)**

    `string`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`end_user_id` (required)**

    `string | null`

  - **`error` (required)**

    `object`

    - **`code` (required)**

      `string` — Machine-readable error code

    - **`message` (required)**

      `string` — Human-readable error message

  - **`garment_image_url` (required)**

    `string`, format: `uri` — Public HTTPS image URL

  - **`id` (required)**

    `string`

  - **`inference_time_ms` (required)**

    `null`

  - **`max_attempts` (required)**

    `integer`

  - **`output_url` (required)**

    `null`

  - **`person_image_url` (required)**

    `string`, format: `uri` — Public HTTPS image URL

  - **`status` (required)**

    `string`, possible values: `"failed"`

* **`event_type` (required)**

  `string`, possible values: `"generation.failed"`

* **`job_id` (required)**

  `string`

* **`user_id` (required)**

  `string`

**Example:**

```json
{
  "job_id": "job_a1b2c3d4",
  "user_id": "usr_01HXYZ",
  "event_type": "generation.failed",
  "data": {
    "id": "job_a1b2c3d4",
    "status": "failed",
    "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
    "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
    "category": null,
    "output_url": null,
    "error": null,
    "attempt": 3,
    "max_attempts": 3,
    "inference_time_ms": null,
    "created_at": "2026-03-15T12:00:00.000Z",
    "completed_at": "2026-03-15T12:00:12.000Z",
    "end_user_id": "shopper_42"
  }
}
```

### WebhookGenerationFailedData

- **Type:**`object`

* **`attempt` (required)**

  `integer`

* **`category` (required)**

  `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

* **`completed_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

* **`created_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

* **`end_user_id` (required)**

  `string | null`

* **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

* **`garment_image_url` (required)**

  `string`, format: `uri` — Public HTTPS image URL

* **`id` (required)**

  `string`

* **`inference_time_ms` (required)**

  `null`

* **`max_attempts` (required)**

  `integer`

* **`output_url` (required)**

  `null`

* **`person_image_url` (required)**

  `string`, format: `uri` — Public HTTPS image URL

* **`status` (required)**

  `string`, possible values: `"failed"`

**Example:**

```json
{
  "id": "job_a1b2c3d4",
  "status": "failed",
  "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
  "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
  "category": "tops",
  "output_url": null,
  "error": {
    "code": "INFERENCE_FAILED",
    "message": "Try-on could not be completed for this image pair."
  },
  "attempt": 3,
  "max_attempts": 3,
  "inference_time_ms": null,
  "created_at": "2026-03-15T12:00:00.000Z",
  "completed_at": "2026-03-15T12:00:12.000Z",
  "end_user_id": "shopper_42"
}
```

### GenerationError

- **Type:**`object`

* **`code` (required)**

  `string` — Machine-readable error code

* **`message` (required)**

  `string` — Human-readable error message

**Example:**

```json
{
  "code": "INFERENCE_FAILED",
  "message": "Try-on could not be completed for this image pair."
}
```

### ListGenerationsResponse

- **Type:**`object`

* **`data` (required)**

  `array`

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`id` (required)**

    `string` — Generation / job id

  - **`status` (required)**

    `string`, possible values: `"submitted", "queued", "processing", "succeeded", "failed"` — Client-facing generation lifecycle status

  - **`attempt`**

    `integer` — Current attempt number

  - **`category`**

    `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

  - **`completed_at`**

    `string | null`, format: `date-time` — ISO-8601 timestamp (UTC)

  - **`end_user_id`**

    `string | null`

  - **`error`**

    `object`

    - **`code` (required)**

      `string` — Machine-readable error code

    - **`message` (required)**

      `string` — Human-readable error message

  - **`garment_image_url`**

    `string`, format: `uri` — Public HTTPS image URL

  - **`inference_time_ms`**

    `integer | null` — Inference duration in milliseconds when available

  - **`max_attempts`**

    `integer` — Maximum attempts before permanent failure

  - **`output_url`**

    `string | null`, format: `uri` — Result image URL when status is succeeded

  - **`person_image_url`**

    `string`, format: `uri` — Public HTTPS image URL

* **`pagination` (required)**

  `object`

  - **`limit` (required)**

    `integer`

  - **`page` (required)**

    `integer`

  - **`total_count` (required)**

    `integer`

  - **`total_pages` (required)**

    `integer`

**Example:**

```json
{
  "data": [
    {
      "id": "job_a1b2c3d4",
      "status": null,
      "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
      "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
      "category": null,
      "output_url": "https://images.example.com/results/job_a1b2c3d4.png",
      "error": null,
      "attempt": 1,
      "max_attempts": 3,
      "inference_time_ms": 4200,
      "created_at": "2026-03-15T12:00:00.000Z",
      "completed_at": "2026-03-15T12:00:08.120Z",
      "end_user_id": "shopper_42"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total_count": 48,
    "total_pages": 3
  }
}
```

### Generation

- **Type:**`object`

* **`created_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

* **`id` (required)**

  `string` — Generation / job id

* **`status` (required)**

  `string`, possible values: `"submitted", "queued", "processing", "succeeded", "failed"` — Client-facing generation lifecycle status

* **`attempt`**

  `integer` — Current attempt number

* **`category`**

  `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

* **`completed_at`**

  `string | null`, format: `date-time` — ISO-8601 timestamp (UTC)

* **`end_user_id`**

  `string | null`

* **`error`**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

* **`garment_image_url`**

  `string`, format: `uri` — Public HTTPS image URL

* **`inference_time_ms`**

  `integer | null` — Inference duration in milliseconds when available

* **`max_attempts`**

  `integer` — Maximum attempts before permanent failure

* **`output_url`**

  `string | null`, format: `uri` — Result image URL when status is succeeded

* **`person_image_url`**

  `string`, format: `uri` — Public HTTPS image URL

**Example:**

```json
{
  "id": "job_a1b2c3d4",
  "status": "succeeded",
  "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
  "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
  "category": "tops",
  "output_url": "https://images.example.com/results/job_a1b2c3d4.png",
  "error": {
    "code": "INFERENCE_FAILED",
    "message": "Try-on could not be completed for this image pair."
  },
  "attempt": 1,
  "max_attempts": 3,
  "inference_time_ms": 4200,
  "created_at": "2026-03-15T12:00:00.000Z",
  "completed_at": "2026-03-15T12:00:08.120Z",
  "end_user_id": "shopper_42"
}
```

### GenerationStatus

- **Type:**`string`

Client-facing generation lifecycle status

**Example:**

### Pagination

- **Type:**`object`

* **`limit` (required)**

  `integer`

* **`page` (required)**

  `integer`

* **`total_count` (required)**

  `integer`

* **`total_pages` (required)**

  `integer`

**Example:**

```json
{
  "page": 1,
  "limit": 20,
  "total_count": 48,
  "total_pages": 3
}
```

### Error

- **Type:**`object`

* **`error` (required)**

  `object`

  - **`code` (required)**

    `string` — Machine-readable error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`status` (required)**

    `integer` — HTTP status code (mirrors the response status)

  - **`details`**

    `array` — Optional field-level validation details

    **Items:**

    - **`message` (required)**

      `string`

    - **`path` (required)**

      `string`

**Example:**

```json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits to submit a generation.",
    "status": 402,
    "details": [
      {
        "path": "person_image_url",
        "message": "Invalid url"
      }
    ]
  }
}
```

### ErrorDetail

- **Type:**`object`

* **`message` (required)**

  `string`

* **`path` (required)**

  `string`

**Example:**

```json
{
  "path": "person_image_url",
  "message": "Invalid url"
}
```

### GenerationSubmitted

- **Type:**`object`

* **`created_at` (required)**

  `string`, format: `date-time` — ISO-8601 timestamp (UTC)

* **`id` (required)**

  `string`

* **`status` (required)**

  `string`, possible values: `"submitted"`

**Example:**

```json
{
  "id": "job_a1b2c3d4",
  "status": "submitted",
  "created_at": "2026-03-15T12:00:00.000Z"
}
```

### CreateGenerationRequest

- **Type:**`object`

* **`category` (required)**

  `string`, possible values: `"tops", "bottoms", "one-pieces"` — Garment category for the try-on model

* **`garment_image_url` (required)**

  `string`, format: `uri` — Public URL of the garment image (usually your storefront CDN)

* **`person_image_url` (required)**

  `string`, format: `uri` — Public URL of the shopper / person photo

* **`end_user_id`**

  `string` — Optional shopper id for per-end-user limits and cooldown

* **`guidance_scale`**

  `number`, default: `1.5` — Guidance scale (default 1.5)

* **`num_timesteps`**

  `integer`, default: `30` — Diffusion steps (default 30)

* **`seed`**

  `integer` — Optional RNG seed for reproducible results

**Example:**

```json
{
  "person_image_url": "https://images.example.com/shoppers/person_01.jpg",
  "garment_image_url": "https://cdn.example.com/products/shirt.jpg",
  "category": "tops",
  "end_user_id": "shopper_42",
  "seed": 42,
  "num_timesteps": 30,
  "guidance_scale": 1.5
}
```

### CreditBalance

- **Type:**`object`

* **`balance` (required)**

  `integer` — Remaining credits on the business account

* **`currency` (required)**

  `string`, possible values: `"credits"`

**Example:**

```json
{
  "balance": 240,
  "currency": "credits"
}
```

### PresignedUploadResponse

- **Type:**`object`

* **`public_url` (required)**

  `string`, format: `uri` — Stable public URL to use as person\_image\_url (or garment\_image\_url) after upload

* **`upload_url` (required)**

  `string`, format: `uri` — Short-lived PUT URL — send the raw image bytes here

**Example:**

```json
{
  "upload_url": "https://upload.example.com/input/usr_01/abc123.jpg?X-Amz-Signature=…",
  "public_url": "https://images.example.com/input/usr_01/abc123.jpg"
}
```

### PresignedUploadRequest

- **Type:**`object`

* **`content_type` (required)**

  `string`, possible values: `"image/png", "image/jpeg", "image/jpg", "image/webp"` — MIME type of the image you will PUT

**Example:**

```json
{
  "content_type": "image/jpeg"
}
```
