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

# Variants in the Livry Public API

> Create, read, rename and delete Variants — the per-brand override sets layered on a Theme — and read the CDN URLs each one is served at.

A Variant is one set of **overrides** on one Theme. It is what you create per brand, per customer, or
per white-label deployment.

Resolution is the Theme's document overlaid with the Variant's, path by path. **The Theme owns the
structure** — the tree, the names, `$type`, `$description` — and a Variant owns values and nothing
else. A Variant cannot add a token the Theme does not have, and a path it overrides that the Theme
lacks is ignored. See [Variants](/theming/variants).

## Endpoints

| | | Needs |
| - | - | - |
| `GET` | `/public/v1/environments/{environmentID}/themes/{themeID}/variants` | `viewer` |
| `GET` | `…/variants/{variantID}` | `viewer` |
| `POST` | `…/themes/{themeID}/variants` | `editor` |
| `PUT` | `…/variants/{variantID}` | `editor` |
| `DELETE` | `…/variants/{variantID}` | `editor` |

Overrides are a sub-resource with their own rules; see [Variant tokens](/api/variant-tokens).

## List Variants

<ParamField query="slug" type="string">
  Exact match, within this Theme.
</ParamField>

<ParamField query="orderDir" default="ascending" type="string" />

<ParamField query="pageNumber" default="1" type="integer" />

<ParamField query="pageSize" default="100" type="integer" />

<ParamField query="includeTotals" default="false" type="boolean" />

## Create a Variant

```http theme={null}
POST /public/v1/environments/{environmentID}/themes/{themeID}/variants
```

<CodeGroup>
  ```json Request theme={null}
  {
    "slug": "northwind",
    "displayName": "Northwind"
  }
  ```

  ```json Response 201 theme={null}
  {
    "resource": {
      "id": "bV8nT4zC",
      "themeID": "kQ3mW9rP",
      "slug": "northwind",
      "displayName": "Northwind",
      "tokenCount": 0,
      "latestVersion": null,
      "publishedAt": null,
      "hasUnpublishedChanges": false,
      "metadata": {},
      "servingUrls": {
        "latest": [
          {
            "file": "tokens.css",
            "url": "https://cdn.livry.dev/nR7xL2vB:production/kQ3mW9rP/bV8nT4zC/latest/tokens.css"
          }
        ],
        "pinned": [
          {
            "file": "tokens.css",
            "url": "https://cdn.livry.dev/nR7xL2vB:production/kQ3mW9rP/bV8nT4zC/7/tokens.css"
          }
        ],
        "version": 7
      },
      "createdAt": "2026-09-18T04:33:12Z",
      "updatedAt": "2026-09-18T04:33:12Z"
    }
  }
  ```
</CodeGroup>

<Warning>
  **Creating a Variant can be refused with `402`.** The Team's plan caps how many Variants a Theme
  may have — 10 on Free, 100 on Pro. The body's `cause` is `planLimit`, and retrying will not help.
  See [Errors](/api/errors) and [Plans and limits](/settings/billing).
</Warning>

## Update a Variant

```http theme={null}
PUT …/variants/{variantID}
```

Renames the Variant and sets its labels. It cannot touch its overrides.

`slug` and `displayName` are required and replace what is stored. `metadata` behaves exactly as it
does on a Theme: **omit it to leave the labels alone, send `{}` to clear them.** The same bounds
apply — 50 keys, 64-character keys, 256-character values.

## Delete a Variant

```http theme={null}
DELETE …/variants/{variantID}
```

Answers `200` with the parent Theme. Every published version of the Variant is removed with it, and
anything reading its serving URLs starts getting `404`.

## The Variant resource

<ResponseField name="id" type="string" required />

<ResponseField name="themeID" type="string" required />

<ResponseField name="slug" type="string" required>
  Unique within the Theme, not globally.
</ResponseField>

<ResponseField name="displayName" type="string" required />

<ResponseField name="tokenCount" type="integer" required>
  How many tokens this Variant **overrides** — not how many the resolved theme has.
</ResponseField>

<ResponseField name="latestVersion" type="integer | null">
  The newest published version of the overrides.

  <Note>
    **A Variant version number *is* the Theme version its overrides are addressed against**, not a
    count of this Variant's own publishes. A Variant's overrides only mean anything against a
    particular Theme structure, so the two numbers are one number.

    The consequence is a real limit: **a Variant can be published only once per Theme version.** A
    second publish against the same Theme version answers `409`.
  </Note>
</ResponseField>

<ResponseField name="publishedAt" type="string | null" />

<ResponseField name="hasUnpublishedChanges" type="boolean" required>
  Expect this to be `true` on Variants nobody touched after their Theme publishes — that is the
  automatic migration waiting for review. See [Versioning](/theming/versioning).
</ResponseField>

<ResponseField name="metadata" type="object" required>
  Your labels. Never a secret.
</ResponseField>

<ResponseField name="servingUrls" type="object" required>
  Where this Variant is served: the Theme's address with this Variant's id in the third segment,
  where a Theme on its own carries `-`.

  **Which files exist is decided by the Theme's integrations.** A Variant has none of its own, so
  this list and the Theme's always name the same files.

  `version` here is the **Theme's** newest published version, for the reason above.
</ResponseField>

<ResponseField name="createdAt" type="string" required />

<ResponseField name="updatedAt" type="string" required />

## What a Variant is not

<Info>
  Say **Variant**, not "brand" and not "organization". A Variant is the product concept; "brand" is
  how we describe what you use it *for*. Nothing sits between an Environment and a Theme, and nothing
  sits below a Variant — there is no application, tenant or sub-brand layer, and there will not be.
</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.