Guide13 min read

Build an Agentic Incorporation Platform (Where No Company Gets Filed Without Human Approval)

Build an agentic incorporation platform on Naïve: an AI agent designs the entity, KYCs founders, and files a real US LLC, with every binding step approval-gated.

Guide
TL;DR
  • An agentic incorporation product requires real identity primitives: an AI cannot substitute for verified human identity or a state filing without KYC, formation, and a human gate on every binding step
  • Naïve ships the whole identity stack as primitives KYC (verification), US company formation, domain purchasing, and identity-aware email — each scoped per tenant user and governed by an Account Kit
  • Every founder is an isolated tenant user; the agent is scoped with naive.forUser(founderId) and filtered by that user's kit, so one founder's KYC, entity, and documents can never leak to another
  • The agent designs the entity with naive.llm.chat() a full OpenRouter wrapper over 300+ models (anthropic/claude-sonnet-4.6, openai/gpt-5.2) — to generate name options, pick a state, map a NAICS code, and draft the business description
  • verification.start, formation.submit, the irreversible filing, and domains.purchase are gated by default: the API returns 202 pending_approval and replays the action only after a human approves
  • Sensitive PII (SSN, DOB, address) is captured during KYC into an encrypted identity vault and decrypted only at filing time, server-side it never touches your database

Most "AI incorporates your startup" demos stop at a chatbot that emails you a PDF checklist. The moment you try to actually file a company for someone, the model isn't the hard part — identity, accounts, and authority are:

  • You can't file a US entity without verified human identity — real KYC on every founder, with SSN, DOB, and addresses you are now legally responsible for protecting.
  • One founder's identity data and formation documents must never leak into another customer's account.
  • Filing an entity, spending the formation fee, and buying a domain are binding, irreversible actions — they need a human in the loop, enforced when the action runs, not as a UI afterthought.

Without a platform, you end up integrating a KYC vendor, encrypting and isolating PII, integrating a filing provider, a domain registrar, an email provider, and building a bespoke approval workflow before you write a single line of product. That plumbing is the product — and it's exactly what Naïve ships as primitives.

This is a developer tutorial. Every code block runs against the current @usenaive-sdk/server, with signatures pulled straight from the docs. By the end you'll have a multi-tenant backend where each founder gets an isolated, governed AI agent that designs a company, runs KYC, files an LLC through Naïve's formation flow, buys a domain, and turns on email — with binding steps frozen for a human by default.

Not legal advice. This tutorial shows how to wire formation and KYC tooling on Naïve. It is not legal or tax advice, and you remain responsible for licensing, disclosures, and compliance in your jurisdiction.

Per-founder architecture: each founder is an isolated tenant user governed by an "Incorporation" Account Kit; the agent is scoped with naive.forUser(founder), designs the entity with the LLM primitive over OpenRouter, and moves through verification, formation, domains, and email — with KYC, filing, and domain purchase frozen for human approval at execution time, and PII held in an encrypted identity vault.

What we're building

  • A backend service that provisions an isolated AI agent per founder (the customer of your product).
  • The agent designs the entity — name options, home state, NAICS industry code, and a compliant business description — using an OpenRouter model through naive.llm.
  • The agent runs KYC on every founder, files a real US LLC, buys a domain, and provisions an inbox on it.
  • Every legally binding step — start KYC, file the entity, purchase the domain — returns 202 pending_approval and only runs after a human approves.

The whole arc is the Users, Account Kits, Verification, Formation, Domains, Email, and LLM primitives, composed behind one key.

Step 0: Install and authenticate

You need one Naïve workspace key (nv_sk_...) from the developer dashboard. It is a server secret — the SDK is server-only.

npm install @usenaive-sdk/server
import { Naive, isPendingApproval } from "@usenaive-sdk/server";
 
const naive = new Naive({ apiKey: process.env.NAIVE_API_KEY! });

That single client is your control plane (company-scoped: naive.users, naive.accountKits) and, via naive.forUser(id), your data plane scoped to any one founder. There are no per-provider keys anywhere in this tutorial — KYC, filing, the registrar, email, and the models all ride on this one key.

Step 1: Define the policy once with an Account Kit

An Account Kit is a reusable policy template. It declares which primitives a user may use and which of those actions must pause for human approval. Define the incorporation policy once, then assign every founder to it.

