- ›
@usenaive-sdk/node— one TypeScript client for every Naïve primitive, behind one API key - ›
Stripe-style scoped client— naive.email, naive.cards, naive.apps, naive.llm, naive.vault, and more as namespaces - ›
Solo mode— top-level calls act as the key's default user; no boilerplate to get going - ›
Multi-tenant mode— naive.forUser(id) returns a client bound to one of your end-users, fully isolated - ›
agentTools()— a drop-in Anthropic tool-use toolset with a discover-then-run design instead of 1000 schemas - ›
Composes with sessions, approvals, and webhooks— the SDK is the front door to the whole platform
The new @usenaive-sdk/node is one TypeScript client for all of agent infrastructure. Email, domains, cards, the backend stack, LLM, search, vault, orchestration — every primitive, every one behind a single API key, all typed. If you've used Stripe's SDK, you already know the shape: construct one client, call resource methods, done. This is a getting-started guide.
Why a unified SDK
Naïve gives an agent the capabilities a real business needs — but capabilities are only useful if they're easy to reach. The SDK's job is to make "the agent can do everything" feel like "I called a method":
- One key, every primitive. No per-service clients, no per-provider credentials. The same
nv_sk_*key reaches all of it. - Scope is structural. You never pass a
userIdto a method. You hold a client that is a scope — the default user, or a specific tenant — so there's no ambiguity about who an action runs as. - Two planes, clearly separated. A control plane for managing your tenants and plans, and a data plane for doing the actual work.
Install and initialize
npm install @usenaive-sdk/nodeimport { Naive } from "@usenaive-sdk/node";
const naive = new Naive({ apiKey: process.env.NAIVE_API_KEY! });
// baseUrl defaults to https://api.usenaive.aiThat's the entire setup. The client is ready to call any primitive.
Solo mode: act as your default user
Top-level calls act as the API key's default user — perfect for a single agent or your own automation. Every primitive is a namespace.
// Send email
await naive.email.send({
from_inbox: inboxId,
to: "alex@example.com",
subject: "Hi",
body: "Sent from the Naïve SDK.",
});
// Run an LLM completion
const res = await naive.llm.chat({
model: "anthropic/claude-sonnet-4.6",
messages: [{ role: "user", content: "Draft a launch tweet." }],
});
// Store a secret in the encrypted vault
await naive.vault.put("api.stripe", "sk_live_...", { kind: "credential" });
// Deploy a fullstack app (gets Postgres, storage, auth, functions)
const app = await naive.apps.create({ name: "My SaaS", type: "fullstack" });Multi-tenant mode: a client per end-user
Building a product where each of your customers has their own agent? Create a tenant user, then scope a client to them with forUser. Their cards, vault, connections, and apps are fully isolated.
// Control plane: create one of your end-users
const alice = await naive.users.create({ external_id: "user_123", email: "alice@app.com" });
// Data plane: a client bound to Alice
const client = naive.forUser(alice.id);
await client.connections.connect("gmail", { callbackUrl });
await client.cards.create({ spending_limit_cents: 25000 });No method takes a userId — the scope is fixed when you call forUser, so there's never a question of who an action ran as. The control plane (naive.users, naive.accountKits, naive.plans, naive.toolkits) is root-only; the data plane (every primitive) is available on both the default scope and any forUser scope.
agentTools(): drop the platform into an LLM loop
The SDK ships a meta-toolset designed for agents. agentTools() returns Anthropic tool-use definitions plus a handle(name, input) dispatcher — drop .tools into the Messages API and route each tool_use block through .handle().
const toolset = naive.forUser(alice.id).agentTools();
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
tools: toolset.tools,
messages: [{ role: "user", content: "Connect my GitHub and issue a $50 card." }],
});
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await toolset.handle(block.name, block.input);
// feed result back into the conversation
}
}Crucially, it doesn't dump a thousand schemas on the model. It uses a discover-then-run design — tools to search primitives and third-party connections, then run them — so the toolset stays small and the model stays fast. And because execution is Account Kit-gated, the same governance that applies everywhere applies here.
It composes with the rest of the platform
The SDK is the front door, not a silo:
- Sessions —
naive.forUser(id).session({ ttlMs })mints a scoped, expiring MCP token for an untrusted runtime. - Approvals — sensitive calls return a pending approval; check with
isPendingApproval(res)andapprovals.wait(...). - Webhooks — subscribe to events with
naive.webhooks.create(...)and verify them withverifyWebhookSignature(...).
Four surfaces, one key
The SDK is one of four ways to reach the same API: the SDK for TypeScript apps, the CLI (@usenaive-sdk/cli) for terminals and agent shells, the hosted MCP server for clients like Claude and Cursor, and raw REST for everything else. They all authenticate with the same key and hit the same primitives — pick whichever fits the integration.
Get started
- SDK overview: usenaive.ai/docs/sdk/overview
- Quickstart (5 min): usenaive.ai/docs/getting-started/quickstart
- Multi-tenancy: usenaive.ai/docs/architecture/multi-tenancy
- API reference: usenaive.ai/docs/api-reference/overview
- Join the community on Discord
npm install @usenaive-sdk/node