BetterAgent Documentation Audit
One-line state: real, well-structured docs for an agent-install product, but the surfaces don't agree — the copy-paste AI-setup prompt names the provider two ways in one example, the homepage's headline code sample exports a shape the wrapper can't import and references an undeclared symbol, the chat-variant list differs page to page, and the marketing site sells models and frameworks the docs say don't exist yet. For a product whose pitch is "paste this into Claude Code and it wires itself up," these contradictions are the whole ballgame: an agent following them fails silently.
1. The AI-setup prompt names the provider two ways inside one example (critical)
Location: https://www.betteragent.dev/agent-setup.md and /ai-setup
Problem: In the raw canonical prompt, the prose says "Register client actions in AgentProvider via the actions prop (add it to the generated components/betteragent-provider.tsx)" and the code immediately below uses <BetterAgentProvider ... actions={{ ... }}> — two different names for the provider in the same snippet. The rendered /ai-setup page then uses the bare name AgentProvider roughly 30 times and BetterAgentProvider zero times. The only place the relationship is ever reconciled is /tools, which shows that AgentProvider is a generated wrapper function in components/betteragent-provider.tsx that internally renders the SDK's BetterAgentProvider (imported from betteragent-react).
Consequence: An AI coding agent — the explicitly intended consumer of this prompt — reads AgentProvider in the prose and BetterAgentProvider in the adjacent code with no in-context explanation of which to import or which receives the actions prop. The reconciling page (/tools) is not the page the prompt sends it to. The agent either imports a symbol that doesn't exist or attaches actions to the wrong component.
The fix: State once, inside agent-setup.md and /ai-setup themselves, that betteragent-react exports BetterAgentProvider and that betteragent init generates a local AgentProvider wrapper around it. Then make the example internally consistent — use the same name in the prose and the code so the snippet doesn't disagree with itself.
2. Homepage server-action example exports an array the documented wrapper can't consume (critical)
Location: https://www.betteragent.dev/ (homepage) vs /tools
Problem: The homepage shows the server-actions file as a single array export:
export const serverActions = [
defineServerAction({ name: "createCampaign", ..., handler: createCampaign }),
];
But the /tools wrapper consumes server actions with a namespace import — import * as serverActions from "@/server-actions.betteragent" — and passes that namespace object straight into <BetterAgentProvider serverActions={serverActions} ...>. A namespace import over a module whose only export is serverActions (an array) yields { serverActions: [ ... ] }, not a map of individual actions.
Consequence: A developer or agent who copies the most prominent code sample on the marketing site produces a module shape the documented wrapper reads incorrectly — the provider receives a nested namespace object instead of the actions, and the agent's tools silently fail to register. (Note: the handler here is imported from @/app/actions, so this is purely an export-shape mismatch, not an inline-handler problem.)
The fix: Make the homepage example match what the /tools wrapper imports — individual named exports consumable via import * as serverActions. If a single array export is actually supported, document that on /tools and show the wrapper importing it that way.
3. Homepage sells three selectable, priced models; docs say only one exists and the picker is illustrative (critical)
Location: https://www.betteragent.dev/ vs /limits-billing
Problem: The marketing homepage presents a model picker with per-token prices as if selectable: claude-sonnet-4.6 · default $3/1M, claude-haiku-4.5 · fast $0.80, claude-opus-4.5 · smart $15. /limits-billing states the opposite: "The hosted chat engine currently runs on a single fixed model — claude-sonnet-4-6 — for every project and plan. Per-project or per-conversation model selection isn't available yet," and that the homepage widget is "illustrative... not a live setting — there's no way to switch to Haiku or Opus today." The model id is even spelled differently: claude-sonnet-4.6 (dot, homepage) vs claude-sonnet-4-6 (hyphen, docs).
Consequence: Developers architect around model selection and per-token pricing that don't exist, then discover at integration time that every call is locked to one model under credit-based billing. The id-spelling mismatch also means anyone grepping for the model name in code or logs gets inconsistent strings.
The fix: Make the homepage picker reflect reality — disable it, label it "coming soon," or show only the shipping model. Pick one canonical model id spelling and use it on both surfaces. If Haiku/Opus and per-token pricing are roadmap, say so explicitly instead of presenting them as live.
4. Homepage server-action example uses z without importing it (significant)
Location: https://www.betteragent.dev/ (homepage)
Problem: The same headline code sample calls schema: z.object({ audienceId: z.string() }) but imports only defineServerAction (from betteragent-next) and createCampaign (from @/app/actions). The z (zod) symbol is never imported anywhere in the snippet.
Consequence: Copy this example as-is and it throws ReferenceError: z is not defined at module load — before the agent ever runs. For the most prominent code block on the marketing site, and exactly the "incomplete example / missing import" failure class agents can't recover from, that's a copy-paste-and-it-breaks defect.
The fix: Add import { z } from "zod"; to the example (and list zod as a dependency if it isn't already implied), so the snippet runs unmodified.
5. The documented setup passes apiUrl from an env var it never creates (significant)
Location: /quickstart (layout example) vs /ai-setup and /quickstart prose
Problem: A quickstart layout sample wraps the app with <AgentProvider ... apiUrl={process.env.NEXT_PUBLIC_BETTERAGENT_API_URL} ...>, passing apiUrl unconditionally. But /ai-setup's "What the agent will do" list says the setup only "Adds NEXT_PUBLIC_BETTERAGENT_CLIENT_KEY to .env.local" — it never sets NEXT_PUBLIC_BETTERAGENT_API_URL. And /quickstart's own prose says apiUrl "defaults to https://www.betteragent.dev and only needs to be set if you're pointing at a local or staging backend." (The live /quickstart render has since dropped the apiUrl prop from its layout, so the two snapshots of the same page now disagree on whether it's there at all.)
Consequence: Following the layout that passes apiUrl from an env var the setup never defines hands the provider apiUrl={undefined} while the documented production default goes unused. Depending on how the SDK treats an explicit undefined, this either overrides the production default with nothing or creates a confusing "works on my machine" footgun. (Conditional: the exact verbatim agent-setup.md layout block was not in the provided excerpt, and the consequence depends on SDK undefined-prop handling — confirm both before treating as critical.)
The fix: Drop apiUrl from the default layout example (rely on the documented production default), or add NEXT_PUBLIC_BETTERAGENT_API_URL to the setup steps with a clear "local/staging only" comment. Don't pass an env-var prop the setup never defines, and make every snapshot of the page agree on whether the prop is present.
6. /quickstart lists four chat variants; /cli and /components list five — drawer missing (significant)
Location: /quickstart vs /cli and /components
Problem: /quickstart states "Available variants: sidebar · chat-popup · cmd-k · inline-bar" — four. Both /cli (the betteragent add NAME row, sidebar | chat-popup | cmd-k | inline-bar | drawer, and the interactive picker, "drawer — Slide-out panel from the right edge") and /components (the component table plus a ChatDrawer component and an attribution prop "available on ChatSidebar, ChatPopup, ChatCmdk, ChatInlineBar, and ChatDrawer") document a fifth: drawer. Two pages corroborate that quickstart's list is the incomplete one.
Consequence: An agent treating /quickstart as the source of truth treats drawer as invalid and will refuse or rewrite a perfectly valid betteragent add drawer. Humans miss a shipped component entirely. The impact is a missing list entry rather than a silent production failure, hence significant rather than critical.
The fix: Add drawer to the /quickstart variant list. Better: generate the variant list from a single source so the pages that mention components can't drift.
7. Homepage "framework agnostic" matrix advertises Svelte "beta"; docs cover only Next.js (significant)
Location: https://www.betteragent.dev/ vs the docs nav
Problem: The homepage "framework agnostic" matrix lists Next.js as shipped, Vue "soon", Svelte "beta", and Remix/Express/Hono "soon". The entire docs tree (Introduction, Quickstart, AI Setup, CLI Reference, Tool Files, Components, Limits & Billing) covers only Next.js — every code sample, the CLI's route/server-action discovery, and the provider wiring assume Next.js.
Consequence: "Beta" reads as available-now, not "soon." A developer who picks BetterAgent for a Svelte app on the strength of the matrix finds zero documentation, no install path, and no components for it — the same marketing-oversells-reality pattern as the model picker.
The fix: Either ship at least minimal Svelte ("beta") docs or relabel the matrix so non-Next.js entries clearly read as not-yet-available. Don't present a framework as "beta" with no documented path to use it.
8. Rate-limit table references API endpoints that have no reference anywhere (significant)
Location: /limits-billing
Problem: The rate-limit table cites POST /api/v1/chat, /api/v1/execute-result, and /api/v1/describe, but there is no API Reference page anywhere in the nav (Introduction, Quickstart, AI Setup, CLI Reference, Tool Files, Components, Limits & Billing — that's the whole tree). The describe endpoint in particular is named once and never otherwise explained — no request shape, no auth, no response.
Consequence: Anyone needing to call the platform directly (custom client, server-to-server, debugging a rate limit) has endpoint names and nothing else — no schemas, no auth headers, no error codes. For an agent there is nothing machine-readable to discover; the endpoints might as well not exist.
The fix: Ship an API reference (ideally an OpenAPI/Swagger spec) covering at least the three already-referenced endpoints, including auth, request/response schemas, and error codes. At minimum, explain what /api/v1/describe is and when it's called.
9. Pricing and the credit period contradict between the marketing homepage and the docs (significant)
Location: https://www.betteragent.dev/ vs /limits-billing
Problem: The homepage prices plans in dollars per month and frames credits as monthly — "Starter $0.99/mo for 1,500 credits," "Free $0/mo · 500 credits / month," "Plus $14.99/mo · 4,000 credits." /limits-billing lists the same credit tiers with no dollar prices at all, and defines the period as "a rolling 30-day window — it resets 30 days after the window last started, not on a fixed calendar date." The docs surfaces (/ai-setup and the raw agent-setup.md) consistently say "500 credits/30-day period," matching the rolling-window model — so the only "/mo" framing is on the homepage.
Consequence: "Per month" and "rolling 30-day window" are materially different billing models — a developer planning quota around calendar months will misjudge when credits reset. And because the docs are silent on dollar prices, the only pricing source is the marketing page the docs otherwise contradict on the period.
The fix: State dollar prices and the reset model in one place and reference it from both surfaces. Use "rolling 30-day window" consistently and stop labeling it "/mo" on the homepage if it isn't calendar-monthly.
10. The "same" AI-setup prompt differs between the rendered page and the file you copy (significant)
Location: /ai-setup (rendered) vs https://www.betteragent.dev/agent-setup.md (raw)
Problem: /ai-setup tells users to copy a prompt, but the rendered "Key facts" block includes a runtime-caps line ("8KB/tool result · 30s route timeout · 20 steps/conv · 80k tokens/conv") and a Limits/billing link that the raw agent-setup.md file — the actual canonical copy source — omits. The two representations of the same prompt are out of sync.
Consequence: Whichever copy a developer or agent grabs, they get a different prompt. Anyone pasting the raw file loses the runtime caps entirely and may build tools that exceed the 8 KB result limit or the 20-step ceiling with no warning that those limits exist.
The fix: Generate the rendered page from the raw file (or vice versa) so they cannot diverge, and make sure the runtime caps and billing link live in the canonical source, not only the rendered view.
11. Footer "Edit this page" and feedback links point at the Nextra template repo, not BetterAgent (significant)
Location: / (docs homepage footer; Nextra-wide)
Problem: The "Edit this page" link targets https://github.com/shuding/nextra/content/index.mdx and "Question? Give us feedback" targets https://github.com/shuding/nextra/issues/new — both pointing at the Nextra framework's own repository (shuding/nextra), not any BetterAgent repo. These are un-customized template placeholders. There is also no GitHub link for the product itself anywhere in the nav.
Consequence: Every "report a problem" or "edit this page" click lands a developer in the docs framework's issue tracker, where their report is noise to the wrong project and never reaches BetterAgent. Feedback is silently lost.
The fix: Point docsRepositoryBase / feedback links at BetterAgent's own repository (or a support channel), or remove the footer links if there's no public repo to edit against.
12. betteragent-cli is a dev dependency on two pages and a runtime dependency on a third (minor)
Location: /components vs /quickstart and /cli
Problem: /quickstart and /cli install the CLI as a dev dependency (-D), while /components installs all three packages as regular dependencies: npm i betteragent-react betteragent-next betteragent-cli.
Consequence: A developer following /components ships the CLI in their production dependency tree (extra install weight, a build-time tool living in runtime deps). Not fatal, but an inconsistency a careful reviewer flags and an agent can't resolve.
The fix: Install betteragent-cli with -D everywhere it appears; keep only betteragent-react and betteragent-next as runtime dependencies.
13. First-run CLI examples mix localhost and production environments (minor)
Location: /cli
Problem: The init example shows ✓ Signed in as acme-app (http://localhost:3000) — a localhost API URL in a first-run sign-in — while the whoami example on the same page shows api: https://www.betteragent.dev. The two examples depict inconsistent environments without explaining why.
Consequence: A reader can't tell whether localhost is the expected default for init or an artifact of someone's local testing, which muddies the "what should I see on first run" signal — exactly the moment a beginner is most uncertain.
The fix: Use the production API URL consistently across first-run examples, or add a one-line note that the localhost value reflects a local/staging backend and isn't the default.
What they do well
- The /tools page does the hard, correct work of explaining the
AgentProvider(generated wrapper) vsBetterAgentProvider(SDK frombetteragent-react) split with a real code sample — the problem is only that nothing else on the site reflects it. - Runtime caps are concrete and quantified where documented (8 KB/tool result, 30s route timeout, 20 steps/conv, 80k tokens/conv) — the kind of hard numbers agents and engineers actually need.
- The CLI reference is genuinely structured — option tables, an explicit
apiUrlresolution order (flag → env → config → production default), and an interactive-picker transcript — more than most early-stage tools ship.
Top 3 recommendations
- Make the AI-setup prompt internally and externally consistent. It's the product's headline feature, yet it names the provider two ways in one example, references an unimported
z, and exists in two out-of-sync copies (rendered vs raw). Fix these before anything else. - Establish one source of truth for cross-page facts — component variants, model id spelling, credit/billing model, package install flags, framework support — and generate the pages from it. Nearly every contradiction here is the same fact stated differently on two surfaces.
- Reconcile the marketing homepage with the docs, then ship an API reference. Stop presenting a non-functional model picker, unstated pricing model, and "beta" frameworks as live, and document the
/api/v1/*endpoints the rate-limit table already names.