Guide3 min read

Getting started with the Naïve SDK: one TypeScript client for all of agent infrastructure

The new @usenaive-sdk/node is a single, Stripe-style TypeScript client for every Naïve primitive — email, cards, apps, LLM, vault, and more — with first-class multi-tenancy and a drop-in agent toolset. A getting-started guide.

Guide
TL;DR
  • @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 userId to 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/node
import { Naive } from "@usenaive-sdk/node";
 
const naive = new Naive({ apiKey: process.env.NAIVE_API_KEY! });
// baseUrl defaults to https://api.usenaive.ai

That'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:

  • Sessionsnaive.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) and approvals.wait(...).
  • Webhooks — subscribe to events with naive.webhooks.create(...) and verify them with verifyWebhookSignature(...).

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

npm install @usenaive-sdk/node
Frequently Asked Questions
What is the Naïve SDK?+
@usenaive-sdk/node is the official TypeScript SDK for Naïve — a single client that exposes every primitive (email, domains, cards, apps, the backend stack, LLM, search, vault, orchestration, and more) as typed namespaces behind one API key. It's designed in the Stripe style: construct one client, call resource methods.
How do I install and initialize it?+
Run npm install @usenaive-sdk/node, then construct const naive = new Naive({ apiKey: process.env.NAIVE_API_KEY }). The base URL defaults to https://api.usenaive.ai. That's the whole setup.
What's the difference between solo and multi-tenant mode?+
Top-level calls like naive.email.send(...) act as the API key's default user — ideal for a single agent or your own automation. For products with many end-users, naive.forUser(id) returns a client scoped to that user, so each customer's cards, vault, connections, and apps are isolated. No method takes a userId; the scope is fixed by which client you hold.
What is agentTools()?+
It's a meta-toolset for LLM agents. naive.forUser(id).agentTools() returns Anthropic tool-use definitions plus a handle(name, input) dispatcher. Instead of exposing a thousand tool schemas, it uses a discover-then-run design — search for primitives and connections, then run them — so the model stays fast and the toolset stays small. Execution is Account Kit-gated.
How does the SDK relate to the CLI, MCP, and REST?+
They're four surfaces over the same API and the same key. The SDK is for TypeScript apps and backends; the CLI is for terminals and agent shells; the hosted MCP server is for MCP clients like Claude and Cursor; REST is for everything else. Pick whichever fits the integration.
Where do I go next?+
Read the SDK overview at usenaive.ai/docs/sdk/overview and the quickstart at usenaive.ai/docs/getting-started/quickstart, then explore the per-primitive guides for the capabilities you need.
DZ
Dennis ZaxCTO

CTO of Naïve. Building the open-source agent runtime.

@denniszax