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

# Preview drafts and versions

> Learn how to preview a Cactal website's draft head on its draft-- host, share previews with clients, review older versions, and understand how preview hosts relate to publishing.

Every website gets preview hosts on its platform subdomain, so you can review the head version before anything reaches production.

## Prerequisites

* An API key with any access role — see [Create an API key](/docs/create-an-api-key)
* A website with a draft change — see [Edit source code](/docs/guides/edit-source-code)

## Preview hosts

Each website has a readable platform subdomain label, for example `ember-oak` or `ember-oak-k7m4p2` when a uniqueness suffix is needed. Three host shapes hang off it:

| Host                        | Serves                                                                                                   | Indexable                                          |
| --------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `<label>.cactal.app`        | The published version.                                                                                   | Yes (unless a primary custom domain redirects it). |
| `draft--<label>.cactal.app` | The current head (draft) version.                                                                        | No — `noindex`.                                    |
| `v<n>--<label>.cactal.app`  | Reserved for a specific source version `n`. Currently responds `501 Version previews are not supported`. | No.                                                |

Use the draft host for review; use version restore (below) when you need to see an older version today.

The site studio displays the draft directly in its Preview tab. You can switch device sizes, choose a static page, enter another path, refresh the draft, or open it in a new tab. The loaded preview stays in place when you move between studio tabs, then refreshes when the draft revision changes or you choose Refresh. A separate live-site action appears after the site is published.

Navigation inside the preview keeps the studio address in sync, including query strings and anchors. While the agent edits, both the studio preview and standalone draft links keep the current page visible until the updated preview is loaded, then restore your scroll position. Standalone draft links keep their address bar and browser Back and Forward navigation synchronized with the displayed page. Queued element selections survive these updates. Older browsers without the Navigation API use normal page reloads. A full preview update loads a new document, so unsent form input and other page-local state may reset.

When the current head has no ready deployment, the studio starts the draft build and shows its progress. Once any build has succeeded, the draft keeps serving the last good build while newer builds run or fail; a status pill in the corner reports progress and swaps the new build in automatically. A failed build never affects the live site.

<Steps>
  <Step title="Find your platform subdomain">
    List the website's domains and take the `platform_subdomain` hostname:

    ```bash Find the label theme={null}
    curl 'https://api.cactal.ai/v1/websites/V1StGXR8_Z5jdHi6B-myT/domains' \
      -H "Authorization: Bearer $CACTAL_API_KEY"
    ```

    ```json Response (truncated) theme={null}
    [
      {
        "kind": "platform_subdomain",
    		"hostname": "ember-oak-k7m4p2.cactal.app",
        "status": "active"
      }
    ]
    ```

    The draft host is the same hostname with a `draft--` prefix on the label: `draft--ember-oak-k7m4p2.cactal.app`.
  </Step>

  <Step title="Open the draft preview">
    Open `https://draft--<label>.cactal.app` in a browser. It renders the head version — every saved source change, published or not.

    Draft previews rebuild automatically after each source write. Once a build has succeeded, the host keeps serving the last good build during rebuilds: a corner pill shows "Updating preview" and the page swaps in the new build on its own. If the head fails to build, the last good build keeps serving and the pill exposes the build error. Before the first successful build, the host returns a `202` "Building your preview" page that updates itself. Transient build infrastructure failures retry automatically; source errors need a source fix (the error is available on the page and via `POST /v1/websiteSourceCode/deployment/status`).

    Two behaviors to know:

    * The draft host previews draft source code, not draft content: CMS reads on it return published items only. Review draft items over the API with `published=false`, or in the dashboard.
    * The draft host never redirects to your primary custom domain; only production hosts do.
  </Step>

  <Step title="Share the preview with clients">
    The draft URL is a plain public URL — reviewers need no Cactal account or login. Send `https://draft--<label>.cactal.app/pricing` to put a client on the exact page under review.

    Search engines are excluded twice over: every preview response carries `X-Robots-Tag: noindex, nofollow`, and the preview's `/robots.txt` serves `Disallow: /`.

    <Note>
      Anyone with the URL can view the draft. Do not stage confidential content on a website whose draft URL has been shared.
    </Note>
  </Step>

  <Step title="Preview an older version">
    Version hosts (`v<n>--<label>`) are reserved and not yet served. To review an older version now, restore it to head and use the draft host:

    ```bash Restore version 7 to head theme={null}
    curl -X POST 'https://api.cactal.ai/v1/websiteSourceCode/head/restore' \
      -H "Authorization: Bearer $CACTAL_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "websiteId": "V1StGXR8_Z5jdHi6B-myT",
        "to": "7",
        "leaseToken": "exampleLeaseToken00000000000000000000000000"
      }'
    ```

    This creates a new head version with version 7's files — history is append-only, so nothing is lost. The draft host now shows version 7's content. See [Roll back and recover](/docs/guides/roll-back-and-recover) for the full recovery toolkit, including how to return to where you were.
  </Step>

  <Step title="Verify preview behavior">
    Confirm the draft host is live and marked non-indexable:

    ```bash Inspect preview headers theme={null}
    curl -sI 'https://draft--ember-oak-k7m4p2.cactal.app/' | grep -iE '^(HTTP|x-robots-tag)'
    ```

    <Check>
      You should see `HTTP/2 200` and `x-robots-tag: noindex, nofollow`. The production host (`<label>.cactal.app`) returns no such header.
    </Check>
  </Step>
