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

# The SDK surface

> The map of what the SDK publishes: what you declare, which subpath covers each part, and how to prove it worked. Next.js 15 and 16 on the App Router is the supported path.

This page is the map of the **published surface** — what an Avocado integration
consists of, which package subpath covers each part, and where the detailed
guide for each one lives.

**Deciding who writes the integration, and how?** That is
[bring your site in](/sites). This page assumes the decision is made and you want
the reference. New to the project? Read [core concepts](/concepts) first.

## What you are integrating

Avocado is the operations layer for a site you already built. Your team edits through an **AI chat editor** and an optional visual editor; the edits arrive at your site as [typed content operations](/concepts) — never as a code change. That vocabulary has no verb for "change a file", so the boundary is not a policy you configure. It is the shape of the only thing that can cross.

Integrating means declaring where that boundary sits:

```mermaid theme={null}
flowchart LR
    A["<b>Your components</b><br/>registered with a schema"] --> B["<b>Block manifest</b><br/>what the AI may edit"]
    C["<b>Your content</b><br/>CMS, files, database"] --> D["<b>PageDoc + BlockInstance</b><br/>what a page is"]
    B --> E["<b>Editor API</b><br/>/api/editor/*"]
    D --> E
    E --> F["<b>Chat + visual editing</b><br/>on your real pages"]
    F --> G["<b>Publish</b><br/>back into your content store"]
```

Five things have to be true when you are done: your components are declared, your content maps to `PageDoc`, the editor API answers, your renderers are marked up so a click on the page resolves to a field, and publishing writes back where you want it.

<Note>
  **Next.js on the App Router is the supported path — 15 and 16 both.** Every SDK helper that ships with adapters in the box is Next.js, as is every example app but one (`examples/sample-site`, `examples/contentful-site`, `examples/contentful-marketing-site`, `examples/sanity-site`, `examples/strapi-site`). If your site is Next.js, follow the [Next.js integration](/integration/nextjs-integration) walkthrough.

  **Astro has its own package.** [`@avocadostudio-ai/astro`](/integration/astro-integration) mounts all five routes, injects the preview bridge and handles Astro's prerendering problem for you — your `.astro` components keep rendering, and nothing on the `/core` path below applies. `examples/astro-site` is the one non-Next.js example, and two gates drive it on every push.

  Other frameworks (Remix, SvelteKit, Nuxt, Hono, Cloudflare Workers) **can** be integrated with the SDK's framework-agnostic `/core` primitives, but you will be a first mover — see [other frameworks](#other-frameworks).
</Note>

## Which Next.js version

The SDK's peer range is `next: >=15.0.0`, so both install, and the integration is the same on both except for one file:

|                 | Next.js 15                                      | Next.js 16                                                                                     |
| --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Editor rewrite  | `src/middleware.ts`, `createEditorMiddleware()` | `src/proxy.ts`, `createEditorProxy()`                                                          |
| `config` export | may be produced by a function call              | **must** be a static object literal — Next 16 reads it at build time and never calls a factory |

One caveat worth stating plainly: the example apps are on 15, so that is the version continuously exercised. The Next 16 path is shipped and documented rather than continuously tested, and it is the path a site created in the last few months will be on.

If your site already has a `middleware.ts` or `proxy.ts`, note that Next allows exactly one per project. Compose yours with the SDK's by hand — the helper assumes the file is new.

## The supported path

<CardGroup cols={1}>
  <Card title="Next.js integration" icon="react" href="/integration/nextjs-integration">
    The canonical walkthrough. Two SDK helpers — `createEditorApiHandler` mounts the catch-all editor route, `createSitePage` is a drop-in `app/[[...slug]]/page.tsx` — plus `npx avocado-register`, five verification `curl` checks, and troubleshooting.
  </Card>
</CardGroup>

### How it works in two helpers

| Helper                   | What it gives you                                                                                                                                                             | Mount at                            |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `createEditorApiHandler` | One catch-all route serving `blocks`, `pages`, `draft`, `draft/disable` and `publish`                                                                                         | `app/api/editor/[...path]/route.ts` |
| `createSitePage`         | A `Page` component plus `generateStaticParams` and `generateMetadata`, with draft mode, navigation, footer, editor overlay, per-page metadata and 404 fallbacks already wired | `app/[[...slug]]/page.tsx`          |