const kit = await naive.accountKits.create({
  name: "Incorporation",
  primitives_config: {
    verification: { enabled: true, requiresApproval: true },
    formation:    { enabled: true, requiresApproval: true },
    domains:      { enabled: true, requiresApproval: true },
    email:        { enabled: true },
  },
});
  • The four primitives an incorporation agent needs are enabled; everything else stays off.
  • requiresApproval: true on verification, formation, and domains is the execution-time gate. In fact, cards, domains, verification, formation, and connecting services default to requiring approval for agent calls — setting it explicitly just makes the policy self-documenting.
  • Change the kit later and the policy changes for every founder on it at once.

Step 2: Provision a founder as a tenant user

A tenant user is one of your end-users. Every primitive — verification, formation, domains, email — is scoped to one. Tenant users never sign into Naïve; you manage them entirely through the SDK.

const founder = await naive.users.create({
  external_id: "founder_db_uuid",      // your own stable id
  email: "ada@foundermail.com",
  label: "Ada Lovelace",
  account_kit_id: kit.id,              // governed by the Incorporation kit
});
 
// Scope the founder's identity work to this client. Every gated call below uses it.
const client = naive.forUser(founder.id);
  • client is the same surface as naive, permanently bound to this founder.
  • Founder A's client can only see and act on Founder A's resources, and every call is additionally filtered by the Incorporation kit. There is no way for it to reach Founder B's KYC, entity, or documents.

Step 3: Let the agent design the company with an OpenRouter model

Before anything binding happens, the agent does the cheap, reversible thinking: pick a few legal name options, choose a home state, map the business to a NAICS industry code, and write a compliant (≤256 char) description.

OpenRouter logo — the LLM primitive is a full wrapper over OpenRouter, reaching 300+ models behind the same Naïve key.

The LLM primitive is a full wrapper over OpenRouter: 300+ models behind one OpenAI-compatible endpoint, with an optional models[] fallback chain and a provider{} routing object. The request/response shapes are exactly OpenRouter's; Naïve holds the OpenRouter key and bills the exact returned cost in credits.

Model reasoning here isn't founder-private PII, so run it on your workspace default user with naive.llm (scope it per founder with client.llm if you'd rather meter the spend that way):

const brief = "A studio building AI agents for small law firms. Founder: Ada Lovelace, 100% owner.";
 
const res = await naive.llm.chat({
  model: "anthropic/claude-sonnet-4.6",
  models: ["anthropic/claude-sonnet-4.6", "openai/gpt-5.2"], // fallback chain
  provider: { sort: "throughput" },                          // OpenRouter routing
  messages: [
    {
      role: "system",
      content:
        "You are a US company-formation assistant. Reply with ONLY compact JSON: " +
        '{"name_options":[{"name":string,"entity_type_ending":"LLC"}],' +
        '"state":"WY"|"DE","naics_hint":string,"description":string}. ' +
        "description must be <=256 chars. Provide exactly 3 name_options.",
    },
    { role: "user", content: brief },
  ],
});
 
const plan = JSON.parse(res.choices[0].message.content);
console.log(plan.name_options, plan.state, plan.naics_hint, plan.description);
console.log("LLM cost (credits):", res.credits_used);
  • Real model slugs, a real fallback chain, and real provider routing — straight from the LLM docs. Swap models by changing one slug.
  • res.credits_used is the exact OpenRouter cost, converted to credits, so you can attribute spend per founder.

Founders weigh where to incorporate — Wyoming (no state income tax, strong privacy, low fees) versus Delaware (the default for venture-backed startups). The agent reasons about the trade-off, but the human still approves the actual filing.

Now resolve the NAICS hint to a real code id. The catalog read is free:

// GET /v1/formation/naics-codes  — or: naive formation naics-codes
const naics = await fetch("https://api.usenaive.ai/v1/formation/naics-codes", {
  headers: { Authorization: `Bearer ${process.env.NAIVE_API_KEY}` },
}).then((r) => r.json());
 
// Match plan.naics_hint against naics.naics_codes[].title and take its id, e.g.:
const naicsCodeId = "2o8v0kcaCWyPyi3LJFsCiTCFSyk";

