Introducing KYC: identity verification for your agent, done once
Verify the human behind an agent once, then let that identity hold domains, inboxes, phone numbers, cards and a company. Here is what verification unlocks, and what it never exposes to the agent.
TL;DR
- KYC is the verification step of the Identity primitive: a hosted identity check a real person completes once, attached to one identity on Naïve.
- An identity is verified once. Everything it holds afterwards, web domains, inboxes, phone numbers, connections, a vault, a company, hangs off that one verified persona.
- Agents hold identities through a grant, not a field. One agent can act as several personas, one persona can be shared by several agents, and a session names which one it acts as.
- The agent sees a name, a description and the addresses it owns. It never sees a document, and the identity object has no field that could carry one.
- Every action an identity takes passes the policy layer before any external request is made, and fails closed if enforcement is unavailable.
Every real-world action an agent takes eventually meets a counterparty who asks the same question: who is actually behind this? A registrar wants a registrant. A carrier wants a registered sender before it lets a number text anyone. A state wants a named organizer on the filing. A card issuer wants a cardholder. None of them accept "an agent" as the answer.
KYC is how an agent on Naïve gets one. It is the verification step of the Identity primitive: a real person completes a hosted identity check once, and the result is attached to an identity, the named persona the agent acts as. From then on, the primitives that need a real person behind them, a company first of all, build on that identity instead of asking again.
The identity model in one paragraph
An identity is a named, described persona an agent acts as in the world. It has a name and a description so the model knows who it is being, and it owns concrete endpoints rather than booleans: billing@acme.com is a different identity from support@acme.com, and a phone number belongs to one identity, not to the org.
The pieces an identity bundles today:
| Piece | What it is |
|---|---|
| Web domains | Verified domains the identity sends and receives on, provisioned, brought-your-own, or purchased |
| Real inboxes on a verified domain | |
| Phone | Provisioned numbers with carrier-registered messaging |
| Connections | Third-party apps the identity has authorized over OAuth |
| Vault | A write-only credential store injected at the network boundary |
Verification is the piece that makes the persona a real principal. The person behind it is checked once; the company that follows is gated on that check, and the domain, inbox and number are issued to the same persona. On the site this is the Identity & legal group: KYC, LLC, Domains, Mail, Phone, plus Cards on the money side, spend-capped virtual cards planned to draw on the identity's wallet.
Verified once, held many times
The relationship between agents and identities is many-to-many, and that is the reason verification only has to happen once.
- One agent can hold several identities, an inbound support persona and an outbound sales persona for example, and choose which to act as per task.
- One identity can be shared by several agents, a
billing@acme.compersona handled by a triage agent and a refunds agent.
Agents reference identities; identities reference no agents. The grant hangs off the agent, and it is idempotent: attaching a persona that is already attached succeeds with attached: true and created: false, so a provisioning script can re-run safely. Detaching removes the grant and leaves the identity, and its verification, untouched.
vetta identity create --name "Acme Billing" \
--description "Handles invoices and refunds for Acme"
vetta identity attach --agent Refunder --identity "Acme Billing"
vetta identity attach --agent Triage --identity "Acme Billing"The same thing from TypeScript:
import { randomUUID } from "node:crypto";
import { createClient } from "@usenaive-sdk/vetta";
const vetta = createClient({
baseUrl: "https://api.vetta.sh",
apiKey: process.env.VETTA_API_KEY!,
fetch: globalThis.fetch,
idempotencyKey: () => randomUUID(),
});
const billing = await vetta.identities.create({
name: "Acme Billing",
description: "Handles invoices and refunds for Acme",
});
// refunder and triage are the agents from vetta.agents.create(...)
await vetta.identities.attach(refunder.id, billing.id);
await vetta.identities.attach(triage.id, billing.id);When an agent holds more than one identity, the acting persona is named on the run, not baked into the agent. Selecting one mints no new agent version:
vetta session create --agent Concierge --identity "Acme Billing"For unattended work the persona goes on the deployment instead, and it is resolved and grant-checked when the schedule is written, so a bad reference fails while you are watching rather than at 03:00.
What verification unlocks
A verified identity is the principal the rest of the Identity primitive builds on.
A real company. Formation takes an agent from KYC to EIN in one funnel: a real US company, with the verified person as the human behind the filing. We wrote about it in Introducing Formation.
A domain, an inbox, a number. Domains gives the identity verified web domains it can send and receive on. Mail puts real inboxes on those domains. Phone provisions numbers with carrier registration, which is exactly the kind of step that wants a named, verified party behind it. See Introducing Domain for the domain side.
Authorized apps. Connections let the identity authorize third-party apps over OAuth and act in them as itself: each connected account is bound to one identity, and the agent never handles the token. Which apps, and which tools inside them, is scoped by the identity's policy.
Secrets it can use but never read. The vault is scoped to the identity. A credential is written once, referenced by name, and swapped in at the network boundary only for the host it is bound to. No route returns a value.
Cards. Cards are virtual cards with a hard spend cap the agent cannot raise. They draw on the agent wallet, which is carried by the identity and gated by the same policy as its email, phone and connections. The wallet is still marked coming soon in the docs, so treat its shape as direction rather than contract.
None of these primitives run a KYC check of their own. They read the identity.
What it never exposes to the agent
Verification produces exactly one thing the runtime cares about: a persona that is known to have a real person behind it. It does not produce anything the agent can read.
When a session acts as an identity, the persona docs are precise about what is put in front of the model: the persona's name, description and the addresses it owns, for that run only. That is the whole surface. There is no document, no date of birth, no photograph in the context window, because none of it is part of the identity object.
You can check this against the identity object in the API. Its fields are id, name, description, emails, phones, domains, connections, metadata, created_at and updated_at. There is no field that could carry a document, and nothing in the reference returns one. The same design shows up in the vault: secret fields are write-only, reads return metadata only, and the model, the transcript and your logs only ever see a placeholder. We treat verification material the same way we treat a credential. The agent gets the effect of it, never the substance.
This matters for the obvious reason, that a prompt-injected agent cannot hand over what it was never given. It also matters for the operator. Verification is a hosted step a human completes, and the material stays on that side of the boundary.
Every action is still gated
Verification says who the persona is. It does not say what the persona may do. That is the job of the policy layer, and it runs on every identity action before any external request is made.
A policy scopes which primitives the identity may use (email, phone, connections, vault, domains), which connections it may authorize and which tools it may call inside them, and which actions need a human to say yes. Some are approval-gated out of the box: connecting a new third-party app, provisioning a phone number, and purchasing a domain. When an action needs approval the session emits a confirmation event and holds at storage cost until you respond.
An approval rule can also carry a threshold: an action whose quoted cost exceeds it forces approval even if the primitive is otherwise allowed. This composes with the agent's mandatory USD budget. The budget caps total spend; the policy governs which actions may spend at all. Enforcement is at the tool-call boundary and fails closed, so an identity can never reach a system or spend money the policy did not grant, verified or not.
Why the check is not a tool the agent calls
The old way to add identity checks to a product was to integrate a verification vendor directly: build the redirect, store the result, own the PII handling, and repeat it in every product that needs it. That is a lot of surface area for a fact that is true exactly once per person.
Naïve is white-labeled end to end. There is no vendor name in the API, the CLI, the SDK or the dashboard, and the check is a hosted step for the person behind the identity, not a tool call the agent makes. A human completes it, the identity becomes the verified principal, and the agent inherits the standing without ever touching the flow. The agent's job is to act as the persona; ours is to make sure the persona is real.
Get started
Create an identity, grant it to an agent, and give it an inbox to work from:
vetta identity create --name "Ava Sales" --description "Outbound SDR persona for the growth team"
vetta identity email provision --identity ava --address ava@acme-mail.com
vetta identity attach --agent Concierge --identity ava
vetta session create --agent Concierge --identity avaThen read the reference: Identity overview, Personas, Identity policies, Credential vault, and the Identities API. The KYC primitive itself lives at /primitives/verification, and the company it gates at /primitives/formation.
FAQ
- What is KYC on Naïve?
- KYC is the verification step of the Identity primitive. A real person completes a hosted identity check once, and the result is attached to an identity, the named persona an agent acts as. From then on the identity can hold web domains, inboxes, phone numbers, connections, a credential vault and, through Formation, a real company.
- Does the agent get access to the verification documents?
- No. When a session acts as an identity, the model is given the persona's name, description and the addresses it owns, nothing else. The identity object exposed by the API carries no document fields, and nothing in the API reference returns verification material.
- Do I have to re-verify for every agent?
- No. Verification attaches to the identity, not the agent. Agents hold identities through a grant, so a verified persona can be attached to as many agents as you like, and detached without touching the verification.
- How does KYC relate to Formation?
- Formation forms a real US company for an agent, and a verified human principal is the gate before the filing. The same verified identity is the one that later owns the company's domain, inbox and phone number, so you verify once and build the rest on top.
- Which vendor runs the check?
- Naïve is white-labeled end to end. The check is hosted by Naïve and the result lives on your identity; you never integrate a third-party verification API, and no vendor name appears in the product.