> ## Documentation Index
> Fetch the complete documentation index at: https://docs.genlook.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Release notes for the public Virtual Try-On API.

The Virtual Try-On API graduated from **alpha** to **v1.0.0** with this release. Everything prior was unstable and not under any compatibility commitment — every breaking change listed below was on the table during alpha. Going forward, v1 is the stable baseline; future changes will land as additions on top, or as a clearly-numbered v2 with its own migration guide.

If you were integrating against the alpha, see the [Breaking changes](/tryon-api/breaking-changes) page for migration recipes.

***

## 1.4.0 — 2026-06-17

<Note>
  **Not a breaking change.** The previous request shape is still accepted (deprecated). Existing integrations keep
  working unchanged — migrate at your own pace.
</Note>

### Changed

* **`POST /try-on` request shape redesigned.** The canonical body is now:
  * `products: [...]` — an **array** (was the singular `product` object). Exactly one item for now; multi-product try-on is on the roadmap.
  * `person: { image: { source: { id | url | fileKey } } }` — the shopper photo (was the `customer` object).
  * `externalUserId` — opaque, no-PII attribution + GDPR key (was `customerId`).
  * `output: { watermark, keepForDays }` — result controls (was the top-level `useWatermark` and `retentionDays`). `keepForDays` is one of `1`, `3`, `7` and sets how long the generation's stored images are kept.
* **`POST /images/upload` form fields renamed.** `externalUserId` (was `customerId`) and `keepForDays` (was `retentionDays`). `crop` is unchanged. The returned `imageId` is passed as `person.image.source.id` on `/try-on`.

### Deprecated

* **Previous try-on / upload shapes.** The old forms — `product` (singular object), `customer` (object), top-level `customerId`, `useWatermark`, `retentionDays` on `/try-on`, and `customerId` / `retentionDays` form fields on `/images/upload` — are still accepted but deprecated. Prefer `products[]`, `person.image`, `externalUserId`, `output.watermark` / `output.keepForDays`, and `keepForDays` in new code.

***

## 1.3.0 — 2026-06-17

### Changed

* **Image references moved under a `source` sub-object.** The customer photo on `POST /try-on` and each product image on `POST /try-on` (inline `product`) and `POST /products` now nest their reference inside `source`:
  * Customer: `customer: { source: { id | url | fileKey } }`.
  * Product images: `images: [{ source: { url | fileKey }, classifications? }]`. The optional `classifications` object now sits **alongside** `source` on each image entry.

### Deprecated

* **Legacy flat image shape.** The old top-level form — `customer: { id | url | fileKey }` and `images: [{ url | fileKey, classifications }]` — is still accepted but deprecated. Migrate to the `source` sub-object; prefer it in new code.

***

## 1.3.0 — 2026-06-25

### Removed

* **`autoClassifyImages` on products.** Removed from `POST /products`, inline `/try-on` product payloads, and product responses. Classification runs based on detected product type instead.

***

## 1.2.0 — 2026-05-18

### Added

* **`autoClassifyImages` flag on products.** Opt-in switch on `POST /products` and the inline `product` payload of `POST /try-on`. Defaults to `false` so generations stay as fast as possible — flip it to `true` on products where the smart front/back picker is worth the extra processing time.

### Changed

* **Smarter image selection for harder categories.** For some product types the engine now picks the image showing the product worn on a real person or bust rather than the flat packshot, when such an image is available. No API change required.

### Removed

* **`autoDetectImageRoles` field.** Replaced by `autoClassifyImages` (same intent, broader scope). Update any callers passing the old name.

***

## 1.1.0 — 2026-05-14

### Added

* **Per-call retention** on `POST /images/upload` and `POST /try-on`. New optional `retentionDays` body field (one of `1`, `3`, `7`) overrides the account-level customer-retention window for that single upload. Omitting the field keeps today's behaviour.
* **Customer identifier on uploads.** `POST /images/upload` now accepts an optional `customerId` body field. When set, the upload is tracked server-side so it can later be wiped by the new GDPR endpoint. `POST /try-on` already accepted `customerId`; same semantics now apply for both endpoints.
* **GDPR delete endpoint** — `DELETE /tryon/v1/customers/:customerId`. Deletes every customer-image and result image stored under the given `customerId`, anonymizes the corresponding generation rows, and removes upload tracking metadata. Idempotent, returns `204 No Content`. See [Delete Customer Data](/tryon-api/endpoints/delete-customer).
* **`PRODUCT_IMAGE_FETCH_FAILED` error code.** When the AI pipeline can't download a product image URL during processing, the generation transitions to `FAILED` with `errorCode: "PRODUCT_IMAGE_FETCH_FAILED"` on `GET /generations/:id`. Mirrors the synchronous `CUSTOMER_IMAGE_FETCH_FAILED` 400 already raised by `POST /try-on`.
* **`errorCode` on the generation-status response.** `GET /generations/:id` now returns a stable `errorCode` alongside `errorMessage` whenever the failure was raised as a typed error. Switch on the code instead of parsing the message.

***

## 1.0.0 — 2026-05-12

First stable release. The surface is intentionally small: upload a customer photo, run a try-on, poll the result. Everything else (products, account management, watermark) is optional.

