POST /api/v1/inquiries, hand the subject a verification link, and read the result (ID scan,
selfie, database checks) back as an inquiry status or webhook. It does that job well, and the API
is mature. But it is also a separate vendor account:
- KYC lives behind its own API key, its own templates and themes, and its own dashboard — disconnected from wherever the verified person’s company, cards, phone, and secrets live.
- An inquiry’s
reference-idis a string tag back to a row in your user table. Persona knows the person passed KYC; it does not know that same person is the responsible party who then incorporates the company and owns its virtual card. - “This founder cleared KYC — now what is allowed to happen on their behalf?” is answered in Persona for verification, and in unrelated systems for formation, banking, and cards. The cleared identity has no shared accountability with the rest of the company’s footprint.
verification primitive gives the same capability —
hosted KYC for one or more company founders, with real-time status — but the person you verify is a
governed identity, not a tagged inquiry:
- The tenant user whose founders you KYC is the same identity that then forms the LLC, and owns its cards, phone number, email inboxes, and vault secrets.
- Verified PII (SSN, DOB, address) is pulled from the encrypted identity vault at formation time — it never lands in your database.
- When every member passes,
ready_for_formationflips totrue— KYC is structurally a step toward incorporation, not an isolated pass/fail you have to wire to a formation vendor yourself.
Persona is a trademark of its owner, used here for identification only. No endorsement or affiliation is implied.
Tested against: the Persona REST API (base
https://api.withpersona.com/api/v1, header
Persona-Version: 2025-10-27 — 2025-12-08 also valid — hosted flow at
https://inquiry.withpersona.com, docs snapshot June 2026), and the Naive Node SDK
@usenaive-sdk/server against the Naive API (base https://api.usenaive.ai/v1,
docs snapshot June 2026).Version notes:- Persona’s officially featured SDKs are client-side (persona-react / persona-web / mobile); the server-side integration is REST, which is what the “before” code below uses. There is no first-party server SDK to swap out.
- Persona verifies one subject per inquiry against a template you author (
itmpl_…). Naive verifies a set of company members in oneverification.startcall, and requiresownership_percentageto sum to 100, exactly onerole: "primary", and exactly oneis_responsible_party: true. This is formation-shaped — see gaps. - Persona is webhook-first (
inquiry.completed/inquiry.approved/inquiry.declined). Naive’s public webhook surface advertises onlyemail.receivedandapproval.resolvedtoday, so KYC status comes from the validation-tokencompleteendpoint (instant for the primary) + pollingGET /v1/verification/:id. TreatGET /v1/webhooks/event-typesas the source of truth before depending on any KYC event.
Concept map
| Persona | Naive | Notes |
|---|---|---|
Authorization: Bearer <persona_api_key> + Persona-Version header | new Naive({ apiKey }), then naive.forUser(id) | Server-side key in both; Naive scopes KYC to a tenant identity |
Inquiry Template (itmpl_…) you design + theme | Fixed, provider-managed KYC playbook | No template authoring or theming — see gaps |
POST /api/v1/inquiries — one subject | verification.start({ members: [...] }) — one or more founders | Naive verifies a set of members with ownership %, not one arbitrary subject |
data.attributes.fields prefill (name_first, name_last, birthdate) | member first_name, last_name, email, phone_number | Prefill / member identity |
reference-id — string tag to a row in your DB | the tenant user (forUser(id)) + member records | Identity is structural, not a string |
POST /inquiries/{id}/generate-one-time-link → meta.one-time-link (or inquiry.withpersona.com/verify?inquiry-id=…) | primary_link in the start response | Primary member’s hosted link returned inline |
| Email the link to the subject yourself | Secondary members emailed automatically | Naive sends secondary KYC links for you |
| Hosted Flow / Embedded Flow (iframe, client SDK) | Naive-hosted flow at verify.usenaive.ai | Hosted only — no embedded SDK config |
GET /api/v1/inquiries/{id} → data.attributes.status | GET /v1/verification/:id → per-member status + ready_for_formation | Status read; Naive adds the formation gate |
Client inquiry-session-token / validationToken | POST /v1/verification/members/:id/complete { validation_token } | Confirm a member instantly without waiting on a webhook |
| Regenerate a link / resume | POST /v1/verification/members/:id/resend | New session + emailed link |
Webhooks inquiry.completed / inquiry.approved / inquiry.declined (recommended) | Validation-token complete + poll; no advertised KYC webhook | The biggest difference — see gaps |
| post-inquiry approve / decline workflow | ready_for_formation (all members pass) | Naive’s “decision” is formation-readiness, not generic decisioning |
| API Keys + role scopes | Account Kit verification primitive + per-user assignment | KYC is execution-time policy on the identity — see gains |
| Reports (Watchlist/AML, adverse media, PEP) | — | No equivalent product |
| Cases (manual-review queue), Accounts, Documents API, Verifications API, Transactions | — | No equivalents — see gaps |
| Workflows / Dynamic Flow, redaction API | — | Not provided |
Before / after: the core path
The path that matters for founder KYC is start verification for the people who own the company, hand them a hosted link, then know when they’ve passed. Here it is on both platforms.- One subject vs. a set of members. Persona’s inquiry is one person tagged with a
reference-id. Naive’sstarttakes the founding members withownership_percentage(must sum to 100), oneprimary, and oneis_responsible_party. That structure is the formation contract — not an arbitrary KYC subject. - The link comes back inline. Persona is create inquiry → generate link → email it yourself.
Naive returns
primary_linkfromstartand emails secondary members automatically. - Status: poll + validation token, not a webhook. Persona steers you to
inquiry.completedwebhooks. Naive’s public webhook surface does not advertise a KYC event today — use the validation-tokencomplete(instant for the primary) and pollverification.getfor the rest. - The id is your identity, not a tag. In Persona,
reference-idpoints back to your DB. In Naive,forUser(id)is the identity that owns the founders’ KYC and the company they’re about to form, its cards, phone, and vault.
Knowing when a founder passed
- Persona’s recommended path is a webhook workflow: subscribe to
inquiry.completed, then approve in a post-inquiry workflow and act oninquiry.approved. - Naive’s reliable public path today is validation token + polling:
- Member statuses map roughly as: Persona
completed/approved→ Naivepass;declined/failed→fail;needs_review→pending_review;pending/created→in_progress/link_sent. See the full status list.
Minimal viable migration
The smallest swap that keeps a working founder-KYC flow running is just start + hand over the link + read status.Install the SDK and set your key
NAIVE_API_KEY (a server-side key from the dashboard).Swap inquiry creation for verification.start
Replace
POST /api/v1/inquiries (one subject + template) with
client.verification.start({ members: [...] }). Provide each founder’s name + email,
ownership_percentage (summing to 100), exactly one role: "primary", and exactly one
is_responsible_party: true. Drop the inquiry-template-id — the KYC playbook is managed for
you.Swap link delivery
Drop
generate-one-time-link + your own email send. Hand the returned primary_link to the
primary founder; secondary members are emailed automatically. Use
verification.resend if a link expires.Swap status tracking
Replace the
inquiry.completed webhook with the validation-token
complete call for the primary (instant) and poll
GET /v1/verification/:id for the rest. Gate on
ready_for_formation instead of an inquiry.approved workflow.Consolidate further once you’re on Naive
This is where the migration pays for itself. In Persona, an inquiry verifies a person and stops there — incorporation, banking, cards, and phone all live in separate systems keyed off areference-id string. On Naive, the verified founders are the identity that carries straight
into formation and every downstream primitive.
Gain #1 — one identity across primitives
- With Persona, KYC is an island: an
inquirytagged with areference-idthat you manually re-key into your formation, banking, card, and phone vendors. - With Naive,
naive.forUser(acme.id)is a single handle to KYC and formation and cards and phone and email and vault. The founders you verified are literally the responsible party the company is formed under — demonstrated byformation.submit({ verification_id }), which requires that the KYC passed.
Gain #2 — execution-time permission enforcement
- Whether an agent may start KYC at all is policy on the Account Kit, enforced at execution time — not a Persona API-key scope you manage separately.
- The agent’s code is identical either way —
client.verification.start({ ... }). Whether the call runs is decided at execution time:- An agent whose kit gates
verification.startgets202 { status: "pending_approval", approval_id }instead of starting KYC. A human approves it and KYC begins on replay — see the start endpoint note. - This is the same approval model that gates
formation.submit,cards, anddomains.purchase— one policy surface, not one per vendor.
- An agent whose kit gates
Gain #3 — unified accountability
- Every KYC start, completion, link resend, and the formation it gates lands in one per-user activity log — alongside the company’s card, phone, email, and vault events, not in a separate Persona dashboard:
- That is the question that is hard to answer when KYC lives in Persona, incorporation in Stripe Atlas, and cards in Stripe Issuing. Under Naive it is a single query.
What does not map yet
A migration guide that hides gaps is worse than none. Founder KYC (start → hosted link → status → formation gate) maps cleanly, but the following Persona capabilities have no direct equivalent on Naive’s verification primitive today. Check this list against your app before you commit.| Persona feature | Status on Naive | Workaround |
|---|---|---|
| General-purpose end-user KYC (verify arbitrary customers, gig workers, riders) | Not provided — Naive KYC is founder / responsible-party only | Keep Persona for end-user KYC; use Naive for the founders who form the company |
Custom inquiry templates / themes (itmpl_…, theme sets, Dynamic Flow) | Not configurable — fixed, provider-managed playbook | None — the flow (ID + selfie + SSN + address) is fixed |
| Embedded Flow (client SDK, your own iframe) | Hosted flow only (verify.usenaive.ai) | Redirect to the hosted primary_link |
Webhooks for KYC (inquiry.completed/approved/declined) | Not advertised today (email.received, approval.resolved only) | Validation-token complete (instant for primary) + poll verification.get; confirm via GET /v1/webhooks/event-types |
| Reports — Watchlist / AML, adverse media, PEP screening | No equivalent product | None — Naive KYC is identity verification, not sanctions/AML screening |
| Cases — manual-review queue + reviewer comments | Not provided (members can surface pending_review) | Manual review happens in the KYC provider; no Cases API |
| Accounts / Documents / Verifications / Transactions APIs | Not provided | None — Naive exposes verifications + members only |
post-inquiry decisioning (approved/declined via Workflows) | ready_for_formation gate (all members pass) | Use the formation gate, not a generic approve/decline workflow |
| Arbitrary subject model | Members require ownership_percentage summing to 100, one primary, one is_responsible_party | Structure is formation-shaped — not for non-founder subjects |
| Reusable verified profiles / one-time-link expiry tuning | resend regenerates a member link; no custom expiry control | Use verification.resend when a link expires |
Where to go next
verificationprimitive — full start → status → complete → resend lifecycle and theready_for_formationgateverificationAPI reference — typed request/response for every endpointverificationCLI — start, status, complete, resend- Formation — the incorporation the KYC feeds, keyed off
verification_id - Account Kits and Approvals — the policy model behind execution-time KYC governance
- Vault encryption — where verified PII lives so it never touches your database
- Tenant users — the identity that owns the founders’ KYC, the company, and every downstream primitive