Skip to main content
This is the other half of the quickstart: everything its prompt tells a coding agent to do, written out so you can do it yourself, or read it when something did not do what it said it would. Two ways through. The shell block is one paste against a brand-new app. The seven steps are the same work with the reasoning attached, which is what you want when you are bringing an existing project in.
Both produce the library-mode shape: the orchestrator runs inside your Next.js app at /api/avocado, and content/pages.json is the site. Bringing a real site in — your components, your CMS, your content — is a different and larger job. Start at bring your site in.

Prerequisites

  • Node.js 22+ — check with node --version
  • A Next.js 15 or 16 project on the App Router, or an empty directory
  • One LLM API key — Anthropic, OpenAI or Google Gemini
No database, no Docker, no auth setup for local development.
Using a Google Gemini key? Add @google/genai too. It is an optional peer dependency of @avocadostudio-ai/orchestrator-core, loaded lazily, so no package manager installs it for you and a Gemini plan fails at the first call without it. ANTHROPIC_API_KEY and OPENAI_API_KEY need nothing extra — both vendor SDKs are ordinary dependencies.

The same thing, as one shell block

No coding agent, or you would rather watch it happen? This is the same wiring in one paste, against a brand-new app. It was run end to end on Node 22 / npm 10 / Next 16.3.5; the siteUrl, writeOnPublish and publishSecret lines it has gained since are the ones create-avocado-site emits, which pnpm test:build builds and serves on every run.
Put a real key in .env.local, then start both processes.

Step by step

1. Install the packages

From your project directory:
site-sdk is the integration surface — routes, page factory, markers, publishing. orchestrator-core is the brain, and it is an optional peer of the SDK, so it is not installed for you: a site that only renders blocks and talks to a remote orchestrator does not need it, and you do. Both pull in @avocadostudio-ai/blocks, @avocadostudio-ai/shared, @avocadostudio-ai/preview-adapter and @avocadostudio-ai/richtext as transitive dependencies.

2. Wrap your Next config

orchestrator-core carries native dependencies (better-sqlite3, sharp). withAvocado sets serverExternalPackages, the matching server externals, and transpilePackages together — serverExternalPackages alone is not enough, because transpilePackages overrides it for a transitive dependency. See server externals. Then import the block stylesheet, at the top of app/globals.css:
The built-in blocks ship their own CSS. Without this line everything below still works — the page renders, the editor drives it, the chat edits land — and it all looks like unstyled HTML, which reads as a broken install rather than a missing import.

3. Give the site some content

Avocado edits a PageDoc — a page with an ordered list of typed blocks. For this quickstart, keep it in a JSON file.
content/pages.json
Then the three fetchers the SDK asks for:
Hero and CTA are two of the 20 built-in block types, which is why this renders with no components of your own. On a real site the blocks are your React components, registered with a schema — see custom blocks.

4. Mount the editor API route

One catch-all route serves the block manifest, the page list, and draft-mode entry and exit. Pass onPublish — and, with it, publishSecret — when you want publishing through this route.

5. Mount the orchestrator inside your app

siteName is what the editor greets you with. Leave it out and the editor title-cases the site id instead, so a mount called my-shop is introduced as “My Shop” — a reasonable guess, and a poor one for anything whose id is not its name. There is also demoContent: true, which tells the editor this mount is serving Avocado’s shipped demo pages and turns on the first-run suggestions written against them; create-avocado-site sets it, and a real site should not. This is library mode: the planner, the operations engine, the draft state and the version log all run inside your Next.js app at /api/avocado. The adapter is how it reads your content on a cold session — and, when someone publishes, how it writes back. writeOnPublish is what gives jsonFileAdapter an onPublish at all; it defaults to false, and without it Publish reports success and leaves the file untouched.
createOrchestrator() is open in development and closed under NODE_ENV=production when neither ACCESS_PASSWORD_HASH nor ORCHESTRATOR_ACCESS_TOKEN is set, and no auth hook is passed. That is deliberate — an unauthenticated publish endpoint on your own domain is not a default anyone should reach by forgetting something. Local next dev needs nothing. Full detail: security and access.