### Added

* **Inline `product` field on `POST /try-on`.** Three usage modes in one endpoint:
  * Reference an existing product by `externalId`.
  * Create or update a product inline (full payload), then run the generation.
  * One-shot — omit `externalId` and the response returns a generated ID you can re-use.
* **Customer image — three input modes** on `POST /try-on`:
  * `customer: { id }` referencing a prior `POST /images/upload` (recommended for repeat use).
  * `customer.url` — server downloads on demand.
  * `customer.fileKey` — multipart file inline with the request.
* **Multipart product images** on `POST /try-on` and `POST /products`. Mix URLs and uploaded files in the same `images` array.
* **`crop` flag** on `POST /tryon/v1/images/upload`. Default `true`; pass `false` to preserve original framing.
* **`useWatermark` flag** on `POST /try-on`. Default `true`; pass `false` to skip the account's watermark logo for a single generation.
* **`productExternalId`** in every `/try-on` response — echoes the caller's value for named products, returns a server-generated ID for one-shots.
* **Per-product stats** at `GET /tryon/v1/products/:externalId/stats` — totals by status and average generation time.
* **Product lifetime model**:
  * Products created via `POST /products` are kept forever by default. Set `validForDays` to opt into a TTL.
  * Products created inline via `/try-on` live 15 days from last use (refreshed on every generation).
  * One-shot products live 7 days from last use.
  * Pass `validForDays: null` on an update to opt into "kept forever" later.
* **Partial updates on `POST /products`.** Send only the fields you want to change; omitted fields stay as they were.
* **Watermark feature** — composite your brand logo onto every generation. Configure once in the dashboard; opt out per-call with `useWatermark: false`. See [Watermark](/tryon-api/watermark).
* **Unified error responses.** Every failure returns `{ code, message, status, details? }` with a stable `code` from a documented enum — switch on that instead of parsing messages. Full list at [Errors](/tryon-api/errors).

### Changed

* **Single `product` field on `POST /try-on`** replaces the older `productId` / `externalProductId` body field. The endpoint figures out reference vs. upsert vs. one-shot from what's present.
* **Single `customer` object on `POST /try-on`** replaces the old `customerImageId` shortcut. Exactly one of `customer.id`, `customer.url`, or `customer.fileKey` must be set.
* **`POST /products` supports partial updates.** Hitting it with an existing `externalId` and only the field(s) you want to change merges over stored values.
* **Product responses** (`POST /products`, `GET /products`, `GET /products/:externalId`) return the full product shape — same fields everywhere, no `productId` (callers reference by `externalId`).
* **Product images on responses** are now `{ sourceUrl, order }`. For URL-sourced images `sourceUrl` echoes back the URL you supplied; for multipart-uploaded images it's an opaque internal reference you can ignore.
* **Consistent identifiers.** `productExternalId` is now returned on every generation. The reserved prefix `_anon_` is used for server-generated IDs.
* **Concurrent identical calls are safe.** Parallel inline upserts with the same content resolve to a single product — no duplicates, no race conditions.

### Removed

* **`PATCH /tryon/v1/products/:externalId`.** Use `POST /products` with the fields you want to change.
* **Generation ratings** (👍 / 👎) are no longer exposed on the public API — the stats endpoint returns counts and timings only.
* **`GET /tryon/v1/generations` (paginated list).** Track your own generation ids from `POST /try-on` responses and poll [`GET /generations/:id`](/tryon-api/endpoints/generation-status) for each one.
* **`resultImageKey`** has been dropped from `GET /generations/:id`. Use `resultImageUrl` instead — re-fetch the endpoint when the URL expires.
* **`productId`** has been dropped from `POST /products` responses. Reference products by `externalId`.

### Deprecated

* **`imageUrls: string[]`** on product upsert. Use `images: [{ url }, …]` instead. The old field is still accepted in v1 and will be removed in v2.

### Documentation

* New **[Quickstart](/tryon-api/quickstart)** — two-call try-on (upload → try-on) in under five minutes.
* New **[Full Example (Python)](/tryon-api/full-example)** — ref-first / upsert-on-miss template you can copy.
* New **[Watermark](/tryon-api/watermark)** page.
* New **[Breaking changes](/tryon-api/breaking-changes)** page — migration recipes for callers coming from the alpha.

***

## Alpha

Unreleased. No version was assigned. The endpoint shapes, response fields, and product lifecycle rules churned regularly during this period. If you have an integration that talked to the alpha surface, the [Breaking changes](/tryon-api/breaking-changes) page covers everything you need to update.

***

## Versioning

The Virtual Try-On API follows **semantic versioning** going forward:

* **Patch** (`1.0.x`) — bug fixes and additive non-breaking changes.
* **Minor** (`1.x.0`) — new endpoints, new optional fields, new response fields. Existing requests keep working.
* **Major** (`2.0.0`+) — breaking changes. Always announced ahead of time with a parallel migration window; the old major stays available until a posted sunset date.

The current major is always reachable at `/tryon/v1/*`. Subscribe to release notifications at [support@genlook.app](mailto:support@genlook.app).