You wire both to your existing content fetchers (`getPage`, `getSlugs`, `getSiteConfig`), then register the site with `npx avocado-register`. That is the skeleton. The rest of this page is the surface around it.

## The integration surface

Everything below is published from `@avocadostudio-ai/site-sdk` at a subpath of its own. You will not need all of it; this is the map of what exists so you know what you are choosing between.

### Your components are the blocks

On an existing site, **the blocks are your own components**, registered with a schema that says which of their props are content. That registration is the boundary: a prop you declare is editable, a prop you do not declare is not, and no amount of asking will change that.

* [Custom blocks](/integration/custom-blocks) — `registerBlock`, custom renderers, and how your components reach the manifest.
* [Field table](/integration/field-table) — one declaration per block that derives the Zod schema, the property panel, the CMS projection and the merge back. Ships as `@avocadostudio-ai/site-sdk/lens`, with [lens packs](/integration/field-table) for Storyblok (`/lens/storyblok`) and Sanity (`/lens/sanity`) that encode each CMS's own field conventions.
* [Built-in blocks](/integration/built-in-blocks) — the 20 built-in types (Hero, CTA, FAQAccordion, Testimonials, Gallery, Stats, Carousel, Table and the rest). A starting catalogue for sites built from scratch, not the main event on a site that already exists.
* [Block system](/integration/block-system) — how manifests, field metadata and validation fit together.

### Marking up your renderers

The editor resolves a click on a rendered page back to a block and a field. That mapping comes from `data-` attributes your renderer emits, and `@avocadostudio-ai/site-sdk/markers` writes them for you:

| Helper                                                   | What it marks                                                                                                                                                                                                       |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `editableProps(path)`                                    | one field, by the path the block manifest names it at — the unit the property panel and inline editing both address. Takes an optional `label` for the hover pill and a `kind` from the manifest's field vocabulary |
| `editableScopeProps(scope)`                              | a repeated item, so `items[2]`'s fields resolve inside it. Pass `{ display: "contents" }` when the wrapper exists only to carry the scope and must not become a box                                                 |
| `getPreviewWrapperProps(editorMode, blockId, blockType)` | the block-level wrapper the selection overlay highlights                                                                                                                                                            |

Only `getPreviewWrapperProps` is required. The other two mark individual
fields, which is what inline text editing, the hover pills and the image buttons
are found by — an optional pass over your own components, worth doing once the
integration runs: [Make the page directly editable](/integration/inline-editing).

Two rules save a lot of debugging. The path in a marker must match the `editablePath` the field table declares — decoupled sources drift silently, and the symptom is an overlay that selects nothing. And a rich-text field rendered as React children must not be mutated in place by the live-draft overlay; the markers handle that distinction for you.

### Keeping the preview clean

`isEditorRender()` from `@avocadostudio-ai/site-sdk/draft` answers *"is this render inside the editor?"* from a **layout**, where `searchParams` is not available. Gate your consent banner, analytics and tag manager on it:

```tsx theme={null}
export default async function RootLayout({ children }) {
  const inEditor = await isEditorRender()
  return (
    <html>
      <body>
        {children}
        {!inEditor && <CookieConsent />}
        {!inEditor && <Analytics />}
      </body>
    </html>
  )
}
```

Without it, one measured integration sent a `page_view` into the site's own analytics for every block an editor clicked through, and put a cookie banner on top of the page being edited.

### Proving it worked

<Card title="Coverage checks" icon="clipboard-check" href="/integration/coverage">
  `editableCoverage` and `panelCoverage`, from `@avocadostudio-ai/site-sdk/coverage`, grade the integration: which declared fields the rendered page actually marks, and whether the property panel is usable for each block. The number is what tells you the integration is finished, rather than a spot check on the one page you happened to open.
</Card>

### Content, publishing and multilingual