6. Render the page

generateMetadata derives a page’s title, description and Open Graph tags from the page itself. Three tags it cannot derive, because none of them is knowable without knowing where the site lives: <link rel="canonical">, og:url, and an og:image resolved to an absolute URL. siteUrl is how you say. Leave it out and the SDK emits none of the three rather than guessing — a wrong canonical is worse than an absent one — and a relative ogImage goes out relative, which is correct in an <img src> and ignored by every social crawler, so the page’s cards render as a bare text link. Read it from an environment variable at the call site, as above, so a preview deployment describes itself rather than claiming to be production.
Delete app/page.tsx if create-next-app made one. Both it and app/[[...slug]]/page.tsx match /, and the more specific route wins — so the starter page keeps being served, no error is logged, no route conflict is reported, and the site you just wired up is invisible. This is the single most common way a first run ends in “nothing happened”.
mode defaults to "auto": one route handles both the published page and the editor preview, with no middleware or proxy to set up. It is not statically rendered, because deciding between the two modes means reading searchParams on every request. A production site wants mode: "static" plus a separate mode: "preview" route — see Next.js integration.

7. Set your key

ORCHESTRATOR_URL tells the SDK’s server-side draft fetch where the orchestrator is. A library-mode mount registers itself, so this is usually redundant — set it anyway, because the fallback when nothing is registered is http://127.0.0.1:4200, and whatever else is listening there will answer. NEXT_PUBLIC_SITE_URL is the origin createSitePage({ siteUrl }) reads. Point it at your real domain when you deploy; the canonical link and the social cards are only as right as this value. OPENAI_API_KEY and GOOGLE_GENAI_API_KEY work in the same slot. The editor’s model picker offers whichever keys are present. Every variable Avocado reads: environment reference.

Troubleshooting

pnpm dev fails with a better-sqlite3 or sharp error. Your Next config is not wrapped. Apply withAvocado (step 2) — the native binaries must stay external to the bundle. The chat answers, but nothing changes, and the model picker is empty. No provider key reached the orchestrator. Check curl http://localhost:3000/api/avocado/status/planner: availableProviders should list your provider, and plannerSource should not be "demo". If it is, the key is missing from .env.local or the dev server has not been restarted since you added it. The preview iframe is blank. Open http://localhost:3000 in a normal tab first. The editor frames your real site, so if the site is not serving, the preview has nothing to show. The preview shows the published page and never your edits. The property panel saying “this block is not in the page the orchestrator returned” is the same symptom: the editor is talking to a different orchestrator than the one inside your site. Confirm you passed --orchestrator http://localhost:3000/api/avocado, or set the site’s Orchestrator URL in the editor’s site settings. The editor says nothing was written, and content/pages.json is unchanged. The adapter has no onPublish, so there was nowhere to put the pages — your edits are still there as a draft. Pass writeOnPublish: true to jsonFileAdapter (step 5). The API answers this one as ok: true, written: false with the same thing in reason, so a script that checks only ok will not notice. Publishing answers 401 or 409. A 401 is POST /api/editor/publish: either it is running under NODE_ENV=production with no publishSecret — the body’s reason names PUBLISH_TOKEN — or the x-publish-token it received does not match the one configured. A 409 means the publish would have removed every page, and the reason says what it protected; resend with "allowDelete": true only if emptying the site is genuinely what you meant. A production build serves the site but the editor loads nothing. createOrchestrator() is closed under NODE_ENV=production with no credential. curl http://localhost:3000/api/avocado/auth/status answers mode: "closed" and a reason naming ACCESS_PASSWORD_HASH and ORCHESTRATOR_ACCESS_TOKEN; set one of them. See security and access. Node version errors. Install Node 22 with your version manager — fnm install 22, nvm install 22, or mise use node@22. Something deeper. See chat troubleshooting.