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

# Pages

> Read, patch, and publish the JSON page documents that make up your workspace.

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const APIBadge = ({method = "GET", path, scope}) => {
  const m = method.toUpperCase();
  return <span className="gv-api-badge">
      <span className={`gv-api-badge__method gv-api-badge__method--${m.toLowerCase()}`}>
        {m}
      </span>
      <code className="gv-api-badge__path">{path}</code>
      {scope && <span className="gv-api-badge__scope">
          <span className="gv-api-badge__scope-label">scope</span>
          <code>{scope}</code>
        </span>}
    </span>;
};

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="RESOURCE" />

A page is a JSON document — a versioned tree of PageBlocks plus data bindings and capability hooks. Every Builder save creates a new version. Through this resource you fetch the current draft, apply surgical edits as RFC 6902 JSON Patch, roll back to a prior version, and publish the draft live. By the end of this page you will know which endpoints exist, what each scope unlocks, and how draft and live state relate.

## A page in shape

```json theme={null}
{
  "version":  "v0.4",
  "metadata": { "slug": "landing", "title": "Acme Co." },
  "schema":   {},
  "tree": {
    "block": "NavShell",
    "props": { "brand_name": "Acme Co.", "variant": "topbar" },
    "children": [
      {
        "block": "Hero",
        "props": { "headline": "Welcome to Acme", "cta_label": "Sign up", "cta_href": "/signup" }
      }
    ]
  }
}
```

The root block is always a `NavShell`; its children are the visible sections. The full block catalogue lives in [PageBlocks](/pageblocks/overview).

## Endpoints

<APIBadge method="GET" path="/v1/workspaces/{slug}/pages" scope="pages:read" />

List all pages in the workspace.

<APIBadge method="GET" path="/v1/workspaces/{slug}/pages/{id}" scope="pages:read" />

Fetch the current draft page document.

<APIBadge method="PATCH" path="/v1/workspaces/{slug}/pages/{id}" scope="pages:write" />

Apply RFC 6902 JSON Patch operations to the draft.

<APIBadge method="POST" path="/v1/workspaces/{slug}/pages/{id}" scope="pages:destructive" />

Roll back the draft to a prior version.

<APIBadge method="POST" path="/v1/workspaces/{slug}/pages-publish" scope="pages:write" />

Publish the current draft to live.

## Draft vs live

| State | What it is                                                                         | How it changes                                                                                                       |
| ----- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| Draft | The working copy you read with `GET /pages/{id}`. Patches and rollbacks act on it. | Every `PATCH` produces a new draft version.                                                                          |
| Live  | The version your visitors see.                                                     | Updated only by `POST /pages-publish`. The publish endpoint promotes every page's current draft to live in one call. |

The decoupling means you can iterate on the draft as long as you like — visitors keep seeing the last published version until you publish again.

## Patching the tree

`PATCH /pages/{id}` accepts an [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) array. Each operation is `{ op, path, value? }`, where `path` is a [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) into the page document. The pointer `/tree/children/0/props/headline` targets the `headline` prop of the first child block.

```bash theme={null}
curl -X PATCH https://api.gavai.app/v1/workspaces/acme/pages/page_01H... \
  -H "Authorization: Bearer gak_live_xxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '[
    { "op": "replace", "path": "/tree/children/0/props/headline", "value": "Welcome to Acme" }
  ]'
```

Fetch the document first to compute the right paths. We chose JSON Patch over a custom diff format because patches are standard, debuggable, and hand-writable in a console without an SDK.

## Common errors

<ErrorTable
  errors={[
{ code: "400 invalid_patch", cause: "One or more operations failed — path not found, `test` op mismatched, etc.", fix: "Fetch the current draft, recompute paths, and resend the patch." },
{ code: "403 insufficient_scope", cause: "Token lacks the required scope for the operation.", fix: "Mint a token with `pages:read`, `pages:write`, or `pages:destructive` as appropriate." },
{ code: "404 not_found", cause: "Page or workspace not found, or not visible to this token.", fix: "Verify the slug and page id; confirm the token can see the workspace." },
{ code: "409 conflict", cause: "Concurrent edit — another writer beat you to the draft.", fix: "Fetch the latest draft and reapply your patch on top of it." },
]}
/>

## Related

<Card title="PageBlocks overview" icon="layout" href="/pageblocks/overview">
  The block catalogue, prop shapes, and how to compose a page tree.
</Card>
