# Styles API reference

> HTML version: https://storeshot.art/docs/api/styles/

Brand kits: save a palette, logo, background and caption tone once and reuse them with `styleId`. Contract 1.18.0; base URL `https://api.storeshot.art/v1`. Schemas: https://storeshot.art/docs/api/schemas.md

## GET /styles

**List styles** (operation `listStyles`). Auth: API key with scope `generations:read` or `generations:write`, or access token.

Requires `generations:read` or `generations:write`. Styles of the account (every API key and environment), most recently updated first.

**When to use.** To find the `styleId` your human means (match on `name`).

**Credits.** Free: no credits.

**Parameters:**

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `limit` | query | `integer` | no | 1–100, default 20. Default: 20 |
| `cursor` | query | `string` | no | `nextCursor` of the previous page. |

**Responses:**

| Status | Description | Schema | Headers |
|---|---|---|---|
| `200` | A page of styles. | `StyleList` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `400` | `INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `401` | `UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential. | `ErrorEnvelope` | `X-Request-Id` |
| `403` | `FORBIDDEN`: see `details.reason`. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `429` | `RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy`, `Retry-After` |
| `default` | Error (see ErrorEnvelope and /v1/docs/errors). | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |

Example 200 response:

```json
{
  "data": [
    {
      "id": "3b5d7f9a-1c2e-4d6f-8a0b-2c4e6f8a0b1c",
      "name": "Trailmate brand",
      "palette": [
        {
          "hex": "#0F766E",
          "name": "Teal"
        },
        {
          "hex": "#F8FAFC",
          "name": "Off White"
        }
      ],
      "logo": null,
      "backgroundMood": "dark",
      "backgroundPrompt": "soft paper texture with mountain line art",
      "captionTone": "friendly",
      "referenceGenerationId": null,
      "createdAt": "2026-11-03T10:00:00.000Z",
      "updatedAt": "2026-11-03T10:05:00.000Z"
    }
  ],
  "nextCursor": "string"
}
```

## POST /styles

**Create a style (brand kit)** (operation `createStyle`). Auth: API key with scope `generations:write`, or access token.

Requires `generations:write`. Saves a palette (up to 8 colours), a logo (copied from an upload), a background mood and description, and a caption tone once; send its `id` as `styleId` when you create a generation or a screenshot set. `referenceGenerationId` copies the colours (and style choices) of a succeeded generation into the fields you leave out. Send an `Idempotency-Key` so a retried request never creates twice; a repeat with the same body answers the style (`200`, `Idempotent-Replayed: true`). At most 50 styles per account (`403 FORBIDDEN`, `details.reason` `style_limit_reached`). Styles are free.

**When to use.** Once per brand: save the palette, logo, background mood and caption tone, then send `styleId` with every generation or screenshot set instead of repeating them.

**Credits.** Free: no credits.

**Parameters:**

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `idempotency-key` | header | `string` | no | 1–255 printable ASCII characters. Retrying with the same key and body returns the original generation (`Idempotent-Replayed: true`) instead of creating and charging another. Keys are kept as long as the generation exists. |

