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

# Agent-safe publishing

> The operational safety model for agent-run website work: website-scoped API keys, draft-first editing, checks before publish, human approval on preview URLs, audit log review, unpublishing, and rollback.

Agents do their best work when the blast radius of a mistake is bounded and recovery is one call away. Cactal gives you six controls for that: scope the key, keep work on drafts, check before publish, put a human at the publish gate, watch the audit log, and roll back when needed.

The built-in [Cactal agent](/docs/agents/cactal-agent) follows this model automatically: it edits drafts, validates with the framework check, and pauses for an in-conversation approval before publishing or any other live change, with its actions attributed in the activity feed. The controls below apply the same discipline to agents you bring yourself.

## Scope the key

Give each agent its own API key, scoped to exactly the websites and capabilities its job requires. Separate keys keep the audit log attributable and let you revoke one agent without breaking another.

| Agent                                           | Access scope                             | Role                                                    |
| ----------------------------------------------- | ---------------------------------------- | ------------------------------------------------------- |
| Content agent (CMS items, assets)               | `websites` with the specific website ids | `content_editor`                                        |
| Site builder (source code, publishing, domains) | `websites` with the specific website ids | `full_editor`                                           |
| Organization-wide automation                    | `organization`                           | `admin`, only when it genuinely manages members or keys |

A `content_editor` key cannot edit source code, publish the website, or change settings — the MCP server does not even show it those tools. The `owner` role is not grantable to keys. Rescope a key at any time by sending `access` to `PATCH /v1/apiKeys/{apiKeyId}`; the change applies immediately. See [Access control](/docs/concepts/access-control) and [Manage API keys](/docs/guides/manage-api-keys).

## Draft-first editing

All source edits land on the website's head — the draft version. Mutations require an edit lease from `POST /v1/websiteSourceCode/lease/acquire`, and nothing an agent writes reaches the live website until an explicit `POST /v1/websiteSourceCode/head/publish`.

Review the draft at any time on its preview host:

```text theme={null}
https://draft--<label>.cactal.app
```

Preview hosts are served with `X-Robots-Tag: noindex, nofollow`, so drafts stay out of search engines. CMS items have their own draft state: an agent can prepare items and leave publishing to you. See [Preview drafts and versions](/docs/guides/preview-drafts-and-versions).

## Check before publish

`POST /v1/websiteSourceCode/head/check` runs the framework quality gate against the draft without publishing anything. Make a passing check a hard precondition in your agent's instructions.

The check answers "does it render," not only "does it compile." It server-renders every page using placeholder content synthesized from the CMS schema, and returns each page that threw in `renderFailures` with its `pageId`, `route`, and error. Treat that array as the repair list. See [What the head check covers](/docs/concepts/publishing#what-the-head-check-covers).

Publishing runs a build in an isolated sandbox and creates a deployment that moves through `building`, then `ready` or `failed`. Only a deployment that reaches `ready` serves traffic — but a failed publish still costs time, so check first.

<Warning>
  An agent that publishes without checking can ship a broken draft build attempt and stall its own workflow. Treat `head/check` as mandatory, not advisory.
</Warning>

## Put a human at the publish gate

The strongest pattern separates preparation from publication.

<Steps>
  <Step title="Agent prepares the draft">
    The agent edits source, content, and assets on head, running `POST /v1/websiteSourceCode/head/check` after each batch of changes.
  </Step>

  <Step title="Agent reports the preview URL">
    When the check passes, the agent posts `https://draft--<label>.cactal.app` and a summary of what changed.
  </Step>

  <Step title="Human reviews the draft">
    Open the preview URL, list files changed from the live version with `GET /v1/websiteSourceCode/changes`, and inspect selected files with `GET /v1/websiteSourceCode/files/diff`.
  </Step>

  <Step title="Publish after approval">
    Either you publish from the dashboard, or you tell the agent to call `websiteSourceCode_head_publish`.
  </Step>
</Steps>

You can enforce this two ways:

* **Hard enforcement** — give the agent a `content_editor` key. It cannot publish at all; a human (or a separate, human-triggered process) owns the publish step.
* **Policy enforcement** — give a `full_editor` key plus a standing instruction. Useful when the same agent must build and ship:

```markdown cactal-publish-policy.md theme={null}
- Work only on the draft head. Never touch published versions directly.
- Run the head check after every batch of edits.
- Before publishing: post the draft preview URL and wait for explicit approval.
- Never publish without a passing check and an approval in this session.
- If a publish fails or looks wrong, stop and report — do not retry in a loop.
```

## Watch the audit log

Lifecycle and access-control operations write organization audit events with actor attribution: website create/rename/delete/restore/transfer, editor grants and invitations, API keys, domains, organization membership, and billing. Agent actions carry `actorKind: "api_key"`, so reviewing that activity is one filtered request:

```bash theme={null}
curl "https://api.cactal.ai/v1/organizations/{organizationId}/audit-log?actorKind=api_key&limit=50" \
  -H "Authorization: Bearer $CACTAL_API_KEY"
```

Each event records the event type, the resource, a human-readable summary, and which API key acted. Narrow further with `eventType`, `resourceType`, `actorPrincipalId`, or `websiteId` (which matches the website itself and resources belonging to it), and page with `cursor` — see [Pagination](/docs/api-reference/pagination). Reading the audit log requires an `admin`-role key or the dashboard.

<Note>
  Source file edits, publishes, CMS content, and asset changes are not audit-logged. Review an agent's content work where it actually lives: `GET /v1/websiteSourceCode/changes` for the pending file list, `GET /v1/websiteSourceCode/files/diff` for selected source changes, and the `draft--` preview for the rendered result.
</Note>

<Tip>
  Review the audit log after an agent's first unattended runs, and periodically afterwards. Attribution is only useful if someone reads it.
</Tip>

## Recover

Every layer of agent work has an undo path. None of these require the agent that caused the problem.

| Situation                                          | Action                                          | Endpoint                                                               |
| -------------------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------- |
| The draft went wrong                               | Restore head to match the published version     | `POST /v1/websiteSourceCode/head/restore` with `{ "to": "published" }` |
| An earlier draft state was better                  | Restore a previous version onto head            | `POST /v1/websiteSourceCode/head/restore` with `{ "to": "<version>" }` |
| Nothing should remain live                         | Unpublish without changing the draft or history | `POST /v1/websiteSourceCode/published/unpublish`                       |
| A previous version should replace the live version | Roll the published website back                 | `POST /v1/websiteSourceCode/published/rollback-version`                |

Unpublish needs no edit lease and takes every site hostname offline. Cached pages may take a few minutes to disappear. Rollback changes what serves immediately and moves head to a new copy of the selected historical version. See [Roll back and recover](/docs/guides/roll-back-and-recover) for the full procedures, including failed-build recovery.

## If a key is compromised

Act on the key first, then investigate.

1. **Rotate** with `POST /v1/apiKeys/{apiKeyId}/rotate` — the old secret stops working immediately and you receive a new one once.
2. **Revoke** with `POST /v1/apiKeys/{apiKeyId}/revoke` if the agent should lose access entirely. Revocation is permanent.
3. **Audit** what the key did: filter the audit log by `actorKind=api_key` and the key's principal, then roll back anything unwanted.

<Check>
  A safe agent setup looks like this: one key per agent, website-scoped, least role; all work on head; check before publish; a human approval step; periodic audit review; and a tested rollback path.
</Check>