Nothing here is binding yet — no money has moved and no identity has been touched. That changes in the next step, which is exactly why the next step is gated.

Step 4: KYC every founder — the first execution-time gate

Filing requires verified founders. The verification primitive runs KYC and exposes a single ready_for_formation signal once everyone passes. Because verification.start is gated by the Incorporation kit, the agent's call does not execute — it freezes for a human.

Identity verification and incorporation paperwork — KYC (ID scan, selfie, SSN, address) must pass for every founder before any entity can be filed, and the sensitive PII lands in an encrypted identity vault, not your database.

const start = await client.verification.start({
  members: [
    {
      first_name: "Ada",
      last_name: "Lovelace",
      email: "ada@foundermail.com",
      ownership_percentage: 100,
      role: "primary",
      is_responsible_party: true,
    },
  ],
});
 
if (isPendingApproval(start)) {
  // 202 — the API froze the action instead of running it.
  console.log("KYC start awaiting approval:", start.approval_id);
 
  // A human (operator or the end-user in your app) approves...
  await client.approvals.approve(start.approval_id);
 
  // ...and the API replays the frozen verification.start.
  const approved = await client.approvals.get(start.approval_id);
  // approved.result holds the verification, incl. the primary member's hosted KYC link.
}
  • isPendingApproval(res) is a typed discriminator exported from the SDK — HTTP 202 is a success, not a thrown error.
  • The gated methods (verification.start, formation.submit, domains.purchase, cards.*, connections.connect) each resolve to either their normal result or a PendingApproval.
  • On approval, the API replays the original action and records the result — the agent never gets to widen its own authority.

The primary founder gets a hosted KYC link in the response; any secondary founders are emailed theirs automatically. Each completes KYC in-browser (ID scan, selfie, SSN, address). Poll until everyone passes:

// GET /v1/verification/:id  — or: naive verification status <id>
const v = await fetch(`https://api.usenaive.ai/v1/verification/${verificationId}`, {
  headers: { Authorization: `Bearer ${process.env.NAIVE_API_KEY}` },
}).then((r) => r.json());
 
if (v.ready_for_formation === true) {
  // every member has status: "pass" — cleared to file
}

The SSN, DOB, and addresses collected here go into Naïve's encrypted identity vault — not your database. You hold a verification_id, nothing more.

Step 5: File a real LLC — the irreversible action, gated

The formation primitive incorporates the LLC end-to-end. It's a deliberate two-step flow so payment and the irreversible filing are distinct:

  1. formation.submit(...) validates KYC and creates a $249 hosted checkout (action formation.create).
  2. After payment, naive formation execute <id> dispatches the actual state filing (action formation.submit) — pulling verified PII from the encrypted identity vault at this moment, so it never persists in your systems.

Both steps are gated by the kit. Here's step 1:

const filing = await client.formation.submit({
  verification_id: verificationId,
  entity_type: "LLC",
  state: plan.state,                    // "WY" or "DE"
  naics_code_id: naicsCodeId,
  description: plan.description,         // <=256 chars, from the model
  name_options: plan.name_options,      // 3 options, for availability
});
 
if (isPendingApproval(filing)) {
  await client.approvals.approve(filing.approval_id);
  const approved = await client.approvals.get(filing.approval_id);
  // approved.result.checkout_url — the $249 formation fee
}

The founder pays the checkout_url. A payment webhook flips payment_status to paid. Only then does the irreversible filing run — and it is gated too:

// POST /v1/formation/:id/submit  (action_type: formation.submit, the irreversible filing)
// SDK/CLI: naive formation execute <formation-id>
// Returns 202 pending_approval; approve it, and the API replays the filing.
await client.approvals.approve(filingExecApprovalId);

The irreversible filing, frozen for a human: a gated formation.execute returns 202 pending_approval and is never filed until a person approves; on approval the API replays it, decrypts PII at filing time, and the state returns Articles of Organization and an EIN letter. Deny, and nothing is filed and no fee is spent.

This is execution-time permission enforcement in its most literal form: the most expensive, least reversible action your product can take — registering a legal entity with a state — is gated by default and does not run until a human approves. A human approves, the API replays, and only then does PII leave the vault.

Track status and pull documents once filing completes:

