Onboarding
Four-step wizard from KYB documents to sealed genesis receipt. Durable mid-flow state; replay-safe transitions.
Onboarding is the multi-step flow that brings a new entity onto the gateway. Each step persists durably so an operator can pause Monday, resume Wednesday, submit Friday — no lost work.
The four steps
draft ─upload docs→ docs_uploaded ─select pack→ policy_selected
│
▼
submitted
│ │
▼ ▼
approved rejectedFlow
1 · Create
POST /v1/onboarding/applications
{
"entity_name": "Acme Corp LLC",
"entity_type": "llc",
"contact_email": "ops@acme.example",
"created_by": "op_1"
}Response includes application_id, freshly-minted entity_id, and required_docs — the document kinds that must be uploaded before submit.
2 · Upload documents
Per-entity-type required kinds:
| entity_type | required docs |
|---|---|
| llc | articles_of_incorporation, beneficial_ownership, ein_letter, operating_agreement, government_id |
| ccorp / scorp | articles_of_incorporation, beneficial_ownership, ein_letter, government_id |
| partnership | articles_of_incorporation, beneficial_ownership, ein_letter, operating_agreement, government_id |
| sole_prop | government_id, proof_of_address, ein_letter |
POST /v1/onboarding/applications/$ID/documents
{
"kind": "articles_of_incorporation",
"content_hash": "sha256:...",
"filename": "articles.pdf",
"uploaded_by": "op_1"
}The gateway stores only the hash — you keep the blob. Managed mode mirrors the hash to Convex; self-host keeps it local.
3 · Select policy pack
POST /v1/onboarding/applications/$ID/policy
{
"policy_pack_id": "ap_strict_v1",
"actor": "op_1"
}The gateway verifies the pack exists in storage and snapshots its sha256 into the application row.
4 · Review + submit
# Readiness check — pure validator, no state change:
GET /v1/onboarding/applications/$ID/review
# Seal genesis + submit:
POST /v1/onboarding/applications/$ID/submit
{ "submitted_by": "op_1" }Submit atomically:
- Re-runs the readiness check (no racing upload-rollback can slip through)
- Appends a genesis decision receipt to the entity's chain with
tool: veto_onboarding_submit,decision: allow, bound to the pack's sha256 - Transitions the application to
submitted
Why a sealed genesis receipt
The per-entity receipt chain is only trustworthy if you can prove where it started. The genesis receipt:
- Anchors the chain to a known point (
prev_receipt_hash = sha256:e3b0..., SHA-256 of empty) - Binds the exact policy pack sha256 active at birth
- Hashes the onboarding snapshot (entity name, type, policy pack, document hashes) into
args_hash
An auditor can:
- Pull the application row (or the genesis receipt alone)
- Recompute
args_hashfrom the canonical snapshot - Verify the full chain with
verifyReceiptChain()— any tampering after submit breaks verification instantly
Terminal decisions
POST /v1/onboarding/applications/$ID/decision
{
"decision": "approved", # or "rejected"
"actor": "op_compliance",
"reason": "reviewed 2026-04-25"
}Managed mode forwards this to Meow's partner API. Self-host is free-form operator input — you're the compliance officer.
Replay safety
Every transition is an atomic CAS on the current state:
- Submit twice? Second call returns 409
workflow_invalid_transition - Upload docs after submit? 409
- Select policy before docs? 409
- Pick a nonexistent pack? 404
policy_pack_not_found
The CAS runs inside BEGIN IMMEDIATE so multi-operator concurrency is safe: two admins hitting submit at the same moment have one succeed and one get a clean 409.