Skip to main content
Every Theme and every Variant holds zero or one draft and N immutable versions.

The draft

A draft is created by the first edit that changes something, and deleted when you publish or discard.
“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.
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.
1

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

Variants are migrated

Every Variant beneath the Theme is corrected onto the new structure. See below.
3

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

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.

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

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

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.

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.