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

# Roll back and recover

> Learn how to recover a Cactal website: discard the draft head, restore an older source version to head, roll the published website back immediately, and recover from failed builds.

Source history is append-only, so every recovery creates a new version — you can always undo an undo.

## Prerequisites

* An API key with `full_editor` access — see [Create an API key](/docs/create-an-api-key)
* A website with version history — see [Edit source code](/docs/guides/edit-source-code)

## The two recovery tools

| Endpoint                                                | What it changes                                                                                | Use when                                                                                           |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `POST /v1/websiteSourceCode/head/restore`               | Head only; copies the files of `to` (`published` or a version number) into a new head version. | The draft went wrong, or you want an older version back with a chance to review before publishing. |
| `POST /v1/websiteSourceCode/published/rollback-version` | Head and published — validates, builds, and re-publishes version `n` immediately.              | Production is broken and must change now.                                                          |

Both require an edit lease token.

<Steps>
  <Step title="Acquire an edit lease">
    ```bash Acquire a lease theme={null}
    curl -X POST 'https://api.cactal.ai/v1/websiteSourceCode/lease/acquire' \
      -H "Authorization: Bearer $CACTAL_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{ "websiteId": "V1StGXR8_Z5jdHi6B-myT" }'
    ```

    The response `{ "leaseToken": "...", "headVersion": 9 }` also tells you the current head version. `GET /v1/websiteSourceCode/summary` reports `headVersion`, `publishedVersion`, and `hasUnpublishedChanges` if you need orientation first.
  </Step>

  <Step title="Discard the draft">
    Restore the head to match the published website. Production does not change.

    ```bash Restore head to published 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": "published",
        "leaseToken": "exampleLeaseToken00000000000000000000000000"
      }'
    ```

    The response is `{ "version": 10, "changed": true }`, a new head version containing the published files. If head already matched, `changed` is `false`. A website that has never published returns `404`.
  </Step>

  <Step title="Restore an older version to head">
    Bring back a known-good version for review by passing a version number as `to`. This changes only the draft:

    ```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"
      }'
    ```

    `to` must be an existing version (1 up to the current head); anything higher returns `400 Version does not exist`. Review the result on the draft host, then take it live with the normal pipeline: `POST /v1/websiteSourceCode/head/check`, then `POST /v1/websiteSourceCode/head/publish`.
  </Step>

  <Step title="Roll back the published website immediately">
    When production is broken, skip the review loop. Rollback validates version `n`, builds it, and re-publishes in one call:

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

    ```json Response theme={null}
    {
      "websiteId": "V1StGXR8_Z5jdHi6B-myT",
      "publishedVersion": 11,
      "deployment": { "id": "dep_9f2k", "status": "ready" }
    }
    ```

    Rollback re-runs the full publish gate: the version must pass validation and build. It does not require billing. It also moves head to the rolled-back content, so head and published end up aligned.
  </Step>

  <Step title="Recover from a failed build">
    A publish that fails validation or build returns a `400` with the failure message, and the deployment records `status: "failed"`. Inspect it:

    ```bash Check deployment status theme={null}
    curl -X POST 'https://api.cactal.ai/v1/websiteSourceCode/deployment/status' \
      -H "Authorization: Bearer $CACTAL_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{ "websiteId": "V1StGXR8_Z5jdHi6B-myT", "target": "head" }'
    ```

    ```json Response (truncated) theme={null}
    {
      "websiteId": "V1StGXR8_Z5jdHi6B-myT",
      "sourceVersion": 11,
      "deployment": { "id": "dep_9f2k", "status": "failed", "failureMessage": "Page \"pricing\" failed to compile", "failureKind": "build" }
    }
    ```

    The recovery loop: fix the offending source file, run `POST /v1/websiteSourceCode/head/check` until it passes, then publish again. `POST /v1/websiteSourceCode/deployment/ensure` (body `{ websiteId, target }`) re-triggers a build for the head or published version without publishing — useful to confirm a fix builds cleanly. The published website keeps serving its last ready deployment throughout; a failed build never takes production down.
  </Step>

  <Step title="Verify the outcome">
    List files changed from published to head to confirm the state you expect:

    ```bash List changed files theme={null}
    curl 'https://api.cactal.ai/v1/websiteSourceCode/changes?websiteId=V1StGXR8_Z5jdHi6B-myT' \
      -H "Authorization: Bearer $CACTAL_API_KEY"
    ```

    <Check>
      After a rollback or a restore to `published` you should see `"items": []`, meaning head and published match. After restoring an older version to head, `items` lists what will change when you publish. Inspect a selected entry with `GET /v1/websiteSourceCode/files/diff?websiteId=...&name=...`.
    </Check>
  </Step>
</Steps>

## Troubleshooting

<AccordionGroup>
  <Accordion title="409 Invalid edit lease">
    Another editor acquired the lease after you did. Re-acquire the lease and repeat the operation.
  </Accordion>

  <Accordion title="409 Website deployment is still building; retry shortly">
    Rollback found a build in progress for that version. Retry after a short wait; the build is reused once ready.
  </Accordion>

  <Accordion title="404 Website has no published version">
    Restoring to `published` and comparing against `published` need at least one publish. Restore to a version number instead on a never-published website.
  </Accordion>
</AccordionGroup>

## Next steps

* [Preview drafts and versions](/docs/guides/preview-drafts-and-versions) to review a restored head before publishing
* [Agent-safe publishing](/docs/agents/agent-safe-publishing) to make rollback a planned part of agent workflows
