# Themes

A store's look rests on three choices, the same three tabs as the Themes page of the dashboard (`/dashboard/themes`): the storefront theme, the theme of the fast-checkout order form on product pages, and the style of the variant picker. `GET /v1/themes` lists the storefront themes with a verdict for the store, and one `POST` call switches each of the three choices. Colours, texts and the other design fields are changed with `PATCH /v1/store/design` (see [Store](https://dzbuild.com/api-docs/resources/store.md)). For what each theme looks like and what changes for buyers, see the merchant guide to [Themes](https://dzbuild.com/docs/customizing/themes.md).

## Before you start

* `GET /v1/themes` needs `store:read`. The three switches need `store:write` and an `Idempotency-Key`. Merchant keys carry both scopes.
* Each theme and style has a minimum plan. Plans rank `free`, `pro`, `unlimited`, `enterprise`, and a plan opens everything the plans below it open. A switch to a theme or style above the store's plan answers `403 plan_required`. A paid plan that has expired counts as `free`.
* A merchant key belongs to a store on an active Enterprise plan, so every theme and style is open to it. Installed-app tokens work on every plan and meet these limits.
* A switch is saved as soon as the call answers. There is no confirmation step.
* The first answer to an `Idempotency-Key` is replayed for 24 hours, `4xx` errors included. After the store's plan changes, send the switch again with a new key. See [Idempotency](https://dzbuild.com/api-docs/idempotency.md).
* Every switch is recorded and can be undone with `POST /v1/changes/{change_id}/undo`; find its id with `GET /v1/changes?entity=store.theme`. An installed app's token cannot read or undo changes: both answer `403 forbidden` (`Apps cannot use this endpoint`).

## `GET /v1/themes`

Lists the storefront themes with the plan each one needs and whether this store can use it, plus the key of the theme in use. The whole list comes in one answer, with no pagination. A theme that can no longer be selected stays in the list with `active: false`.

**Auth:** platform key with `store:read`. This call is not cached: every answer reads the current state.

### Request

```
curl https://api.dzbuild.app/v1/themes \

  -H "Authorization: Bearer $DZ_KEY"
```

### Response 200

Three items are shown. Titles come in the store language, and this store is set to French.

```
{

  "data": {

    "current": "starter",

    "items": [

      {

        "key":           "starter",

        "title":         "Starter",

        "plan_required": "free",

        "active":        true,

        "color_mode":    "light",

        "digital_only":  false,

        "can_use":       true,

        "current":       true

      },

      {

        "key":           "digital",

        "title":         "Digital",

        "plan_required": "free",

        "active":        true,

        "color_mode":    "dark",

        "digital_only":  true,

        "can_use":       true,

        "current":       false

      },

      {

        "key":           "ariana",

        "title":         "Ariana",

        "plan_required": "unlimited",

        "active":        false,

        "color_mode":    "dark",

        "digital_only":  false,

        "can_use":       false,

        "current":       false

      }

    ]

  },

  "meta": { "request_id": "...", "api_version": "v1" }

}
```

### Field reference

| Field                   | Type   | Notes                                                                                                                              |
| ----------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `current`               | string | Key of the theme the store uses.                                                                                                   |
| `items[].key`           | string | The value to send as `theme` to `POST /v1/store/theme`.                                                                            |
| `items[].title`         | string | The theme name in the store language: Arabic, or French for a French store.                                                        |
| `items[].plan_required` | string | The lowest plan that can use the theme.                                                                                            |
| `items[].active`        | bool   | `false` for a theme that can no longer be selected.                                                                                |
| `items[].color_mode`    | string | `light` or `dark`.                                                                                                                 |
| `items[].digital_only`  | bool   | `true` for a theme made for digital products only. Selecting it changes the store type, as described under `POST /v1/store/theme`. |
| `items[].can_use`       | bool   | `true` when the theme is active and the store's plan reaches `plan_required`.                                                      |
| `items[].current`       | bool   | `true` for the theme in use.                                                                                                       |

### Errors

