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

# Publish a new version

> Replace a Page's HTML by adding a version. The Page's URL serves it right away.

Add a version to a Page owned by the key's bot. Needs a key with **Pages write** turned on. The Page's main URL serves the new version right away. The previous one stays reachable at its `/v/{n}` address until it is pruned.

## Authorization

<ParamField header="Authorization" type="string" required>
  `Bearer mac_live_…`
</ParamField>

## Path parameters

<ParamField path="page_id" type="string" required>
  The Page's id.
</ParamField>

## Body

<ParamField body="content" type="string" required>
  The full HTML document. Up to your plan's per-version cap.
</ParamField>

<ParamField body="change_note" type="string">
  Up to 500 characters, shown next to the version in the app.
</ParamField>

## Response

<ResponseField name="ok" type="boolean">
  `true` on success.
</ResponseField>

<ResponseField name="version" type="integer">
  The new version number, or the current one when `deduplicated`.
</ResponseField>

<ResponseField name="url" type="string">
  The Page's link.
</ResponseField>

<ResponseField name="version_url" type="string">
  The permanent `/v/{n}` address of this version.
</ResponseField>

<ResponseField name="content_sha256" type="string">
  SHA-256 of the stored content.
</ResponseField>

<ResponseField name="deduplicated" type="boolean">
  `true` when the content was byte-for-byte identical to the current version, so nothing new was stored. A revision loop that gets stuck cannot burn through your version history.
</ResponseField>

<RequestExample>
  ```bash curl theme={null}
  curl -X PUT "https://app.my-aichatbot.com/api/v1/pages/$PAGE_ID/content" \
    -H "Authorization: Bearer $MAC_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "<!doctype html><html><head><title>Sales</title></head><body><h1>Sales, revised</h1></body></html>",
      "change_note": "Sticky header"
    }'
  ```

  ```javascript Node.js theme={null}
  const res = await fetch(
    `https://app.my-aichatbot.com/api/v1/pages/${pageId}/content`,
    {
      method: 'PUT',
      headers: {
        Authorization: `Bearer ${process.env.MAC_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ content: html, change_note: 'Sticky header' })
    }
  )

  const { version, deduplicated } = await res.json()
  ```

  ```python Python theme={null}
  import os, requests

  res = requests.put(
      f"https://app.my-aichatbot.com/api/v1/pages/{page_id}/content",
      headers={"Authorization": f"Bearer {os.environ['MAC_KEY']}"},
      json={"content": html, "change_note": "Sticky header"},
  )

  version = res.json()["version"]
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "ok": true,
    "version": 5,
    "url": "https://app.my-aichatbot.com/pages/view/acme/sales-report",
    "version_url": "https://acme.my-aichatbot-space.com/sales-report/v/5",
    "content_sha256": "c0ffee…",
    "deduplicated": false
  }
  ```

  ```json 413 Too large theme={null}
  {
    "ok": false,
    "error": {
      "code": "CONTENT_TOO_LARGE",
      "message": "Content exceeds your plan's per-version cap (524288 bytes).",
      "limit_bytes": 524288
    }
  }
  ```
</ResponseExample>

## Errors

| Status | Code                     | Meaning                                                                  |
| ------ | ------------------------ | ------------------------------------------------------------------------ |
| `401`  | `INVALID_API_KEY`        | Missing, broken, or revoked.                                             |
| `402`  | `STORAGE_QUOTA_EXCEEDED` | Workspace storage is full.                                               |
| `403`  | `PAGES_WRITE_DISABLED`   | Turn on **Pages write** for this key.                                    |
| `403`  | `FORBIDDEN_CHATBOT`      | The Page belongs to another bot.                                         |
| `403`  | `PAGE_DISABLED`          | The Page was disabled by an admin. Writes are rejected.                  |
| `404`  | `NOT_FOUND`              | No live Page with that id in this workspace.                             |
| `413`  | `CONTENT_TOO_LARGE`      | Over your plan's per-version cap. `limit_bytes` says how much.           |
| `422`  | `VALIDATION_FAILED`      | Bad body.                                                                |
| `429`  | `RATE_LIMITED`           | Over 120 writes per minute for this key. Wait for `retry_after_seconds`. |
| `500`  | `PERSIST_FAILED`         | We could not store the version. Safe to retry.                           |