// GET /v1/formation/:id → status: submitted → formation_completed
// GET /v1/formation/:id/documents → ArticlesOfOrganization, EinLetter (signed URLs, ~1h)

Step 6: Buy the domain and turn on email

A company needs an address. Domains can search, price, and purchase a domain directly — and purchased domains are auto-configured for email (SPF/DKIM/DMARC handled). domains.purchase is gated, so it runs the same approve-and-replay loop.

// Check availability + price (free): GET /v1/domains/search?domain=...
const avail = await fetch(
  "https://api.usenaive.ai/v1/domains/search?domain=adastudio.com",
  { headers: { Authorization: `Bearer ${process.env.NAIVE_API_KEY}` } },
).then((r) => r.json());
// { available: true, price: 14.99, ... }
 
if (avail.available) {
  const buy = await client.domains.purchase("adastudio.com");
  if (isPendingApproval(buy)) {
    await client.approvals.approve(buy.approval_id);
    const approved = await client.approvals.get(buy.approval_id);
    // approved.result.checkout_url — open to complete the registration payment
  }
}

Once the domain shows status: "active" in client.domains.list(), provision the inbox and send the founder their first email from their own company address:

const domains = await client.domains.list();
const domainId = domains.domains.find((d) => d.domain === "adastudio.com")?.id;
 
const inbox = await client.email.createInbox({ local_part: "ada", domain_id: domainId });
 
await client.email.send({
  from_inbox: inbox.address,          // ada@adastudio.com
  to: "ada@foundermail.com",
  subject: "Your company is live",
  body: "Ada Studio LLC is filed. Articles + EIN attached in your dashboard.",
});

The founder now has a verified identity on record, a filed LLC, an EIN, a registered domain, and a working inbox on it — all provisioned by an agent, all gated where it counts.

The whole arc, one function

async function incorporate(founderId: string, brief: string) {
  const client = naive.forUser(founderId);
 
  // 1. Design (reversible, cheap) — OpenRouter model
  const plan = await designEntity(client, brief);
 
  // 2. KYC every founder (gated)
  const verificationId = await runKyc(client, plan.members);
 
  // 3. File the LLC: submit → pay → execute (both steps gated)
  const formationId = await fileEntity(client, verificationId, plan);
 
  // 4. Domain + inbox (purchase gated)
  await provisionDomainAndEmail(client, plan.domain);
 
  return { verificationId, formationId };
}

Every helper is the SDK calls above. The gates aren't your code — they're the API returning 202 and waiting for a human. Your product can't accidentally skip them.

Execution-time permission enforcement, explicitly

This is the part that can't be bolted on later. For an agent acting on a real tenant user, these actions return 202 pending_approval and execute only on human approval:

Actionaction_typeWhy it must be gated
Start KYCverification.startPulls a real person into an identity check
Create formation + checkoutformation.createCommits to a $249 charge
File the entityformation.submitIrreversible state filing; decrypts PII
Purchase a domaindomains.purchaseSpends money, registers an asset
  • The agent cannot raise its own ceiling — only the Account Kit (changed by you, not the agent) decides what's gated.
  • Human callers (your dashboard/session) and agent calls on your own default user are not subject to agent approval gating; only agent calls on real tenant users are frozen.
  • Approve, deny with a reason, or client.approvals.wait(id) to block until resolved — the full queue is a first-class resource alongside vault and logs.

What this costs

ItemCostNotes
KYC verificationFreePer the verification primitive
LLC formation$249Hosted checkout; covers filing, registered agent, EIN, documents
Domainmarket price (often ~$14.99)Hosted checkout via the registrar
LLM (entity design)exact OpenRouter cost, in creditsBilled at $0.50 = 1 credit
Email send1 credit$0.50
Approvals, vault, status readsFreeNew accounts start with 20 free credits

LLM, email, and other primitives bill in credits ($0.50 each); formation and domains are paid via their own hosted checkouts.

What Naïve handles for you

ConcernHow it works
Per-founder isolationOne tenant user per founder; naive.forUser(id) scopes every call; kits filter it
Policy as dataAn Account Kit declares enabled primitives + which actions need approval
KYCHosted identity verification, a single ready_for_formation signal, statuses tracked
PII protectionSSN/DOB/address held in an encrypted identity vault, decrypted only at filing time
IncorporationReal US LLC filing: NAICS codes, $249 checkout, EIN, Articles of Organization
DomainsSearch, price, purchase, auto-configured DNS + SPF/DKIM/DMARC
EmailIdentity-aware inboxes on the new domain — send and receive
Models300+ via OpenRouter behind one key, exact-cost billing, fallback + routing
Execution-time permissions202 pending_approval → human approves → API replays the frozen action