* [CMS adapters](/integration/cms-adapters) — the `CmsAdapter` contract plus `jsonFileAdapter` and `editorApiAdapter`. Working examples for JSON files, Contentful, Sanity and Strapi.
* [Publishing](/integration/publishing) — `@avocadostudio-ai/site-sdk/publish` diffs the payload field by field against a restart-surviving baseline, so a target that writes diffs writes nothing for pages nobody touched.
* [Multilingual](/integration/multilingual) — one editable page per (document × language) for a CMS that localises per field, and the two traps that silently corrupt a dataset when the projection is wrong.

### The rest of the published surface

| Subpath                             | What it is                                                                                                                                                       |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/routes`, `/routes/core`           | the editor API handler, and the framework-agnostic primitives behind it                                                                                          |
| `/page`, `/seo`                     | `createSitePage` and the page-metadata derivation it uses                                                                                                        |
| `/draft`, `/draft/core`             | draft-mode context resolution, `isEditorRender`                                                                                                                  |
| `/editor`, `/editor-manifest`       | the overlay, block rendering in editor mode, manifest building                                                                                                   |
| `/middleware`, `/proxy`, `/matcher` | the Next 15 middleware, the Next 16 proxy, and the path matcher both use                                                                                         |
| `/blocks`                           | `registerBlock`, the block-meta types, and the SDK's own copy of `z` — import Zod from here so your schemas are built by the same copy the SDK validates against |
| `/navigation`                       | nav item and site-header building from your site config                                                                                                          |
| `/publish-handlers/json-file`       | a ready-made publish target that writes a JSON file — `PageDoc[]` by default, or `{ pages, siteConfig }` with `shape: "wrapper"`                                 |
| `/server`                           | `createOrchestrator` for library mode                                                                                                                            |
| `/next-config`                      | `withAvocado`, which wraps your Next config                                                                                                                      |

## Library mode: the orchestrator inside your site

If you would rather run the orchestrator **inside** your Next.js app than as a standalone service, mount `createOrchestrator({ adapter })` from `@avocadostudio-ai/site-sdk/server` as a catch-all route and point it at a [`CmsAdapter`](/integration/cms-adapters) that knows how to read your content. Two bundled adapters cover most cases:

* `jsonFileAdapter` — content checked into git as a JSON file
* `editorApiAdapter` — content fetched from your site's own `/api/editor/pages` endpoint

Library mode needs one extra package: `@avocadostudio-ai/orchestrator-core` is an **optional** peer dependency of the SDK, so `pnpm add @avocadostudio-ai/site-sdk` alone does not pull it in — which keeps it out of installs that only render blocks and talk to a standalone orchestrator. Add it explicitly:

```bash theme={null}
pnpm add @avocadostudio-ai/site-sdk @avocadostudio-ai/orchestrator-core
```

It publishes exactly two entry points, and everything you need is behind them:

| Entry point                               | What it gives you                                                                                                    |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `@avocadostudio-ai/orchestrator-core`     | `createOrchestrator` and its config and auth types, plus everything under `/cms`                                     |
| `@avocadostudio-ai/orchestrator-core/cms` | the [`CmsAdapter`](/integration/cms-adapters) contract, `jsonFileAdapter`, `editorApiAdapter`, `resolveCapabilities` |

Anything deeper — a path into `state/`, `http/`, `ops/` — is internal and will not resolve. If you find yourself needing one, that is a gap in the contract: get in touch rather than routing around it.

It carries native dependencies (`better-sqlite3`, `sharp`). A bundler must be told to leave all of them alone — otherwise the native binary is bundled and crashes when it loads, and Turbopack fails the build over an optional peer you deliberately never installed. Wrapping your config applies both halves:

```ts theme={null}
// next.config.ts
import { withAvocado } from "@avocadostudio-ai/site-sdk/next-config"

