> ## 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.

# Drafts, Publishing and Immutable Versions

> How a Livry draft becomes an immutable version, what publishing builds, and what happens to a Theme's Variants when it publishes.

Every Theme and every Variant holds **zero or one draft** and **N immutable versions**.

```text theme={null}
version 1  ─┐
version 2   ├─ immutable. Written once. Never editable, by anyone, ever.
version 3  ─┘
draft      ── your unpublished edits, standing on top of version 3
```

## The draft

A draft is created by **the first edit that changes something**, and deleted when you publish or
discard.

<Note>
  **"Is there a draft" and "are there unpublished changes" are the same question.** A draft is
  defined as the version below it with a patch applied, and the patch is recomputed on every save —
  so an edit that reverted itself leaves nothing behind. There is no state in which a draft says
  nothing.
</Note>

The API reports this as `hasUnpublishedChanges`.

## Publishing

Publishing freezes the draft as the next version and deletes the draft. Version numbers start at 1
and never skip.

It also does the thing that matters to your users: **it builds the files the edge serves.**

<Steps>
  <Step title="The version is written">
    The resolved document is stored under an explicit path, alongside the patch that produced it —
    which is what makes a version diffable for audit.
  </Step>

  <Step title="Variants are migrated">
    Every Variant beneath the Theme is corrected onto the new structure. See below.
  </Step>

  <Step title="Files are materialised">
    Every (Theme, Variant, version) combination is rendered into the files its integrations require,
    and `latest` is moved. This happens asynchronously, a few seconds behind the response.
  </Step>
</Steps>

<Warning>
  **Nothing is served until a first publish.** A Theme with a draft and no published version has
  serving URLs that answer `404`. This catches everybody once.
</Warning>

## Discarding

Discarding throws the draft away and leaves every version alone. It is not a deletion of the Theme's
tokens — the newest published version becomes current again.

## Older versions are read-only

Nothing in Livry can write an existing version. There is no version switcher in the portal and no
"edit version 3" operation on the API.

This is what makes a pinned URL worth pinning: `/{n}/tokens.css` is byte-for-byte stable forever, so
it is cached for a year. See [Caching](/serving/caching).

## What a Theme publish does to its Variants

A Variant's overrides are addressed against a particular Theme structure. When that structure
changes, the overrides have to follow.

Livry reads the patch the publish produced and, for every Variant:

| The Theme did | The Variant gets |
| - | - |
| Renamed a token | The override key renamed with it |
| Removed a token | The override dropped |
| Retyped a token past fitting | The override dropped |
| Added a token | Nothing — it inherits the new default |
| Changed a value | Nothing — its override still wins |

<Warning>
  **This writes each Variant's draft; it does not publish them.**

  After a Theme publish, Variants nobody touched will report unpublished changes. That is the
  migration waiting for a human to look at it. Review each one and publish.
</Warning>

### Help the migration out

A rename is tracked reliably only when the Theme's edit was expressed as a **`move`** operation. A
remove-and-add pair has to be inferred by matching removed content against added content, which can
lose an override when:

* the rename also changed the value, or
* two identically valued tokens were renamed in the same publish.

The portal's drag-and-drop emits the right thing for a straightforward move. If you are driving the
[API](/api/theme-tokens) directly, send `move` rather than `remove` + `add`.

## A Variant's own versions

A Variant version number **is** the Theme version its overrides answer.

So publishing a Variant writes a file numbered with the Theme's current version, and:

<Warning>
  **A Variant can be published only once per Theme version.** A second publish against the same Theme
  version finds the file already written and answers `409 conflict`. Re-reading and retrying will not
  clear it — publish the Theme first.
</Warning>

## Concurrency

Edits are guarded by an ETag on the draft, not last-write-wins. Read, edit, and send the ETag back;
a `409` means somebody else got there first.

The portal does this for you. Through the API it is the `?etag=` parameter — see
[Errors](/api/errors).

## Asset settings publish as a version too

Changing the CSS custom-property prefix creates a **new Theme version** carrying the previous
version's document and an empty diff.

That looks odd until you see why: the prefix changes the *names* in `tokens.css`, so a pinned URL
whose content changed would break an app that had already deployed against it. Publishing it as n+1
leaves every pinned file exactly as it was.


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