Five doc pages cover the happy path (install, scaffold, render a chat UI), but every operational reality a developer needs to ship — pricing/credits, model selection, rate limits, hard caps, auth flows, observability — is either undocumented or buried in a copy-paste AI prompt, and the headline code examples contradict each other and the marketing site (the marketing hero even writes code the docs explicitly forbid).
1. Quickstart code reads an env var that no example ever defines (significant)
Problem: The AgentProvider snippet sets apiUrl={process.env.NEXT_PUBLIC_BETTERAGENT_API_URL}, but the .env.local block on the same page only defines NEXT_PUBLIC_BETTERAGENT_CLIENT_KEY=ba_client_.... NEXT_PUBLIC_BETTERAGENT_API_URL is never defined, exported, or mentioned anywhere in the docs. The CLI page's resolution order doesn't list it either — it uses BETTERAGENT_API_URL, with no NEXT_PUBLIC_ prefix.
Consequence: A developer copy-pasting the canonical Quickstart ships apiUrl={undefined}, which in JSX is equivalent to not passing the prop — so requests most likely fall back silently to the documented "Production default (https://www.betteragent.dev)" and the app appears to work. The defect is subtler than a crash: the example dangles a NEXT_PUBLIC_BETTERAGENT_API_URL that looks required but is defined nowhere, so a developer who wants to point at a non-default host has no documented var name to set, and an agent that "fixes" the undefined var by inventing a value can silently redirect traffic.
The fix: Either drop the apiUrl prop from the example entirely (let it resolve from betteragent.config.json/default), or add NEXT_PUBLIC_BETTERAGENT_API_URL= to the .env.local block and explain when to set it. Reconcile the NEXT_PUBLIC_ vs BETTERAGENT_API_URL naming with the CLI resolution order.
2. The docs forbid the exact array export the marketing site demonstrates (critical)
Problem: /tools states for server-actions.betteragent.ts: "No array export — export each action individually. files cannot export arrays; the generated uses to pick them all up automatically." The marketing landing page's example does the opposite:
Code verification
Runtime snippet checks
Run the public examples from this audit in an isolated sandbox and attach the pass, fixed, skipped, and failed counts to the report.
Consequence: The most prominent code sample a developer sees (the homepage hero) is structurally invalid per the docs' own rule. Anyone who learns from the landing page writes server-action files that the documented import * wiring won't pick up — and worse, the sibling files routes.betteragent.ts and actions.betteragent.tsdo export arrays (export const routes = [...], export const actions = [...]), so the three tool files have three different, undocumented export conventions.
The fix: Make the marketing example match the docs (individual exports for server actions). Add an explicit table to /tools stating, per file, whether it exports an array or individual symbols, since the rule differs across the three sibling files.
3. The provider component has three different names across the docs (critical)
Problem: The component you import and wrap your app with is named inconsistently. Quickstart and Tool Files use AgentProvider (import { AgentProvider } from "@/components/betteragent-provider"). The Components page imports BetterAgentProvider directly from betteragent-react. The AI Setup page uses all of them on a single page: the layout example shows <AgentProvider>, the "Register client actions" snippet shows <BetterAgentProvider>, and the "What the agent will do" summary says it "Wraps your root layout with BetterAgentProvider."
Consequence: A developer can't tell whether the provider is a generated local wrapper (@/components/betteragent-provider) or a package export (betteragent-react), or which name is correct. Copy-pasting the Components page snippet produces an import that doesn't match what betteragent init actually generated, yielding an unresolved import. AI agents, which can't reconcile this by judgment, will emit broken code.
The fix: Pick one canonical name and one import source. If betteragent init generates a local wrapper named AgentProvider, use that everywhere and show the underlying betteragent-react export only in an advanced "without the CLI" section. Fix the AI Setup page so all three references agree.
4. Tool names are camelCase in the docs but snake_case in the product UI (significant)
Problem: /tools says a tool's name is the "Unique identifier used in tool calls. camelCase recommended," and every example is camelCase (searchProducts, createOrder). The marketing site's own server-action example also defines name: "createCampaign" (camelCase) — but the chat-UI mockups on that same marketing page render the resulting tool calls in snake_case (create_campaign, list_audiences). So the identical action appears as createCampaign in the definition and create_campaign in the running product.
Consequence: A developer cannot tell whether names are silently transformed at runtime (camelCase in code → snake_case at invocation/display) or whether the marketing UI is simply wrong. If a transform exists, anyone who hard-codes or matches on the literal name they wrote will mismatch what the engine actually invokes or shows; if no transform exists, the docs and the product disagree about the canonical identifier. Agents that route on tool names have no authoritative casing to rely on.
The fix: State the exact naming contract on /tools: what casing you write, whether the engine normalizes it, and what string appears in tool-call events and the UI. Make the marketing mockups match whatever the rule is.
5. Every operational limit needed to ship safely is undocumented or buried in a copy-paste prompt (critical)
Location: docs nav (only Quickstart, AI Setup, CLI, Tool Files, Components); /pricing, /models, /observability, /errors, /authentication, /providers all 404
Problem: The marketing site advertises hard caps ("20 turns / conv", "80k tokens / conv", "8KB / tool result", "30s route timeout"), rate limits, three selectable models (claude-sonnet-4.6 default, claude-haiku-4.5, claude-opus-4.5), a credit-based billing model, a "Bring your own API key" capability on the Plus plan, and an observability dashboard at app.betteragent.dev. None of these has a docs page — /pricing, /models, /observability, /errors, /authentication, /providers all return 404. The only place credit pricing appears in the entire docs is the tail of a copy-paste AI prompt on /ai-setup ("Credits: 2 per conversation start · 1 per message · 3 per tool call", "Free plan: 500 credits/30-day period").
Consequence: A developer cannot answer basic production questions from the docs: How much will this cost? What happens at 80k tokens or turn 20? Why did my route get killed at 30s? Which model am I running and how do I switch? How do I bring my own API key on Plus? Where do I watch runs and spend? They either guess, hit limits in production with no documented error behavior, or reverse-engineer the marketing page. Agents indexing the docs will never surface these limits at all.
The fix: Add dedicated reference pages for Pricing/Credits, Models (and the BYO-API-key flow), Limits & Timeouts, Rate Limits, and Observability (with the app.betteragent.dev URL). Move the "Key facts" content out of the AI prompt and onto real pages; link the prompt to them.
6. "keep it concise" vs a hard 8KB cap the docs never mention (significant)
Problem: /tools describes the server-action return value as: "Keep it concise — the agent uses it to decide the next step," with no stated size limit. The marketing site lists a concrete enforced cap: "8KB / tool result."
Consequence: A developer who returns a large object (a list of rows, a full record) gets truncated or rejected at a boundary the docs never disclosed, and "keep it concise" gives them no way to know where the line is. This is a silent failure mode for both humans and agents.
The fix: State the 8KB tool-result limit on /tools, describe what happens when it's exceeded (truncation? error?), and give guidance on returning IDs/summaries instead of full payloads.
7. CLI claims "any Node ≥ 18 project" while everything is Next.js-only (significant)
Location: /cli vs the rest of the docs and the marketing framework matrix
Problem: The CLI Reference opens with "One CLI for auth, tool discovery, syncing, and component installation. Works with any Node ≥ 18 project." But every primitive is Next.js-specific: server-actions.betteragent.ts requires "use server" Server Actions, defineRoute/defineServerAction import from betteragent-next, the provider wraps a Next.js layout.tsx, and examples use next/headers cookies. The marketing site confirms the scope: "Next.js today… Vue soon, Svelte beta, Remix soon, Express soon, Hono soon."
Consequence: A developer on Express, Remix, or plain Node reads "any Node ≥ 18 project," installs the CLI, and discovers nothing works because the tool model assumes Next.js Server Actions. The claim sets a false expectation the product can't currently meet.
The fix: Change the CLI line to "Works with Next.js (App Router) projects on Node ≥ 18" and add a short "Supported frameworks" note matching the marketing roadmap.
8. authToken has a third form documented on only one page (significant)
Location: /quickstart vs /ai-setup
Problem: Quickstart documents authToken as accepting exactly two shapes: String (forwarded as Authorization: Bearer <token>) and Object (forwarded verbatim). The AI Setup page documents a third form — "Function (Client Component only): called per-request for refreshed tokens" — which the Quickstart never mentions.
Consequence: A developer who needs refreshed/rotating tokens (a common requirement) reads the Quickstart, concludes only static strings/objects are supported, and builds a brittle workaround — unaware the Function form exists. The most important authToken use case (token refresh) is hidden on the AI-prompt page rather than the auth-focused Quickstart.
The fix: Document all three authToken forms (String, Object, Function) in one place on the Quickstart, and note the "Client Component only" constraint for the Function form.
9. Components page documents four containers but uses an undocumented fifth (significant)
Location: /components
Problem: The "Available components" table lists exactly four containers: sidebar, chat-popup, cmd-k, inline-bar. The Attribution section then references a fifth, ChatDrawer: it lists ChatDrawer among components that accept the attribution prop ("available on ChatSidebar, ChatPopup, ChatCmdk, ChatInlineBar, and ChatDrawer") and renders <ChatDrawer /> as the example. betteragent add also only offers the same four (sidebar/chat-popup/cmd-k/inline-bar).
Consequence: A developer who sees <ChatDrawer /> in the example tries to install or import it and can't — it's in no component list and no add picker. They can't tell whether it's a real component, a typo, or a removed one, and the import errors out.
The fix: Either add ChatDrawer to the "Available components" table and the betteragent add picker, or replace the <ChatDrawer /> attribution example with one of the four documented containers.
10. routes depend on a baseUrl the docs never show how to set (significant)
Location: /tools (routes.betteragent.ts)
Problem: Route tools perform "a server-to-server request using the baseUrl you configured in your project settings," and path is "appended to your project's baseUrl setting." But no page shows where "project settings" live or how to configure baseUrl — there's no settings reference, and the dashboard URL itself is never given.
Consequence: A developer defining a route tool has no way to set the one value that makes routes resolve to their backend. The first route tool they write fails because baseUrl is unset, with no documented place to fix it.
The fix: Add a "Project settings" section (with the dashboard URL) showing how to set baseUrl, and link to it from the routes documentation.
11. No llms.txt and no machine-readable API surface (significant)
Location: docs.betteragent.dev — /llms.txt, /llms-full.txt, /sitemap.xml, /api all 404
Problem: For a product whose headline pitch is "Copy the AI setup prompt into Claude Code, Cursor, or any AI coding assistant," the docs site offers no llms.txt or llms-full.txt, no sitemap, and no OpenAPI/machine-readable spec. The tool protocol (routes, server actions, client actions), credit costs, and limits exist only as prose scattered across five pages and one copy-paste prompt.
Consequence: The exact audience BetterAgent targets — AI coding agents — can't efficiently or reliably index these docs. There's no canonical entry point an agent can fetch, and the contradictions in findings #1–#4 mean an agent that does scrape the docs will emit broken code with no structured source to fall back on.
The fix: Publish llms.txt/llms-full.txt and a sitemap. Provide a machine-readable description of the tool-file schema and the credit/limit constants so agents (and humans) have one authoritative, parseable source.
12. The documented API host is the marketing www host (significant)
Location: /cli (resolution order and whoami)
Problem: The CLI's API resolution order ends in "Production default (https://www.betteragent.dev)," and betteragent whoami reports api: https://www.betteragent.dev. That is the same www. marketing host that serves the landing page and agent-setup.md, not a dedicated API origin (e.g., api.betteragent.dev).
Consequence: A developer debugging server-to-server traffic sees agent requests going to the marketing domain and reasonably suspects a misconfiguration. It also couples the API to the marketing site's routing/CDN. If this is intentional it needs to be stated; if not, it's a leaked internal default.
The fix: Confirm and document the correct API base URL. If a distinct API origin exists, use it as the production default; otherwise explicitly note that www.betteragent.dev is the intended API host so developers don't treat it as a bug.
13. init is documented as writing .env.local, but its own transcript never shows it (minor)
Location: /cli and /ai-setup (and /agent-setup.md)
Problem: The CLI page says betteragent init "writes .env.local," and the AI Setup/agent-setup pages say init "Adds NEXT_PUBLIC_BETTERAGENT_CLIENT_KEY to .env.local." But the init wizard transcript shows only ✓ Signed in as acme-app and a Which chat variant? prompt — no line confirming it created or modified .env.local.
Consequence: A developer can't tell whether init actually wrote the client key or whether they must add it manually, so they may end up with a missing key and a non-functional provider (compounding finding #1).
The fix: Either show the .env.local write in the wizard transcript (e.g., ✓ Wrote NEXT_PUBLIC_BETTERAGENT_CLIENT_KEY to .env.local) or state explicitly that the developer must add it, and keep this consistent across all three pages.
14. Inconsistent CLI sign-in output and client-action examples (minor)
Location: /quickstart and /cli; /components vs /tools / /ai-setup
Problem: Three small inconsistencies. (a) The login success message on Quickstart is ✓ Signed in to acme-app (FREE)., while the init transcript on the CLI page prints ✓ Signed in as acme-app (http://localhost:3000) — different verb ("to" vs "as") and different parenthetical (plan vs URL) across the two pages. (b) The client-action example name differs by page: /components registers actions={{ openSettings: ... }}, while /tools and /ai-setup use openModal/navigate. (c) The free-plan credit wording differs: "500 credits/30-day period" (ai-setup) vs "500 credits / month" (marketing).
Consequence: Minor confusion and copy-paste friction — a developer following /components sees an openSettings action that appears in no tool-file example, and the differing sign-in/credit strings make it unclear which output or number is authoritative.
The fix: Standardize the CLI success message format across pages, use one consistent client-action example name, and align the free-plan credit phrasing ("per 30-day period" vs "per month") everywhere.
What they do well
The Quickstart follows a clear, ordered path (create project → authenticate → add provider → render component) that matches how someone actually onboards.
The tool-file model is conceptually clean — separating server-to-server routes, server actions, and client-side actions into three named files is easy to reason about.
Offering a copy-paste AI setup prompt (and a raw agent-setup.md) is a genuinely agent-friendly idea — it just shouldn't be the only home for credits, limits, and the authToken Function form.
Top 3 recommendations
Fix the code that breaks today. Make the marketing server-actions example obey the docs' own "no array export" rule (#2) and settle on one provider name/import source (#3) — these produce code that won't wire up. While there, kill the phantom NEXT_PUBLIC_BETTERAGENT_API_URL in the canonical Quickstart so the example stops dangling a var it never defines (#1), and pin down the camelCase-vs-snake_case naming contract (#4).
Build the missing reference layer. Add dedicated pages for Pricing/Credits, Models (incl. BYO-API-key), Limits & Timeouts (8KB result, 30s route, 80k tokens, 20 turns), Rate Limits, Authentication, and Observability — and move the "Key facts" out of the AI prompt onto those pages (#5, #6).
Make the docs agent-readable. Publish llms.txt/llms-full.txt, a sitemap, and a machine-readable tool-file schema and limit constants so the AI assistants BetterAgent targets have one authoritative, contradiction-free source (#11).