---
title: "How a site goes live"
description: "Draft, revision, build, preview, publication and rollback, and which of them visitors can see."
canonical: "https://atelier201.com/docs/platform/drafts-builds-and-publishing"
updated: "2026-11-06"
---

# How a site goes live

A site moves through four stages. Only the last one changes what visitors see.

```mermaid title="From a change to a live site"
flowchart LR
  A["Draft<br/>every change"] --> B["Build<br/>draft compiled"]
  B --> C["Preview<br/>private link"]
  B --> D["Publication<br/>visitors see it"]
```

## Draft and revisions

The draft is the current state of the site's files. Each change creates a
**revision**: a Git commit id of 40 characters, with a one-sentence summary and
the author (the editor, the assistant, an agent over MCP, the CLI or the API).
Nothing is erased: the history lists every revision, and you can compare two of
them or restore an earlier one as a new entry.

A change is one revision, whatever it touches. A call that changes three files
writes one entry, and a change spread over several calls uses a
[changeset](/docs/api/changesets/open) that you commit once. A changeset stays
open for 10 minutes.

### Two writers at once

When you change a file, send the hash you read (`expected_hash`). If someone
changed that file since, the whole change is refused with `409 conflict`,
nothing is written, and a REST or CLI error carries the current revision:

```json
{
  "error": {
    "type": "conflict_error",
    "code": "conflict",
    "message": "...",
    "details": {
      "reason": "revision_conflict",
      "current_revision": "9f2c...",
      "paths": [
        { "path": "src/content/site/business.json", "expected": "a1b2...", "found": "c3d4..." }
      ]
    }
  }
}
```

Read the files named in `paths` again, apply your change on top and retry. Over
MCP the error says that the site changed since the edit was prepared, without
`details`, so read the files again yourself. The
content endpoints read the current hash themselves, so they only conflict when
someone writes at the very same moment.

## Builds

A build compiles the draft, or a revision you name, into a static site. It runs
in an isolated container with no network and no credentials, and stops after 5
minutes. A build moves through `queued`, `building` and `collecting` to `ready`,
or ends as `failed` or `cancelled`. Starting a build of a revision that already
has a ready build returns that one instead of building twice.

When a build fails, `error` says why and the build log shows the compiler's
output. Fix the files and build again.

## Previews

A preview is a private link to one build, for 7 days by default and 30 at most.
Anyone with the link sees that build, without an account. The link grants
nothing else, is never indexed and can be revoked. It shows the site as it would
look, so share it before you publish.

A preview is not a published site. Every page carries a small "Preview" ribbon
(hidden while you edit in the app), and it runs sandboxed with origin `null`.
Its scripts cannot set cookies, use browser storage or start a Worker, and a
third-party API that refuses that origin blocks them, so a form, a cookie banner
or a client-side tool that needs these only works once the site is published. A
build warning names the ones your site uses.

## Publishing

Publishing makes one build what visitors see on every address of the site,
within about a minute. It is the only stage that changes the public site.

1. **Review.** `review_publish` shows which build would go live, whether it is
   the latest draft, what blocks it, and an `expected_generation`.
2. **Publish** that build with the `expected_generation` you read.

The **generation** is a counter that increases with every publish, rollback and
unpublish. Sending it back means you publish exactly the state you reviewed. If
someone else published in between, the call is refused with `409 conflict` and
you review again.

A build cannot be published while a blocker stands:

| Blocker            | What to do                                                                           |
| ------------------ | ------------------------------------------------------------------------------------ |
| `guest_site`       | The person must [claim the site](/docs/platform/guest-sites) first.                  |
| `no_build`         | Build the draft.                                                                     |
| `build_not_ready`  | Wait for the build to be `ready`, or build again if it failed.                       |
| `origin_changed`   | The site's main address changed after this build. Build again.                       |
| `plan_required`    | The site is a draft: publishing needs the Site plan. Share a preview link meanwhile. |
| `site_unavailable` | The site is being created, suspended or deleted.                                     |

### Rollback and unpublish

A **rollback** serves the build of an earlier publication again, without
rebuilding. The draft does not change, so it can be newer than what visitors
see. A Site keeps its last 50 publications for rollback.

**Unpublish** takes the site offline: visitors see that there is no site at the
address until you publish again. Nothing is deleted.

Publish, rollback and unpublish need the `sites:publish` scope, and only a
workspace admin can use them. An agent without that scope can prepare everything
and ask the owner to publish.

## Export and import

The whole project exports as a tar archive that builds anywhere with
`npm ci && npm run build`. Importing an archive replaces the draft with its files
as one revision. `node_modules`, `dist`, `.git` and secret files are skipped. An
import neither builds nor publishes.