</Steps>

## Capture a screenshot

Opening the draft host in a browser is the fastest review, but agents and scripts often have no browser. `POST /v1/websitePreview/capture` renders one page in a real browser and returns the image, so layout, spacing, and responsive behavior can be checked without one. Over MCP the image comes back as an image block the model can actually look at.

```bash Capture the pricing page on mobile theme={null}
curl -X POST 'https://api.cactal.ai/v1/websitePreview/capture' \
  -H "Authorization: Bearer $CACTAL_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "websiteId": "V1StGXR8_Z5jdHi6B-myT",
    "path": "/pricing",
    "viewport": "mobile"
  }' --output pricing-mobile.jpg
```

`path` is site-relative and may carry a query string, so dynamic routes are captured with real values rather than placeholders. `target` selects `head` (default) or `published`. `viewport` is `desktop` (1440x900), `tablet` (834x1112), or `mobile` (390x844).

To control framing and timing:

| Option             | Effect                                                                                                                                        |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `colorScheme`      | Emulate the browser's `dark` or `light` color preference.                                                                                     |
| `fullPage`         | Capture the whole scrollable page instead of the viewport. Excludes `selector` and `clip`.                                                    |
| `selector`         | Crop to the first element matching a CSS selector.                                                                                            |
| `padding`          | CSS pixels of surrounding page to include on every side of the `selector` match.                                                              |
| `clip`             | Crop to `{ x, y, width, height }`, in CSS pixels from the top-left of the rendered page at the chosen viewport. Must fit within the viewport. |
| `scrollTo`         | Scroll to a CSS selector or a Y offset before capturing.                                                                                      |
| `waitForSelector`  | Wait for a CSS selector to match before capturing.                                                                                            |
| `delayMs`          | Extra settle time, up to 5000ms.                                                                                                              |
| `freezeAnimations` | Fast-forward CSS animations and transitions to their end state. Defaults to `true`.                                                           |

Fonts, images, and — for `fullPage` — lazily loaded content are awaited before every capture, so the extra waits are only needed for content that arrives on its own schedule.

### Focus on one region

A viewport or full-page capture often shows far more than the question needs. To look at one component, pass `selector` and, when the surrounding context matters, `padding`:

```bash Capture the pricing table with 24px of context on mobile theme={null}
curl -X POST 'https://api.cactal.ai/v1/websitePreview/capture' \
  -H "Authorization: Bearer $CACTAL_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "websiteId": "V1StGXR8_Z5jdHi6B-myT",
    "path": "/pricing",
    "viewport": "mobile",
    "selector": "#plans",
    "padding": 24
  }' --output plans-mobile.jpg
```

Prefer a selector: it is semantic and follows the responsive layout, so the same request frames the element correctly at every viewport. Use `clip` only for a region no element frames, such as part of a canvas or chart or a band spanning several elements. Clip coordinates are CSS pixels from the top-left of the page, measured at the chosen viewport.

A focused region is scrolled into view and captured from the configured viewport, exactly as a visitor would see it, so it must fit within that viewport. A region that is larger than the viewport, a clip that lies outside the page, a selector that matches nothing visible, or an ambiguous combination fails with `400` and a message naming the problem; page through larger regions with `scrollTo` or capture `fullPage` instead. `selector` and `clip` exclude each other and `fullPage`.

Color-scheme captures reserve the `__cactal` query parameter to isolate renderer caches. Cactal removes it before route data and site scripts run, so it never becomes part of the page's application state.

A `head` capture requires a ready draft build. While the build is running the endpoint returns `409` rather than a screenshot of an interim state; poll `POST /v1/websiteSourceCode/deployment/status` with `target: "head"` until the deployment is `ready`, then capture again.

## How previews relate to publishing

The draft host tracks `headVersion`; production hosts track `publishedVersion`. Publishing (`POST /v1/websiteSourceCode/head/publish`) promotes the checked head to published — at that moment the production host changes and the two hosts converge. Until then, edits affect only the draft host. `GET /v1/websiteSourceCode/summary` reports both versions and `hasUnpublishedChanges`.

## Troubleshooting

| Symptom                                                             | Cause                                                   | Fix                                                                                                                       |
| ------------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| "Building your preview" persists                                    | The first draft build is still running.                 | Wait; the page updates itself. Heavy sites take longer to build.                                                          |
| "Updating preview" pill persists                                    | A rebuild of the head is still running.                 | Wait; the pill clears and the page swaps when the build lands.                                                            |
| "Latest change didn't build" pill or "The first build needs a hand" | The head fails to build on a source error.              | Run `POST /v1/websiteSourceCode/head/check` and fix the reported file; the preview recovers on the next successful build. |
| `501` on a `v<n>--` host                                            | Version preview serving is not available yet.           | Restore the version to head and use the draft host.                                                                       |
| `409` from `websitePreview/capture`                                 | The draft build is still running or has failed.         | Poll `POST /v1/websiteSourceCode/deployment/status` with `target: "head"` until it is `ready`.                            |
| Draft shows stale CMS content                                       | The item is unpublished; previews read published items. | Publish the item, or check its `publishedAt`.                                                                             |
| "Website not published" on the production host                      | The website has no published version yet.               | Publish once; the draft host works regardless.                                                                            |

## Next steps

* [Roll back and recover](/docs/guides/roll-back-and-recover) to act on what you find in review
* [Publishing concepts](/docs/concepts/publishing) for the check → publish → serve pipeline
