Skip to main content
Publishing promotes the head version to the published version through a framework quality gate and a sandboxed build — the live site only ever changes when both succeed. Published sites in Free organizations display a compact Made with Cactal badge linking to Cactal. Paid organizations have no badge on either Cactal or custom domains. The badge is added when serving the page, not stored in website source or deployment assets; billing updates and cache invalidation change it without republishing. Draft previews do not display the badge.

The publish flow

1

Check the head version

POST /v1/websiteSourceCode/head/check runs the framework quality gate without changing anything: format, typecheck, lint, build, and a server-render of every page. It returns { "version": 12, "ok": true } or { "version": 12, "ok": false, "message": "...", "details": { ... } } describing what to fix.
2

Publish

POST /v1/websiteSourceCode/head/publish with { "websiteId": "...", "leaseToken": "..." } validates the head version, builds it in a sandbox, and promotes it. Publishing requires an active edit lease.
3

Wait for the deployment

The publish response includes the deployment. Its status is building, ready, or failed. You can poll POST /v1/websiteSourceCode/deployment/status with { "websiteId": "...", "target": "published" } at any time.
4

Verify the live site

On success, publishedVersion moves to the published head version. Open the platform subdomain and confirm the change. See Domains.
Run head/check before every publish. It surfaces framework validation errors cheaply, without holding the live site’s fate on a build.
Both head/check and head/publish run real builds and use the expensive rate-limit class. See Rate limits.

Unpublish a site

POST /v1/websiteSourceCode/published/unpublish with { "websiteId": "..." } takes the site offline on its platform and custom domains. Cached pages may take a few minutes to disappear. It clears publishedVersion without changing head, deleting the built deployment, or rewriting version history. No edit lease or rebuild is required. Unpublish is the fastest recovery when nothing should remain public, including when an agent published without approval. Publish head again whenever the site is ready to return.

What the head check covers

The check goes past compilation. After formatting, typechecking, linting, and bundling, it server-renders every page in the build, including dynamic routes such as /blog/:slug, and reports the pages that threw. Rendering needs page data, so the check synthesizes placeholder content from your CMS schema rather than reading CMS items. Each query a page declares becomes a sample item, or a one-item list, built from that collection’s field definitions, and each route parameter gets a placeholder value. That has two consequences:
  • The result depends only on your source and your CMS schema. Identical source checks identically every time, and a page never fails the gate because of one unusual CMS row.
  • The check catches render-time failures such as browser-only globals like window, invalid hook usage, and libraries that do not render on the server. It does not catch failures that depend on specific content.
Pages that compile but throw while rendering come back in renderFailures, one entry per page:

Deployment statuses

The status response includes whether pages exist, the available pageId and route values, an opaque preview revision, and whether the draft is ready to publish.

What can go wrong

Publish is designed to fail safe: if any step fails, publishedVersion does not move and the live site keeps serving the previous published version.
Publish can rewrite head once: if the framework returns canonical formatting or normalized route literals, the formatted files are committed as a new head version before promotion. The response then includes "message": "Source formatting was applied before publishing." — your published version may be one higher than the head you started from.
Drafting and publishing are free on every plan. A site counts toward your plan when an organization owner enables custom domains for it. Sites on Cactal domains are unlimited and free, and the platform domain keeps serving the published version without a paid plan.

Recovery paths

Both recovery operations require an edit lease, and both preserve history. Versions are append-only, so nothing is ever overwritten.

Restore head to a previous state

POST /v1/websiteSourceCode/head/restore copies an earlier state’s files into a new head version without touching the live site. Pass { "to": "published" } to discard draft work when the live site is the state you trust (404 Website has no published version on a never-published website), or { "to": "38" } for any historical version between 1 and the current headVersion (anything higher returns 400 Version does not exist). Publish afterward if you want it live.

Roll back the published site

POST /v1/websiteSourceCode/published/rollback-version with { "version": 38 } re-publishes a prior version directly: it validates and builds that version, commits it as the new head, and moves publishedVersion to it. This is the fast path when a bad publish is live. It runs the same quality gate and build as a normal publish, so it can fail the same ways.
Rollback changes head too. It commits the rolled-back files as a new head version. Any unpublished draft on top of the bad publish is no longer head after a rollback. Recover the draft afterward with head/restore pointing at the draft’s version number.

Prepare a build

POST /v1/websiteSourceCode/deployment/ensure with { "target": "head" } or { "target": "published" } prepares that version without publishing it. Draft pages do this automatically. If a draft build fails, use the retry action shown on the draft page.

Next steps

Roll back and recover

Step-by-step recovery from bad publishes and failed builds.

Agent-safe publishing

Draft-first workflows and approval checkpoints for agents.