---
title: "/creatives/generate"
method: POST
path: "/projects/{project_id}/creatives/generate"
tags: ["Creatives"]
---

# /creatives/generate

`POST /projects/{project_id}/creatives/generate`

## Request

- Base URL: `https://api.productbyte.com/v1`
- URL: `https://api.productbyte.com/v1/projects/{project_id}/creatives/generate`
- Auth: HTTP bearer

Generates a creative from a brief — a still image, or a multi-slide carousel with `type: carousel`.

This is the other way to make a creative — `/creatives/upload` is the one where you supply the media.

**It answers before the work is done.** Composing a creative is an LLM pipeline that runs for minutes, far longer than a request should wait, so you get a generation to poll at `/creatives/generations/{generation_id}`. The `creative_id` is usable immediately: the creative exists from the start, empty, and fills in when the generation succeeds.

Naming `product_ids` builds the creative around those catalogue products, using their images and fields. Without them the composer works from the brief alone.

Generation consumes the workspace's credits, so this can fail on billing as well as on input.

Requires the `creatives:write` scope.

## Path parameters

- `project_id` string, required: The project to create the creative in.

## Request body

- object
  - `type` 'image' | 'carousel': What to compose. `carousel` produces a multi-slide deck.
  - `prompt` string, required: The brief, in your own words — what the creative is for, and any direction to follow. Up to 4000 characters.
  - `name` string: What to call the creative. Worth sending: an upload takes its name from the filename, but a generated creative has nothing to take one from and is otherwise unnamed in ProductByte.
  - `format` '1:1' | '4:5' | '16:9' | '9:16' | '2:3', required: Aspect ratio to compose at.
  - `language` 'en-GB' | 'en-US' | 'nl-NL' | 'fr-FR' | 'de-DE' | 'es-ES' | 'it-IT' | 'pt-PT' | 'pl-PL' | 'sv-SE' | 'da-DK' | 'fi-FI' | 'no-NO': Language of the copy rendered into the creative. Defaults to the project’s.
  - `product_ids` string[]: Catalogue products to build the creative around, from `/products`. Up to 10.
  - `variant_count` integer: How many options to produce. Image only — a carousel composes one deck.
  - `slide_count` integer: Slides in the deck, from 3 to 6. Carousel only; defaults to five.

## Response `202`

The generation was queued. Poll it to learn the outcome.

- object
  - `id` string, required: Pass this to `/creatives/generations/{generation_id}` to follow it.
  - `creative_id` string, required: The creative being generated into. It exists from the moment the generation starts, empty until the first result lands, so you can reference it before the work finishes.
  - `status` 'running' | 'succeeded' | 'failed', required: `running` until it settles. On `succeeded`, read the creative for its formats.
  - `error_code` string: Why it failed. A stable code, never prose.
  - `created_at` string, date-time, required: When the generation was started.
  - `updated_at` string, date-time, required: When it last changed.

## Other responses

- `401`: Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).
- `403`: The key lacks `creatives:write` or the project is outside its allowlist.
- `409`: The `Idempotency-Key` was already used with a different body (`API_IDEMPOTENCY_KEY_REUSED`), or the first request using it is still running (`API_IDEMPOTENCY_KEY_IN_FLIGHT`) — retry in a moment to collect its response.
- `429`: Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.

---

[API](https://skmtc.dev/productbyte/apis/productbyte-api.md) · [All operations](https://skmtc.dev/productbyte/apis/productbyte-api/llms.txt) · [OpenAPI document](https://skmtc.dev/productbyte/apis/productbyte-api/revisions/e44332d6b9c6?raw)