export default withAvocado({ /* your config */ })
```

`serverExternalPackages` on its own is not enough here: `transpilePackages`, which library mode also needs, overrides it for a transitive dependency. See [server externals](/integration/nextjs-integration#server-externals).

<Warning>
  **Library mode is credentialed by default in production.** `createOrchestrator()` gates every route. With no `auth` hook and neither `ACCESS_PASSWORD_HASH` nor `ORCHESTRATOR_ACCESS_TOKEN` set, it refuses every request under `NODE_ENV=production` — an unauthenticated publish endpoint on your own domain is not a default anyone should reach by forgetting something. To run it open on purpose, say so: `auth: () => true`.
</Warning>

<Warning>
  **Linking the packages instead of installing them? Install the two vendor SDKs yourself.**

  ```bash theme={null}
  pnpm add openai @anthropic-ai/sdk
  ```

  `openai` and `@anthropic-ai/sdk` are ordinary `dependencies` of `@avocadostudio-ai/orchestrator-core`, so a normal registry install already brings them and you can skip this. It matters when you point at a local checkout with `file:` or `link:`: your package manager symlinks the package **without** installing its dependency tree into your project, and Next externalises bare `node_modules` packages on the server — so the emitted `require("openai")` runs from your `.next/` and looks in *your* `node_modules`, never the link target's.

  Both are loaded at module scope, so a missing one is not a degraded feature. The route file fails to load and **every** endpoint answers 500, including ones that never touch a model:

  ```
  ⨯ Error: Cannot find module 'openai'
    page: /api/avocado/draft/slugs
  ```

  This applies even if you only plan with one vendor — the handler is one module graph and it loads both. `googleapis` and `@google/genai` are different: they are genuinely optional peers, loaded through `await import(...)`, and a mount runs fine without either. You do need `@google/genai` if you plan with Gemini or generate images with it.
</Warning>

Library mode is the lightest integration when your content is already `PageDoc`-shaped, and it sidesteps the standalone-orchestrator deployment entirely. See [CMS adapters](/integration/cms-adapters) for the full pattern.

## Integration model (high level)

Whichever framework you are on, the contract is the same:

* **Source of truth** — an adopter-owned component registry in code: your own components, exposed through the SDK's manifest.
* **Transport contract** — `GET /api/editor/blocks` returns the block manifest as JSON, generated by the SDK from your registry.
* **Preview bootstrap** — Draft Mode cookies, set by `GET /api/editor/draft?secret=…&redirect=…` and cleared by `GET /api/editor/draft/disable?redirect=…`.
* **Page render** — in editor mode your page handler reads draft content from the orchestrator instead of your CMS, and mounts the editor overlay.
* **Publish** — optionally, `POST /api/editor/publish` accepts the edited content back so you can write it to your CMS, file system or database.

### Required endpoints

`createEditorApiHandler` mounts all five at once under a single catch-all route on Next.js — see the [walkthrough](/integration/nextjs-integration#walkthrough) for the one-file setup. On other frameworks you mount them yourself with the `/core` primitives.

| Method | Path                                    | Purpose                                                                                                                                                                                       |
| ------ | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/api/editor/blocks`                    | Block manifest for structural edits                                                                                                                                                           |
| `GET`  | `/api/editor/pages`                     | `{ pages: PageDoc[] }` of published content for editor session bootstrap                                                                                                                      |
| `GET`  | `/api/editor/draft?secret=…&redirect=…` | Enable Draft Mode — validates the secret, allows internal redirects only                                                                                                                      |
| `GET`  | `/api/editor/draft/disable?redirect=…`  | Disable Draft Mode                                                                                                                                                                            |
| `POST` | `/api/editor/publish`                   | Receives published pages back from the editor — only mounted when you pass `onPublish`. Needs `publishSecret` under `NODE_ENV=production`, and refuses a publish that would remove every page |

### Required environment variables

| Where             | Variable                 | Purpose                                                                                                                                                                                                                                            |
| ----------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Site `.env.local` | `DRAFT_MODE_SECRET`      | Validates `?secret=` on draft entry and on the preview render itself. Generated by `avocado-register` if missing.                                                                                                                                  |
| Site `.env.local` | `ORCHESTRATOR_URL`       | Where the SDK fetches draft pages from. In library mode, your own mount — for example `http://localhost:3000/api/avocado`.                                                                                                                         |
| Site `.env.local` | `PUBLISH_TOKEN`          | The publish secret, passed to `createEditorApiHandler` as `publishSecret`. Required once the site runs under `NODE_ENV=production`: without it `POST /api/editor/publish` answers 401. The orchestrator sends the same value as `x-publish-token`. |
| Editor            | `VITE_SITE_ORIGIN`       | Where the editor's iframe loads from                                                                                                                                                                                                               |
| Editor            | `VITE_SITE_DRAFT_SECRET` | **Must equal** `DRAFT_MODE_SECRET`. The editor puts it on every site URL it builds.                                                                                                                                                                |