You write the product. Naïve owns identity → accounts → permissions.

Get started

  1. Grab a key at usenaive.ai/developers and npm install @usenaive-sdk/server.
  2. Define the kit — enable verification, formation, domains, email; keep the binding actions gated.
  3. Provision a founder with naive.users.create() and scope with naive.forUser(id).
  4. Design, KYC, file, register, send — and approve the gated steps as a human.
Frequently Asked Questions
Why can't I just build an incorporation product with a KYC vendor and a filing API directly?+
You can wire up a KYC vendor and a filing API, but you then own the hardest, most regulated parts yourself: storing and encrypting each founder's PII (SSN, DOB, address), proving that one founder's identity data and documents can never leak into another tenant's account, and putting a human in the loop on every legally binding action so an agent can't file the wrong entity or spend money unprompted. Naïve ships those as primitives — tenant users isolate each founder, the verification and formation primitives keep PII in an encrypted identity vault that's only decrypted at filing time, and the approvals queue freezes binding actions until a human approves. The LLM call that designs the company is the easy 10%; the identity, accounts, and execution-time permissions are the other 90%.
How does Naïve isolate one founder from another?+
Every primitive — verification, formation, domains, email, vault, llm — is scoped to a tenant user. You create one tenant user per founder with naive.users.create(), then make every call through naive.forUser(founderId). That scoped client can only see and act on that founder's resources, and each call is additionally filtered by the founder's Account Kit. There is no path for one founder's scoped client to reach another's KYC, entity, or formation documents.
Which actions require approval, and can I change that?+
By default, agent calls on real tenant users freeze for approval on: starting KYC (verification.start), creating the formation record (formation.create) and filing it (formation.submit), purchasing a domain (domains.purchase), plus issuing or topping up cards and connecting third-party services. When gated, the API returns 202 with status pending_approval and an approval_id instead of executing. A human approves or denies; on approval the API replays the frozen action. You override this per primitive in the Account Kit with requiresApproval, and agent calls on your own default user run un-gated.
Where does sensitive PII like SSN and DOB live?+
It is captured during the hosted KYC flow and stored in Naïve's encrypted identity vault — not in your database and not in the formation request body. The formation submit endpoint takes only a verification_id and business details; the SSN, DOB, and member addresses are decrypted from the encrypted identity vault during the execute (filing) step, after payment is confirmed, so the sensitive data never persists in your systems.
Which models can the agent use to design the company, and how is it billed?+
The llm primitive is a full wrapper over OpenRouter, so you get 300+ models across Anthropic, OpenAI, Google, Meta, and Mistral through one OpenAI-compatible endpoint — e.g. anthropic/claude-sonnet-4.6 or openai/gpt-5.2, with an optional models[] fallback chain and a provider{} routing object. The request/response bodies are exactly OpenRouter's. Naïve holds the OpenRouter key and bills OpenRouter's reported usage cost, converted to credits ($0.50 = 1 credit).
What does it cost to form a company through Naïve?+
LLC formation is $249, paid upfront through a hosted checkout when the agent files (covers the managed formation service, state filing fees, a registered agent, the EIN application, and all legal documents). KYC verification itself is free. A purchased domain is its market price (often around $14.99) via checkout. LLM, email sends, and other primitives are billed in credits at $0.50 each — an email send is 1 credit, and LLM is billed at the exact OpenRouter cost. New accounts start with 20 free credits.
Do my founders ever sign into Naïve?+
No. Tenant users are not auth subjects — they never sign in, and you manage them entirely through the API/SDK/CLI. The founder signs into your product; your backend uses one Naïve workspace key (nv_sk_...) and scopes calls per founder with naive.forUser(id). The only hosted surfaces the founder touches directly are the KYC link (to complete identity verification in-browser) and the checkout pages for the formation fee and domain purchase.
NT
Naïve Team

Building the autonomous company infrastructure.

Keep reading