The same as `GET /v1/store`: `401 unauthorized`, `402 quota_exceeded`, `403 forbidden`, `404 not_found` and `429 rate_limited`. See [Errors](https://dzbuild.com/api-docs/errors.md).

## `POST /v1/store/theme`

Switches the storefront theme. Only the theme changes: colours, texts and the other design values stay as they are, and the new theme shows the ones it uses. Switching to `digital` is the exception described below.

**Auth:** platform key with `store:write`. **Requires `Idempotency-Key`.**

### Body

| Field   | Type   | Required | Notes                                                                                   |
| ------- | ------ | -------- | --------------------------------------------------------------------------------------- |
| `theme` | string | yes      | A `key` from `GET /v1/themes`. Latin letters, digits, `_` and `-`, up to 50 characters. |

### Switching to `digital`

`digital` is the theme with `digital_only: true`. Switching to it turns the store into a digital-products store, and the answer carries `is_digital: true`. Switching a digital store to any other theme turns it back into a physical-products store. The merchant guide to [Themes](https://dzbuild.com/docs/customizing/themes.md) explains what changes for buyers.

The switch to `digital` also replaces the colours that still hold the stock light values, such as a `#ffffff` background, with the Digital dark palette. Colours the merchant chose are kept. Undoing the switch puts the earlier theme and those colours back.

### Request

```
curl -X POST https://api.dzbuild.app/v1/store/theme \

  -H "Authorization: Bearer $DZ_KEY" \

  -H "Content-Type: application/json" \

  -H "Idempotency-Key: store-theme-1" \

  -d '{"theme": "bloom"}'
```

### Response 200

```
{

  "data": {

    "theme":      "bloom",

    "is_digital": false

  },

  "meta": { "request_id": "...", "api_version": "v1" }

}
```

Through `api.dzbuild.app`, a `GET /v1/store/design` sent right after the switch can still show the old theme for up to 30 seconds. `GET /v1/themes` is not cached.

### Errors

| HTTP | Code                    | Cause                                                                                                              |
| ---- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| 400  | `bad_request`           | The body is not valid JSON, or `Idempotency-Key` is missing or malformed.                                          |
| 403  | `forbidden`             | `Missing scope: store:write`, or a merchant key whose store is not on an active Enterprise plan.                   |
| 403  | `plan_required`         | The theme needs a higher plan. The message names that plan and the store's plan.                                   |
| 404  | `theme_not_found`       | No active theme has this key.                                                                                      |
| 404  | `store_not_found`       | The store was deleted.                                                                                             |
| 422  | `invalid_theme`         | `theme` is missing, longer than 50 characters, or holds a character other than Latin letters, digits, `_` and `-`. |
| 422  | `idempotency_key_reuse` | The same `Idempotency-Key` was used with another body.                                                             |

## `POST /v1/store/fast-checkout-theme`

Switches the look of the fast-checkout order form on product pages. The form's texts, colours and switches, which are fields of `PATCH /v1/store/design`, stay as they are.

**Auth:** platform key with `store:write`. **Requires `Idempotency-Key`.**

### Body

| Field   | Type   | Required | Notes                                                                                |
| ------- | ------ | -------- | ------------------------------------------------------------------------------------ |
| `theme` | string | yes      | A key from the table below. Latin letters, digits, `_` and `-`, up to 50 characters. |

### Fast-checkout themes

No call lists these themes, and no read returns the one in use. Every store starts on `classic`. The merchant guide to [Themes](https://dzbuild.com/docs/customizing/themes.md) describes each one.

| `theme`     | Plan         |
| ----------- | ------------ |
| `classic`   | `free`       |
| `commerce`  | `pro`        |
| `editorial` | `unlimited`  |
| `compact`   | `enterprise` |
| `stepper`   | `enterprise` |

### Request

```
curl -X POST https://api.dzbuild.app/v1/store/fast-checkout-theme \

  -H "Authorization: Bearer $DZ_KEY" \

  -H "Content-Type: application/json" \

  -H "Idempotency-Key: store-fc-theme-1" \

  -d '{"theme": "stepper"}'
```

### Response 200

```
{

  "data": {

    "fast_checkout_theme": "stepper"

  },

  "meta": { "request_id": "...", "api_version": "v1" }

}
```

### Errors

| HTTP | Code                    | Cause                                                                                                              |
| ---- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| 400  | `bad_request`           | The body is not valid JSON, or `Idempotency-Key` is missing or malformed.                                          |
| 403  | `forbidden`             | `Missing scope: store:write`, or a merchant key whose store is not on an active Enterprise plan.                   |
| 403  | `plan_required`         | The theme needs a higher plan than the store's.                                                                    |
| 404  | `theme_not_found`       | No active fast-checkout theme has this key.                                                                        |
| 404  | `store_not_found`       | The store was deleted.                                                                                             |
| 422  | `invalid_theme`         | `theme` is missing, longer than 50 characters, or holds a character other than Latin letters, digits, `_` and `-`. |
| 422  | `idempotency_key_reuse` | The same `Idempotency-Key` was used with another body.                                                             |

## `POST /v1/store/variant-style`

Switches how the variant choices, such as sizes and colours, are drawn on product pages.

**Auth:** platform key with `store:write`. **Requires `Idempotency-Key`.**

### Body

| Field   | Type   | Required | Notes                                            |
| ------- | ------ | -------- | ------------------------------------------------ |
| `style` | string | yes      | A key from the table below, up to 50 characters. |

### Variant styles

No call lists the styles, and no read returns the one in use. Every store starts on `default`, the standard picker with no added style, and any store can go back to it. An unknown or inactive key answers `404 style_not_found`; the API never falls back to `default` on its own.

| Plan         | `style`                                         |
| ------------ | ----------------------------------------------- |
| every plan   | `default`                                       |
| `pro`        | `minimal`, `clay`, `softplay`, `stacked`        |
| `unlimited`  | `editorial`, `material`, `offer`                |
| `enterprise` | `brutal`, `glass`, `lux`, `mashrabiya`, `pixel` |

### Request

```
curl -X POST https://api.dzbuild.app/v1/store/variant-style \

  -H "Authorization: Bearer $DZ_KEY" \

  -H "Content-Type: application/json" \

  -H "Idempotency-Key: store-variant-style-1" \

  -d '{"style": "minimal"}'
```

### Response 200

```
{

  "data": {

    "variant_card_style": "minimal"

  },

  "meta": { "request_id": "...", "api_version": "v1" }

}
```

### Errors

| HTTP | Code                    | Cause                                                                                            |
| ---- | ----------------------- | ------------------------------------------------------------------------------------------------ |
| 400  | `bad_request`           | The body is not valid JSON, or `Idempotency-Key` is missing or malformed.                        |
| 403  | `forbidden`             | `Missing scope: store:write`, or a merchant key whose store is not on an active Enterprise plan. |
| 403  | `plan_required`         | The style needs a higher plan than the store's.                                                  |
| 404  | `style_not_found`       | No active style has this key.                                                                    |
| 404  | `store_not_found`       | The store was deleted.                                                                           |
| 422  | `invalid_style`         | `style` is missing, empty or longer than 50 characters.                                          |
| 422  | `idempotency_key_reuse` | The same `Idempotency-Key` was used with another body.                                           |