A mismatch between the editor's `VITE_SITE_DRAFT_SECRET` and the site's `DRAFT_MODE_SECRET` is the single most common failure — `avocado-register` surfaces it as a warning. It degrades to *"the preview shows published content"* rather than an error, which is why it goes unnoticed.

### Adoption checklist

For a Next.js App Router project:

1. `pnpm add @avocadostudio-ai/site-sdk` (plus `@avocadostudio-ai/orchestrator-core` for library mode)
2. Wrap `next.config.ts` with `withAvocado`
3. Create `app/api/editor/[...path]/route.ts` with `createEditorApiHandler`
4. Replace `app/[[...slug]]/page.tsx` with `createSitePage`
5. Declare your own components as blocks, and mark up their renderers
6. Run `npx avocado-register --name "My Site" --orchestrator http://localhost:3000/api/avocado` from the project directory (the flag matters — the default is the standalone server on `:4200`)
7. Run the five verification `curl` checks, then [coverage](/integration/coverage) until it reports what you expect

Steps 1–4 are an afternoon. Step 5 is the integration.

For non-Next.js frameworks the conceptual steps are identical, but you implement the route handlers and the page-render branching by hand — see [non-Next.js integration](/integration/non-nextjs).

## Other frameworks

<Warning>
  **First-mover territory — unless you are on Astro.** Two frameworks have adapters in the box: Next.js, in `@avocadostudio-ai/site-sdk/draft` and `/routes`, and Astro, in [`@avocadostudio-ai/astro`](/integration/astro-integration). Everywhere else the SDK's `/core` primitives are framework-agnostic by design and the patterns work, but you are writing the adapter, and there is no support commitment on that path today.
</Warning>

**On Astro, stop here and read the [Astro integration](/integration/astro-integration).** It is an install and one config block; none of the three options below apply.

If your site is on neither, you have three honest options:

<CardGroup cols={3}>
  <Card title="Wrap in a Next.js shell" icon="layer-group">
    Stand up a thin Next.js project that proxies to your existing backend, and use the standard [Next.js integration](/integration/nextjs-integration). Fully supported, and where most people who try the non-Next.js path end up.
  </Card>

  <Card title="DIY with /core primitives" icon="screwdriver-wrench" href="/integration/non-nextjs">
    Implement the editor API contract by hand with `createDraftEnableHandlerCore`, `createBlocksHandler`, `resolveDraftContextCore` and friends. Worked examples for Hono and SvelteKit.
  </Card>

  <Card title="Hand it to your coding agent" icon="terminal" href="/sites/coding-agent">
    Give the integration to Claude Code, Codex or Cursor. The guidance assumes Next.js; an agent that knows your framework can usually adapt it, and results vary.
  </Card>
</CardGroup>

The Next.js adapter is small — `packages/site-sdk/src/draft-routes.ts` is about 30 lines over the framework-agnostic core — so a working adapter for another framework is a bounded piece of work rather than a rewrite. If you want your framework supported first class, or you have built an adapter you would like folded in, get in touch.

## Related

* [Coverage checks](/integration/coverage) — prove the integration is complete
* [Visual editor](/integration/puck-mode) — opt-in drag-and-drop direct manipulation on the same blocks, same publishing pipeline, same orchestrator
* [Environment reference](/reference/environment) — every variable, grouped by what it configures
* [Native tools](/integration/tools-mvp) — the tool contract for connecting a PIM, a DAM, Unsplash or AI image generation to the planner
* [MCP server](/integration/mcp-server) — 49 tools over Model Context Protocol, for any MCP host
