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

# Bring your site in

> Three ways to get an existing website into Avocado Studio — hand it to your own coding agent, use the built-in onboarding agent, or read the contract and write it yourself.

Avocado Studio manages websites as **sites** — each with its own pages, blocks, theme, and publishing target. Before anyone can edit anything, one site has to exist in the orchestrator and your codebase has to declare what is editable.

That declaration is the integration. It is the only work there is, and it is the reason the rest of the product can be safe: what your repo declares is exactly what an agent can touch, and nothing else is expressible.

## Three paths

<CardGroup cols={3}>
  <Card icon="terminal" href="/sites/coding-agent">
    <span style={{ float: "right", margin: "-36px 0 0", padding: "3px 10px", borderRadius: "999px", background: "#1F7A3A", color: "#ffffff", fontSize: "11px", fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", lineHeight: 1.4, whiteSpace: "nowrap" }}>Recommended</span>
    <span style={{ display: "block", fontSize: "17px", lineHeight: 1.4, margin: "2px 0 10px" }}>**Your coding agent**</span>
    Claude Code, Codex, Cursor — whatever you already run. It knows your codebase, your conventions and your history, and the work lands in your repo through your normal review flow. We give you the prompt and the number that says you are done. **Every production integration has used this path.**
  </Card>

  <Card icon="robot" href="/sites/site-agent">
    <span style={{ float: "right", margin: "-36px 0 0", padding: "3px 10px", borderRadius: "999px", background: "rgba(148,163,184,0.16)", border: "1px solid rgba(148,163,184,0.45)", color: "inherit", fontSize: "11px", fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", lineHeight: 1.4, whiteSpace: "nowrap" }}>Early</span>
    <span style={{ display: "block", fontSize: "17px", lineHeight: 1.4, margin: "2px 0 10px" }}>**Built-in agent**</span>
    An agent inside the editor that migrates a public URL, integrates a repo, or scaffolds a new site. Claude-only, and its first pass is a draft you will refine. **Needs the standalone orchestrator from a source checkout; library mode does not mount it.**
  </Card>

  <Card icon="book" href="/sites/manual">
    <span style={{ float: "right", margin: "-36px 0 0", padding: "3px 10px", borderRadius: "999px", background: "rgba(148,163,184,0.16)", border: "1px solid rgba(148,163,184,0.45)", color: "inherit", fontSize: "11px", fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", lineHeight: 1.4, whiteSpace: "nowrap" }}>Reference</span>
    <span style={{ display: "block", fontSize: "17px", lineHeight: 1.4, margin: "2px 0 10px" }}>**The contract**</span>
    Every seam the integration must satisfy, whether a human or an agent writes the code. Read this if you want to understand the shape before you delegate it — or review what an agent produced.
  </Card>
</CardGroup>

All three end in the same place: a site registered with the orchestrator, your pages serving from your existing routes, and your components exposing the fields you declared.

<Note>
  **Next.js is the tested path; Astro is a supported one.** Avocado is built and tested against **Next.js 15 and 16 (App Router)** on **Node 22+**, and every production integration so far is Next.js. On **Astro 5+**, install [`@avocadostudio-ai/astro`](/integration/astro-integration) instead — it mounts the editor routes and the preview bridge from one config block, with your own `.astro` components doing the rendering and no React added. It ships with `examples/astro-site/` and two gates that drive that fixture on every push, one of them running the preview in a real browser. What neither covers is contact with a large real template, so expect gaps there and tell us when you find one. Before you deploy, read [Production](/integration/astro-integration#production): three of the integration's settings are inert in development, so nothing in your local loop can tell you they are wrong. Any other framework can be wired through the framework-agnostic `/core` primitives — see [Non-Next.js integration](/integration/non-nextjs) — but you would be a first mover.
</Note>

## What the work actually is

Three things, in this order. Nothing else about your site changes.

```mermaid theme={null}
flowchart LR
    A["<b>1. Declare the content model</b><br/>which props are content<br/>and of what kind"]
    B["<b>2. Mark up the renderers</b><br/>so the editor can address<br/>a field on the page"]
    C["<b>3. Adapt the CMS</b><br/>read pages out,<br/>write edits back"]
    D["<b>Coverage</b><br/>a number that says<br/>it is finished"]
    A --> B --> C --> D
```

### 1. Declare the content model

Your components become blocks by registering a schema that says which props are content and what kind each one is — text, richtext, image, link, list. That registration is the boundary: a prop you declare is editable, a prop you do not declare is not, and no amount of asking changes that.

On a JSON- or file-backed site this is `registerBlock` from `@avocadostudio-ai/site-sdk/blocks`. On a CMS-backed site it is a [field table](/integration/field-table), which derives the Zod schema, the property-panel metadata, the projection out of the CMS and the merge back into it from one declaration instead of four that drift apart.

**One-time**, plus one line whenever someone needs a field that is not there yet.

### 2. Mark up the renderers

The editor addresses a field by a path scoped from the block down — `items[3].question`. Your components have to say which DOM element carries which path, using `editableProps` and, where a list row is drawn by a component of its own, `editableScopeProps` from `@avocadostudio-ai/site-sdk/markers`.

This is the part that takes the longest on a real design system, because it is a pass over every renderer. Skipping a field is silent: the block still renders, the panel still lists the prop, and clicking it on the page does nothing.

**One-time**, and then a habit — a new renderer needs its markers the way it needs its types.

### 3. Adapt the CMS

Two functions: `getPages()` returns your content as pages of blocks, `onPublish(pages, config)` writes edits back. How much sits behind them depends entirely on your CMS. A JSON-backed site is almost nothing. A CMS that localises per field and stores list rows as their own documents is the real work — see [CMS adapters](/integration/cms-adapters) and [multilingual](/integration/multilingual).

Working examples ship in the repo under `examples/sample-site/` (JSON), `examples/astro-site/` (Astro, JSON written back under `src/`), `examples/contentful-site/`, `examples/sanity-site/` and `examples/strapi-site/`. Field-table lens packs ship for Storyblok, Sanity and Contentful, under `@avocadostudio-ai/site-sdk/lens/*`.

Keep your site's own data layer. Fetch a page the way the site already does, then lay the draft's block props over it with `applyDraft` from `@avocadostudio-ai/site-sdk/lens`. Your templates keep their prop shapes, and your public route never depends on Avocado.

**One-time.** Publishing after that is a field-level diff, so a page nobody touched writes nothing.

`POST /api/editor/publish` — the contract endpoint a standalone orchestrator
publishes into — is guarded twice, because it replaces the site's content.
Under `NODE_ENV=production` it refuses every request until `publishSecret` is
configured; the scaffolds read that from `PUBLISH_TOKEN`, which the orchestrator
sends back as `x-publish-token`. And in any environment, a publish that would
remove *every* page is refused with a `409` unless the body carries
`"allowDelete": true` — what actually produces an empty page list is a client
publishing what it thinks it has after its own state failed to load, not someone
deleting a site. Removing one page of several stays an ordinary edit.

### What recurs

Almost nothing. After the integration lands, adding a field is one line in the field table, adding a block is a registration plus markers, and everything else — the copy changes, the images, the sections, the metadata, the translations — is your marketing team talking to the site.

## Which path should you pick?

| If you… | Use this |
| - | - |
| Already run a coding agent in your repo | **[Your own coding agent](/sites/coding-agent)** |
| Want the work to arrive as a reviewable PR in your normal flow | **[Your own coding agent](/sites/coding-agent)** |
| Have a monorepo, a custom build, or non-standard routing | **[Your own coding agent](/sites/coding-agent)** |
| Have compliance or data-residency constraints — the work must happen in your environment | **[Your own coding agent](/sites/coding-agent)** |
| Have a **public URL** and want a Next.js site rebuilt from it as a starting point | **[Onboarding agent → Migrate](/sites/site-agent)** — standalone orchestrator only |
| Have only a description and want a fresh site scaffolded | **[Onboarding agent → Create](/sites/site-agent)** — standalone orchestrator only |
| Want to understand the seams before you delegate — or review what an agent wrote | **[The integration contract](/sites/manual)** |

## Path 1 — Hand it to your own coding agent

This is the path we recommend.

Your agent already has what ours does not: your codebase, your conventions, your component library, your CMS client, and the history of why things are the way they are. Our agent has to discover all of that from scratch inside a repository it has never seen, and on a non-trivial site it discovers some of it wrong.

**What you get:** a copy-paste prompt that sends the agent to the right doc pages, names the SDK surfaces it should use, and ends on a verification step that is a *number* rather than "the dev server started".

**What you keep:** your review flow. The agent opens a branch, you read the diff, CI runs, you merge. Nothing lands in your repo that you did not approve.

→ **[Hand it to your own coding agent](/sites/coding-agent)**

## Path 2 — The built-in onboarding agent

An agent inside the editor's *Sites* page, with three modes:

| Mode | Starting point | What it does |
| - | - | - |
| **Migrate** | A public website URL, any framework | Scrapes the live HTML, extracts structure, downloads images, generates page specs, applies a theme, and creates a fresh Next.js site |
| **Integrate** | An existing Next.js repo | Clones it, analyses the codebase, wires `@avocadostudio-ai/site-sdk` in, registers the site |
| **Create** | A natural-language description | Scaffolds a fresh Next.js site with starter blocks, theme and pages |

<Warning>
  **Honest about where this is.** The onboarding agent works on simple landing pages and small Next.js repos. On a real site — heavy interactivity, a custom CMS schema, deep routing, a design system with non-standard tokens — it gets you part of the way and you finish the rest. Treat the first pass as a draft.

  Its strongest mode is **Migrate**, where there is no existing codebase to misunderstand and the alternative is hand-authoring pages.
</Warning>

<Note>
  **Claude only.** The onboarding agent runs on Anthropic Claude — either through your Claude subscription via the CLI backend, or an `ANTHROPIC_API_KEY` via the SDK backend. An OpenAI key alone will not drive *this* path. The editor's per-edit chat, which is the AI you talk to after a site exists, runs on Anthropic, OpenAI or Google.
</Note>

The agent runs file and shell tools on the orchestrator's host. So in development its routes answer loopback callers only, or callers with an access token once a credential is configured. In production they are not mounted unless `AGENT_SURFACE=on` and an access credential is configured.

→ **[Onboarding agent](/sites/site-agent)** — modes, prerequisites, CLI vs SDK billing, troubleshooting

## Path 3 — Read the contract

Whoever writes the code, the integration has to satisfy the same contract: the five `/api/editor/*` endpoints, the page factory, the block registration, the markers, and the registration call. The contract page walks each seam and says what it is for.

Read it if you want to understand the shape before delegating, or to review what an agent produced. It is a reference, not a race.

→ **[The integration contract](/sites/manual)**

## How you know you are done

Not "it builds". Not "the dev server started". Two numbers, then one command.

* **`editableCoverage`** — of the fields your manifest declares, how many does a rendered page actually carry a marker for? A field that lost its marker in a refactor is a field marketing silently cannot click.
* **`panelCoverage`** — is the property panel intelligible? Can every list row be told apart, do polymorphic branches narrow, is anything in your content described by nothing?

Both ship from `@avocadostudio-ai/site-sdk/coverage` and run without a browser or a model call. Put them in CI and the integration stops regressing.

Then run **`npx avocado qa`** from the site's directory, with its dev server up. It renders every page in a frame on the editor's origin, from a throwaway session, and exits 1 on any failure. It runs both coverage checks as part of that. The integration is not finished until it exits 0.

→ **[Coverage checks](/integration/coverage)** · **[QA gate](/integration/qa)**

## After your site is in

* **[A tour of the editor](/editing)** — what your team sees once the site is in
* **[Custom blocks](/integration/custom-blocks)** — register more of your own components
* **[Field table](/integration/field-table)** — one declaration, four consumers
* **[Visual editor](/integration/puck-mode)** — enable click-and-drag editing alongside the AI chat editor
* **[Publishing](/integration/publishing)** — field-level diffs back into your CMS
* **[Deployment](/operations/docker-deployment)** — run the orchestrator on your own infrastructure


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.