**Request body** (`application/json`, required, schema `CreateStyleRequest`):

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | yes |  |
| `palette` | `array of PaletteColor` | no | 1–8 colours. |
| `logo` | `object` | no |  |
| `logo.uploadId` | `string` | yes | A `ready` upload of this account (PNG, JPEG or WebP, at most 5 MB and 4096 × 4096 pixels; either environment). It is copied: deleting the upload later keeps the logo. Format: uuid |
| `backgroundMood` | `BackgroundMood & string` | no |  |
| `backgroundPrompt` | `string` | no | At most 200 characters on one line: what the background looks like. It is passed to the model as a quoted description of the look, never as instructions. |
| `captionTone` | `CaptionTone & string` | no |  |
| `referenceGenerationId` | `string` | no | A succeeded generation of yours: its colours (and, when it was made with a style, that style's background and caption choices) are copied into the fields you leave out, once. Its image and logo are not used. Format: uuid |

Example request body:

```json
{
  "name": "Trailmate brand",
  "palette": [
    {
      "hex": "#0F766E",
      "name": "Teal"
    },
    {
      "hex": "#F8FAFC",
      "name": "Off White"
    }
  ],
  "logo": {
    "uploadId": "6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"
  },
  "backgroundMood": "dark",
  "backgroundPrompt": "soft paper texture with mountain line art",
  "captionTone": "friendly"
}
```

**Responses:**

| Status | Description | Schema | Headers |
|---|---|---|---|
| `200` | A replay: the style created with this `Idempotency-Key`. | `Style` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy`, `Location`, `Idempotent-Replayed` |
| `201` | Created. | `Style` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy`, `Location` |
| `400` | `INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `401` | `UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential. | `ErrorEnvelope` | `X-Request-Id` |
| `403` | `FORBIDDEN`: see `details.reason`. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `409` | `IDEMPOTENCY_CONFLICT`: the `Idempotency-Key` was used for a different request (`details.styleId`). | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `429` | `RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy`, `Retry-After` |
| `default` | Error (see ErrorEnvelope and /v1/docs/errors). | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |

Example 200 response:

```json
{
  "id": "3b5d7f9a-1c2e-4d6f-8a0b-2c4e6f8a0b1c",
  "name": "Trailmate brand",
  "palette": [
    {
      "hex": "#0F766E",
      "name": "Teal"
    },
    {
      "hex": "#F8FAFC",
      "name": "Off White"
    }
  ],
  "logo": null,
  "backgroundMood": "dark",
  "backgroundPrompt": "soft paper texture with mountain line art",
  "captionTone": "friendly",
  "referenceGenerationId": null,
  "createdAt": "2026-11-03T10:00:00.000Z",
  "updatedAt": "2026-11-03T10:05:00.000Z"
}
```

Example 201 response:

```json
{
  "id": "3b5d7f9a-1c2e-4d6f-8a0b-2c4e6f8a0b1c",
  "name": "Trailmate brand",
  "palette": [
    {
      "hex": "#0F766E",
      "name": "Teal"
    },
    {
      "hex": "#F8FAFC",
      "name": "Off White"
    }
  ],
  "logo": null,
  "backgroundMood": "dark",
  "backgroundPrompt": "soft paper texture with mountain line art",
  "captionTone": "friendly",
  "referenceGenerationId": null,
  "createdAt": "2026-11-03T10:00:00.000Z",
  "updatedAt": "2026-11-03T10:05:00.000Z"
}
```

## GET /styles/{id}

**Get a style** (operation `getStyle`). Auth: API key with scope `generations:read` or `generations:write`, or access token.

Requires `generations:read` or `generations:write`.

**Credits.** Free: no credits.

**Parameters:**

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | `string` | yes | Style id. An unknown or malformed id is 404. |

**Responses:**

| Status | Description | Schema | Headers |
|---|---|---|---|
| `200` | The style. | `Style` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `400` | `INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `401` | `UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential. | `ErrorEnvelope` | `X-Request-Id` |
| `403` | `FORBIDDEN`: see `details.reason`. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `404` | `NOT_FOUND`: no such resource for this caller. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `429` | `RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy`, `Retry-After` |
| `default` | Error (see ErrorEnvelope and /v1/docs/errors). | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |

Example 200 response:

```json
{
  "id": "3b5d7f9a-1c2e-4d6f-8a0b-2c4e6f8a0b1c",
  "name": "Trailmate brand",
  "palette": [
    {
      "hex": "#0F766E",
      "name": "Teal"
    },
    {
      "hex": "#F8FAFC",
      "name": "Off White"
    }
  ],
  "logo": null,
  "backgroundMood": "dark",
  "backgroundPrompt": "soft paper texture with mountain line art",
  "captionTone": "friendly",
  "referenceGenerationId": null,
  "createdAt": "2026-11-03T10:00:00.000Z",
  "updatedAt": "2026-11-03T10:05:00.000Z"
}
```

## DELETE /styles/{id}

**Delete a style** (operation `deleteStyle`). Auth: API key with scope `generations:write`, or access token.

Requires `generations:write`. Deletes the style and its logo. Generations and screenshot sets made with it keep what they used (their `style.id` becomes `null`), and so do their retries.

**Credits.** Free: no credits.

**Parameters:**

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | `string` | yes | Style id. An unknown or malformed id is 404. |

**Responses:**

| Status | Description | Schema | Headers |
|---|---|---|---|
| `204` | Deleted. | — | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `400` | `INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `401` | `UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential. | `ErrorEnvelope` | `X-Request-Id` |
| `403` | `FORBIDDEN`: see `details.reason`. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `404` | `NOT_FOUND`: no such resource for this caller. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `429` | `RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy`, `Retry-After` |
| `default` | Error (see ErrorEnvelope and /v1/docs/errors). | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |

## PATCH /styles/{id}

**Update a style** (operation `updateStyle`). Auth: API key with scope `generations:write`, or access token.

Requires `generations:write`. Changes only the fields sent: `palette: []` clears the palette, `null` clears `logo`, `backgroundMood`, `backgroundPrompt` and `captionTone`. Generations and screenshot sets already made with the style, and their retries, keep what they used.

**Credits.** Free: no credits.

**Parameters:**

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | `string` | yes | Style id. An unknown or malformed id is 404. |

**Request body** (`application/json`, required, schema `UpdateStyleRequest`):

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | no |  |
| `palette` | `array of PaletteColor` | no | Replaces the palette; `[]` clears it. |
| `logo` | `object`, nullable | no | A new logo, or `null` to remove it. |
| `logo.uploadId` | `string` | yes | A `ready` upload of this account (PNG, JPEG or WebP, at most 5 MB and 4096 × 4096 pixels; either environment). It is copied: deleting the upload later keeps the logo. Format: uuid |
| `backgroundMood` | `BackgroundMood` | no | `null` clears it. |
| `backgroundPrompt` | `string`, nullable | no | At most 200 characters on one line; `null` clears it. |
| `captionTone` | `CaptionTone` | no | `null` clears it. |
| `referenceGenerationId` | `string`, nullable | no | Copy from this generation again (into the fields you leave out), or `null` to forget where the values came from (the values stay). Format: uuid |

Example request body:

```json
{
  "captionTone": "bold",
  "logo": null
}
```

**Responses:**

| Status | Description | Schema | Headers |
|---|---|---|---|
| `200` | The updated style. | `Style` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `400` | `INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `401` | `UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential. | `ErrorEnvelope` | `X-Request-Id` |
| `403` | `FORBIDDEN`: see `details.reason`. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `404` | `NOT_FOUND`: no such resource for this caller. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |
| `429` | `RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds. | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy`, `Retry-After` |
| `default` | Error (see ErrorEnvelope and /v1/docs/errors). | `ErrorEnvelope` | `X-Request-Id`, `RateLimit`, `RateLimit-Policy` |

Example 200 response:

```json
{
  "id": "3b5d7f9a-1c2e-4d6f-8a0b-2c4e6f8a0b1c",
  "name": "Trailmate brand",
  "palette": [
    {
      "hex": "#0F766E",
      "name": "Teal"
    },
    {
      "hex": "#F8FAFC",
      "name": "Off White"
    }
  ],
  "logo": null,
  "backgroundMood": "dark",
  "backgroundPrompt": "soft paper texture with mountain line art",
  "captionTone": "friendly",
  "referenceGenerationId": null,
  "createdAt": "2026-11-03T10:00:00.000Z",
  "updatedAt": "2026-11-03T10:05:00.000Z"
}
```
