Every end-user signs up and gets their own AI assistant that connects third-party apps, manages encrypted credentials, and runs native business primitives β all isolated per user and governed by an Account Kit.
Quick start Β· How it works Β· Deploy Β· Customize
| Page | What it shows | |
|---|---|---|
| π¬ | Chat | A streaming agent loop powered by agentTools() β searches/connects apps, runs capabilities, and calls native primitives. |
| π³ | Cards | Per-user virtual payment cards (client.cards). |
| π¬ | Per-user inboxes on a Naive domain (client.email). |
|
| π | Connections | Connect third-party apps (Gmail, GitHub, Slack, β¦) per user. |
| π | Credentials | A per-user, KMS-encrypted vault (client.vault). |
| β | Approvals | Human-in-the-loop queue for sensitive agent actions. |
Cards & Email are sample primitive pages β the same
client.<primitive>pattern extends to domains, verification, formation, and social.
Wiring an AI agent into a real product takes more than a chat box. You need:
- π₯ Per-user identity β each app user maps 1:1 to a Naive
tenant_user. - π Isolation β one server-side API key; every call scoped via
naive.forUser(id). - π Reach β the agent can use ~1000 third-party apps and native primitives.
- π‘οΈ Governance β sensitive actions are gated by each user's Account Kit and surface as approvals.
This repo shows the entire integration in a small, readable codebase you can clone and ship.
- β‘ Next.js 15 (App Router) β UI + API routes
- π BetterAuth β email/password auth
- ποΈ Drizzle ORM + libSQL/SQLite β zero-infra local DB
- π§ Anthropic Claude β the chat model
- π§© @usenaive-sdk/node β multi-tenant business infrastructure
β οΈ Pointrepository-urlat your own repo/fork. Production needs a hosted database β see Deployment.
Prerequisites
- π’ Node.js 20+
- π An Anthropic API key
- π A Naive workspace API key (+ optional Account Kit) from the Naive dashboard
1. Get your Naive credentials
- π Sign up at the Naive dashboard.
- ποΈ (Optional) Create an Account Kit β allowlist apps (
gmail,github, β¦) and enable primitives (cards,email, β¦). Copy its id; blank uses your default kit. - π Create a workspace API key (Settings β API Keys). Copy it.
2. Configure environment
cp .env.example .envFill in the values (see Environment variables). Minimum: ANTHROPIC_API_KEY, NAIVE_API_KEY, BETTER_AUTH_SECRET.
3. Install, migrate, run
npm install
npm run db:push # create the local SQLite tables
npm run dev # β http://localhost:34004. Try it β sign up, then in the chat:
- π¬ "What can you do for me?" β lists apps + primitives
- π "Connect my GitHub" β third-party app lane (OAuth link)
- π³ "Issue a $50 virtual card" β native primitive (may need approval)
The entire Naive integration lives in three files β read these:
- 1οΈβ£ Provision on signup β
lib/auth.ts: a BetterAuthcreate.afterhook callsnaive.users.create({ external_id })and stores the id onuser.naiveUserId. - 2οΈβ£ Scope to the user β
lib/naive.ts:requireNaiveUser()resolves the session tonaive.forUser(naiveUserId). - 3οΈβ£ Give the agent tools β
app/api/chat/route.ts:client.agentTools()returns Anthropic tool defs + ahandle()dispatcher, run in a streaming loop.
flowchart LR
signup["Signup (BetterAuth)"] -->|create hook| provision["naive.users.create()"]
user["End user"] --> chat["/api/chat"]
chat --> claude["Anthropic (tool-use)"]
claude -->|tool_use| tools["naive.forUser(id).agentTools()"]
tools --> api["Naive API"]
api -->|kit-gated| ext["Third-party apps + built-in primitives"]
- π One secret API key lives on the server only.
- π―
naive.forUser(id)pins every request to/v1/users/{id}β no method ever takes auserId. - π§± Connections, vault, cards, and every primitive are isolated per user and enforced by that user's Account Kit.
flowchart TB
key["One workspace API key (server-side)"]
key -->|"forUser(A)"| sa["tenant_user A β Account Kit A"]
key -->|"forUser(B)"| sb["tenant_user B β Account Kit B"]
- π Third-party apps β
naive_search_apps,naive_list_connections,naive_connect_app,naive_list_capabilities,naive_run_capability. Reaches ~1000 external OAuth apps and all their capabilities. - π§© Built-in primitives β
naive_search_primitives(discover primitives + methods + arg schemas) andnaive_run_primitive(execute, e.g.primitive='cards', method='create'). Covers cards, email, domains, vault, KYC, formation, social, approvals.
π Same
search β runpattern for both lanes.naive_search_appsis third-party only. Sensitive methods returnpending_approvaland surface in the Approvals tab.
/api/chatstreams newline-delimited JSON (text,tool,pending,done,error).- The client renders replies token-by-token; conversations persist in
localStorage; "New chat" clears them.
.
βββ app/
β βββ api/ # Server routes β ALL Naive calls happen here
β β βββ chat/route.ts # π¬ streaming agent loop (agentTools)
β β βββ cards/route.ts # π³ cards primitive
β β βββ email/route.ts # π¬ email primitive
β β βββ connections/ # π third-party app connections
β β βββ vault/route.ts # π credentials vault
β β βββ approvals/ # β
human-in-the-loop queue
β β βββ auth/[...all]/ # π BetterAuth handler
β βββ app/ # Authenticated UI (chat + primitive pages)
β βββ login/ Β· signup/ # Auth pages
β βββ layout.tsx Β· globals.css Β· page.tsx
βββ components/ # Shared UI (ui.tsx, auth-ui.tsx)
βββ db/ # Drizzle schema + libSQL client
βββ lib/
β βββ naive.ts # β SDK wiring: provisioning + requireNaiveUser()
β βββ auth.ts # BetterAuth config + provisioning hook
β βββ auth-client.ts # Client-side auth helpers
β βββ api-utils.ts # Error β response helper
βββ .env.example
βββ drizzle.config.ts
| Variable | Required | Description |
|---|---|---|
ANTHROPIC_API_KEY |
β | Anthropic API key β the chat LLM. |
NAIVE_API_KEY |
β | Workspace API key (Naive dashboard β Settings β API Keys). |
BETTER_AUTH_SECRET |
β | Session-signing secret. openssl rand -base64 32. |
BETTER_AUTH_URL |
β prod | App base URL. http://localhost:3400 in dev; your deployed URL in prod. |
NAIVE_API_URL |
β | Naive API base. Defaults to https://api.usenaive.ai. |
NAIVE_ACCOUNT_KIT_ID |
β | Account Kit for new users. Blank = workspace default. |
DATABASE_URL |
β | libSQL/SQLite URL. Defaults to file:./dev.db. |
DATABASE_AUTH_TOKEN |
β | Auth token for a hosted libSQL/Turso database. |
npm run typecheck # tsc --noEmit
npm run build # production buildManual QA (sign up first, then chat):
- π¬ "What can you do for me?" β describes apps + primitives
- π "Connect my GitHub" β OAuth link; shows on Connections
- π¬ "Create an inbox called support" then "send an email from it" β Email updates
- π "Is
example.comavailable?" β domain check (no purchase) - π "Store my OpenAI key as
openai.key" β appears in Credentials - π³ "Issue a $50 virtual card" β if gated, shows in Approvals; approve to run
Standard Next.js app β deploys anywhere. For Vercel:
- β¬οΈ Push to GitHub and import into Vercel (or use the Deploy button).
- ποΈ Use a hosted database β serverless filesystems are ephemeral, so
file:./dev.dbwon't persist. Create a Turso DB and setDATABASE_URL+DATABASE_AUTH_TOKEN. (Or switch the dialect to Postgres indrizzle.config.ts+db/index.ts.) - π§± Apply the schema once:
DATABASE_URL=β¦ DATABASE_AUTH_TOKEN=β¦ npm run db:push. - π Set all environment variables in Vercel; set
BETTER_AUTH_URLto your deployed URL. - π Deploy.
- π·οΈ Rebrand β "Acme AI" + avatar live in
app/app/nav.tsxandcomponents/auth-ui.tsx; theme tokens inapp/globals.css. - β Add a primitive page β copy a route in
app/api/<primitive>/route.ts(thin pass-through toclient.<primitive>) + a page underapp/app/<primitive>/. The agent already reaches every primitive vianaive_run_primitive. - π‘οΈ Control agent permissions β configure each user's Account Kit in the dashboard (allowed apps, enabled primitives, what needs approval).
- ποΈ Swap the database β point at Turso, or change the Drizzle dialect for Postgres.
- π€ Agent says a built-in feature "isn't available" β ensure
@usenaive-sdk/nodeis^0.3.0+ and restartnpm run dev(Next caches resolved deps). - β³ A sensitive action never runs β it's waiting in Approvals (gated by the Account Kit). Approve it, or relax the kit policy.
- π Auth redirect loops in production β set
BETTER_AUTH_URLto your exact deployed origin. - ποΈ
db:pushfails on Vercel β run it against the hosted DB with the productionDATABASE_URL/DATABASE_AUTH_TOKEN.
- π§© Naive β the platform powering this template
- β‘ Next.js App Router
- π BetterAuth
- ποΈ Drizzle ORM
- π§ Anthropic API