The SDK is split into four packages with clean boundaries:
| Package | When | What |
|---|
@usenaive-sdk/iac | build-time | Declarative naive.config.ts — defineConfig, cloud.*, business.*, agent, identity, skills (plus runtime.pool, defineModule). Run by naive up. |
@usenaive-sdk/server | run-time (your app) | new Naive({ apiKey }), forUser(id).provision(role) → an AgentProfile with tools() / revoke(), runtime(pool).start(), plus the flat resource API (cards.create, comms.email.create). |
@usenaive-sdk/runtime | inside a hosted agent | agentProfile.tools() scoped to the current agentProfile. |
@usenaive-sdk/cli (naive) | terminal / CI | login, init, up, agent profiles, revoke, module, env, and every primitive. |
import { Naive } from "@usenaive-sdk/server";
const naive = new Naive({ apiKey: process.env.NAIVE_SECRET_KEY! });
// Provision a governed agent profile per tenant, then pull its tools / revoke it.
const op = await naive.forUser(tenant.id).provision("sdr", { idempotencyKey: `op:${tenant.id}` });
const tools = await op.tools();
// await op.revoke();
@usenaive-sdk/node is the previous package name. It still works and is the base
that @usenaive-sdk/server re-exports, so existing imports keep functioning — but new
code should import from @usenaive-sdk/server to get the agent profile surface.
Scoped clients — no polymorphic args
Top-level data-plane calls act on your default user:
await naive.cards.create({ spending_limit_cents: 25000 });
await naive.vault.put("instantly.api_key", "key_xyz");
naive.forUser(id) returns the same surface bound to a specific tenant user:
const client = naive.forUser(alice.id);
await client.cards.create({ spending_limit_cents: 25000 });
await client.vault.put("instantly.api_key", "key_xyz");
There is no userId | {args} overloading — vault.put(key, value) always means
(key, value). The scope is fixed by which client you got the sub-client from.
Agent Profiles
forUser(id).provision(role) instantiates a governed agent profile for a tenant
— its identity, card, comms, and policy as one unit — and returns an AgentProfile:
const op = await naive.forUser(tenant.id).provision("sdr", {
idempotencyKey: `op:${tenant.id}`, // retried webhook → same agent profile
overrides: { identity: { legalName: tenant.company } },
});
op.status; // "provisioning" | "active" | "needs_action" | ...
const tools = await op.tools(); // governed toolset for your agent / Eve / LangGraph
await op.refresh(); // re-fetch status (provisioning is async)
await op.revoke(); // absolute: freeze card, halt sends, rotate, tear down
Run the agent wherever you like — naive.runtime("pool").start(op.id, { goal }) for
Naïve-hosted microVMs, or pull op.tools() into your own runtime. Governance is
constant either way (the governance gateway).
Control plane vs data plane
- Control plane (
naive.users, naive.accountKits, naive.plans, naive.toolkits,
and forUser(id).provision / agent profile management) lives at company scope.
- Data plane (
cards, phone, trading, email, verification, formation, domains, social,
connections, vault, logs, sessions, images, video, search, clips, llm,
browser, profile, seo, aeo, cron, jobs, memory, billing, webhooks,
plus the build primitives apps, database, storage, functions, auth) is available
on both the root (default user) and on forUser(id).
The build primitives operate on a fullstack app’s managed backend —
naive.apps.create({ name, type: "fullstack" }) first, then naive.database / naive.storage
/ naive.functions / naive.auth (own project), or naive.forUser(id).database for an
end-user’s project.
When to use the SDK vs CLI vs MCP
- SDK — embedding Naive in your multi-tenant SaaS backend.
- CLI — you, a developer, using Naive from a terminal (acts on your default user).
- MCP — handing tools to an MCP-native agent (Claude, Cursor).
The SDK is server-only in v1 (API keys are server secrets).