Skip to main content
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

Recommended Your coding agent 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.
Early Built-in agent 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; library mode does not mount it.
Reference The contract 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.
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.
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 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: 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 — but you would be a first mover.

What the work actually is

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

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, 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 and 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 and Sanity. 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?

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

Path 2 — The built-in onboarding agent

An agent inside the editor’s Sites page, with three modes:
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.
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.
Onboarding 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

How you know you are done

Not “it builds”. Not “the dev server started”. Two numbers:
  • 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. Coverage checks

After your site is in