C
Codex / Customer Onboarding API
1.0.0-proposal
SandboxOpenAPI specHosted flow ↗

Customer Onboarding API

A proposed extension to the Codex FX API: programmatic customer onboarding, replacing form-based intake. A customer approved here appears in GET /api/v1/customers and can transact immediately — same auth, same conventions, same error shapes as the rest of the platform.

13
Endpoints
1.0.0-proposal
Version
Live
codex-api.omniboarder.com

Design principles

Proofs, not documents

Requirements are proof categories (entity_formation, tax_registered, …). Any accepted local document, registry enrichment, or structured field can satisfy one. Adding a country adds registry rows, not endpoints.

Registry enrichment first

Where a public registry exists — Receita Federal (BR), FINTRAC (CA MSBs), Companies House (GB) — the proof is satisfied live at create time and never becomes a document request.

AI classification at upload

Every document is classified and parsed synchronously: type, entity name, dates, freshness. A stale or mismatched document is rejected in the response with the reason — not discovered at review.

Progressive by default

GATE proofs decide first movement (a capped approval); DEFERRED proofs collect while capped rails run. RFIs are generated from open proofs and auto-close as they satisfy.

Authentication

Bearer JWT in the Authorization header, exactly as the Codex FX API. The live prototype accepts any bearer token; requests without one return 401 UnauthorizedError.

Errors

Errors reuse the Codex FX shape — {"message": "…", "code": "…"} with a typed code.

StatusCodeWhen
400BadRequestErrorInvalid body, missing required field, unreadable document
401UnauthorizedErrorMissing or invalid bearer token
404NotFoundErrorUnknown customer, person, or route

Webhooks

Reuses the existing POST /api/v1/webhook-subscriber mechanism. New event types:

EventFires when
onboarding.customer.createdA customer enters DRAFT
onboarding.document.classifiedA document finishes AI classification (accepted or rejected)
onboarding.customer.approvedSubmit resolves to APPROVED_CAPPED — customer is transactable
onboarding.rfi.openedSubmit finds open gates and generates an RFI
onboarding.rfi.closedThe last open RFI item's proof is satisfied
Guide

Quickstart: 5 calls to a decision

Onboard a Brazilian PSP end to end. Every response below is from the live prototype — nothing is mocked.

1

Create the customer

Send the legal entity and its CNPJ. The registry check runs inside this call: an ATIVA record from Receita Federal satisfies entity_formation, entity_active_status and tax_registered before you have uploaded anything.

Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "legalEntityName": "Nu Pagamentos S.A.",
    "countryCode": "BR",
    "businessType": "COMPANY",
    "registrationNumber": "18.236.120/0001-58",
    "website": "https://nubank.com.br",
    "expectedMonthlyVolumeUsd": 5000000,
    "requestedRails": [
      "USDC_PAYOUT",
      "USD_WIRE"
    ],
    "accountPurpose": "Supplier payouts to US vendors settled in USDC",
    "idempotencyKey": "demo:nu:001"
  }'
Response
{
  "customerId": "cust_ob_he8wuf7n",
  "status": "DRAFT",
  "registryEnrichment": {
    "source": "Receita Federal via BrasilAPI",
    "status": "ATIVA",
    "active": true
  }
}
2

Add the people

UBOs, directors, signers. Sanctions/PEP screening runs synchronously — a hit returns screeningStatus: "HIT" with EDD questions in the same response. Mapping ≥75%% of ownership satisfies beneficial_owners_identified.

Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/persons \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "David Velez Osorno",
    "role": "UBO",
    "ownershipPercent": 80
  }'
3

Upload corporate documents

Base64 JPG/PNG/PDF. Each document is classified against the country's accepted types, parsed, freshness-checked, and the proofs it satisfies are applied — all in the response. A Contrato Social closes governance_framework_documented and ownership_structure_documented in one upload.

Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/documents \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "contrato_social.pdf",
    "contentBase64": "data:application/pdf;base64,JVBERi0…"
  }'
4

Verify the principal's identity

One call per UBO/signer. The extracted name must match the screened person; the document must show all four corners and be valid for 6+ months. When every principal verifies, principal_identity_verified closes.

Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/persons/per_x1k9c2ab/identity-document \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "passport.jpg",
    "contentBase64": "data:image/jpeg;base64,/9j/4AAQ…"
  }'
5

Submit for a decision

Attestation by an authorized signer, then an immediate decision. All gates satisfied → APPROVED_CAPPED with a facility sized to expected volume, and the customer appears in GET /api/v1/customers. Open gates → an RFI generated from exactly those proofs.

Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/submit \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "attestedBy": "David Velez Osorno"
  }'
Response
{
  "status": "RFI_REQUIRED",
  "openGates": [
    "governance_framework_documented",
    "…"
  ],
  "rfiId": "rfi_21q23sgd"
}
Guide

Documents & classification

Classification is not OCR-and-hope. Each jurisdiction has a registry of accepted document types with issuer, freshness rule, and the proofs each type carries. The model classifies to the most specific candidate using the first-page title and issuing authority as the authoritative signal — or returns other with its reasoning.

OutcomeWhat you get
accepted: trueProofs applied to the customer immediately; freshness.status: "FRESH" where a rule exists (e.g. Certidão Simplificada ≤ 90 days)
accepted: false, stalefreshness.status: "STALE" — right type, too old; re-request with the rule in hand
accepted: false, wrong typedetectedType: "other" plus reasoning naming what the document actually is
A document that mentions something does not prove it. A bank letter that mentions an address is not address verification; a contract that names directors is not a directors register. The classifier holds this line so analysts don't have to.
Guide

Working with RFIs

Nobody writes an RFI. When /submit finds open gates, the RFI is that list — each item references the proof it exists to satisfy. Because items are proofs rather than document names, the customer can answer with whichever accepted document (or registry fact) closes the proof.

Items auto-close as proofs satisfy: upload the missing Contrato Social and both governance items resolve without an analyst touching the case. When the last GATE item closes, re-submit for the decision. Deferred-phase items keep collecting after approval while capped rails run.

Onboarding Customers

Create an onboarding customer

POST/api/v1/onboarding/customers

Creates a customer in DRAFT and computes its requirement set (proofs) from country, business type and expected volume. Where a live public registry is available, enrichment runs inline: for BR, the CNPJ is verified against Receita Federal and entity_formation + entity_active_status are satisfied with zero documents.

The CNPJ was verified against Receita Federal inside this call — three gates satisfied with zero documents, and the legal name corrected to the registry's version.

Body

legalEntityNamestringrequired
countryCodestringrequired
businessTypeenumrequired
One of: COMPANY, FINANCIAL_INSTITUTION, SOLE_PROPRIETOR
registrationNumberstring | nulloptional
Company registration / tax number. BR: CNPJ (verified live). CA FIs: FINTRAC MSB number (verified live).
websitestring | nulloptional
expectedMonthlyVolumeUsdnumber | nulloptional
>= 10,000,000 activates the funds_flow_documented gate
requestedRailsarrayoptional
Items: USDC_PAYOUT, USDT_PAYOUT, USD_WIRE, EUR_SWIFT
accountPurposestring | nulloptional
idempotencyKeystringoptional
Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "legalEntityName": "Nu Pagamentos S.A.",
    "countryCode": "BR",
    "businessType": "COMPANY",
    "registrationNumber": "18.236.120/0001-58",
    "website": "https://nubank.com.br",
    "expectedMonthlyVolumeUsd": 5000000,
    "requestedRails": [
      "USDC_PAYOUT",
      "USD_WIRE"
    ],
    "accountPurpose": "Supplier payouts to US vendors settled in USDC",
    "idempotencyKey": "demo:nu:001"
  }'
Response · 201
{
  "customerId": "cust_ob_he8wuf7n",
  "legalEntityName": "NU PAGAMENTOS S.A. - INSTITUICAO DE PAGAMENTO",
  "countryCode": "BR",
  "businessType": "COMPANY",
  "registrationNumber": "18.236.120/0001-58",
  "status": "DRAFT",
  "riskTier": null,
  "facilityUsd": null,
  "registryEnrichment": {
    "source": "Receita Federal via BrasilAPI",
    "legalName": "NU PAGAMENTOS S.A. - INSTITUICAO DE PAGAMENTO",
    "status": "ATIVA",
    "active": true
  },
  "requirements": [
    {
      "proof": "entity_formation",
      "phase": "GATE",
      "status": "SATISFIED",
      "satisfiedBy": "registry_enrichment"
    },
    {
      "proof": "entity_active_status",
      "phase": "GATE",
      "status": "SATISFIED",
      "satisfiedBy": "registry_enrichment"
    },
    {
      "proof": "tax_registered",
      "phase": "GATE",
      "status": "SATISFIED",
      "satisfiedBy": "registry_enrichment"
    },
    {
      "proof": "governance_framework_documented",
      "phase": "GATE",
      "status": "OPEN",
      "satisfiedBy": null
    },
    {
      "proof": "…",
      "phase": "…",
      "status": "…",
      "satisfiedBy": null
    }
  ],
  "createdAt": "2026-08-10T11:07:41.000Z"
}
Onboarding Customers

List onboarding customers

GET/api/v1/onboarding/customers

Cursor pagination matching the Codex FX convention.

Query parameters

startingAfterstringoptional
endingBeforestringoptional
limitintegeroptional
Request
curl https://codex-api.omniboarder.com/api/v1/onboarding/customers \
  -H "Authorization: Bearer <token>"
Response · 200
{
  "data": [
    {
      "customerId": "cust_ob_he8wuf7n",
      "legalEntityName": "NU PAGAMENTOS S.A. - INSTITUICAO DE PAGAMENTO",
      "countryCode": "BR",
      "status": "RFI_REQUIRED",
      "createdAt": "2026-08-10T11:07:41.000Z"
    }
  ],
  "hasMore": false
}
Onboarding Customers

Get an onboarding customer

GET/api/v1/onboarding/customers/{customerId}

Returns the customer with its live requirement set.

Path parameters

customerIdstringrequired
Request
curl https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n \
  -H "Authorization: Bearer <token>"
Response · 200
{
  "customerId": "cust_ob_he8wuf7n",
  "status": "RFI_REQUIRED",
  "riskTier": "LOW",
  "requirements": [
    {
      "proof": "principal_identity_verified",
      "phase": "GATE",
      "status": "OPEN",
      "satisfiedBy": null
    }
  ],
  "createdAt": "2026-08-10T11:07:41.000Z"
}
Onboarding Customers

Update an onboarding customer

PATCH/api/v1/onboarding/customers/{customerId}

Updates mutable fields; requirements are recomputed.

Structured fields satisfy proofs too — an attested operating address closes operating_address_confirmed without a utility bill.

Path parameters

customerIdstringrequired

Body

websitestringoptional
expectedMonthlyVolumeUsdnumberoptional
accountPurposestringoptional
operatingAddressstringoptional
Request
curl -X PATCH https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "operatingAddress": "Rua Capote Valente 39, Sao Paulo, SP"
  }'
Response · 200
{
  "customerId": "cust_ob_he8wuf7n",
  "status": "DRAFT",
  "requirements": [
    {
      "proof": "operating_address_confirmed",
      "phase": "GATE",
      "status": "SATISFIED",
      "satisfiedBy": "attested"
    }
  ]
}
Onboarding Customers

Get requirements

GET/api/v1/onboarding/customers/{customerId}/requirements

The smart union of Codex's compliance floor and the customer's rails/volume/entity-type, expressed as proofs. GATE items block first movement; DEFERRED items collect while capped rails run.

Path parameters

customerIdstringrequired
Request
curl https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/requirements \
  -H "Authorization: Bearer <token>"
Response · 200
{
  "data": [
    {
      "proof": "entity_formation",
      "phase": "GATE",
      "status": "SATISFIED",
      "satisfiedBy": "registry_enrichment"
    },
    {
      "proof": "governance_framework_documented",
      "phase": "GATE",
      "status": "OPEN",
      "satisfiedBy": null
    },
    {
      "proof": "sof_verified",
      "phase": "DEFERRED",
      "status": "OPEN",
      "satisfiedBy": null
    }
  ],
  "hasMore": false
}
Persons

Add a person (UBO / director / signer)

POST/api/v1/onboarding/customers/{customerId}/persons

Sanctions/PEP screening runs synchronously on entry; a hit returns screeningStatus: HIT with EDD questions rather than silently queueing.

Screening runs before the response returns. A list hit comes back as screeningStatus: "HIT" with the EDD questions to resolve it — in the flow, not three days later.

Path parameters

customerIdstringrequired

Body

fullNamestringrequired
roleenumrequired
One of: UBO, DIRECTOR, SIGNER
ownershipPercentnumber | nulloptional
Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/persons \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "David Velez Osorno",
    "role": "UBO",
    "ownershipPercent": 80
  }'
Response · 201
{
  "personId": "per_x1k9c2ab",
  "fullName": "David Velez Osorno",
  "role": "UBO",
  "ownershipPercent": 80,
  "screeningStatus": "CLEAR",
  "eddQuestions": []
}
Persons

List persons

GET/api/v1/onboarding/customers/{customerId}/persons

Path parameters

customerIdstringrequired
Request
curl https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/persons \
  -H "Authorization: Bearer <token>"
Response · 200
{
  "data": [
    {
      "personId": "per_x1k9c2ab",
      "fullName": "David Velez Osorno",
      "role": "UBO",
      "screeningStatus": "CLEAR"
    }
  ],
  "hasMore": false
}
Documents

Upload a document (AI-classified synchronously)

POST/api/v1/onboarding/customers/{customerId}/documents

Accepts JPG/PNG/PDF as base64. The document is classified against the jurisdiction's accepted types, fields are extracted, per-document freshness rules are enforced, and the proofs it satisfies are applied — all in the response. A document that is stale, unreadable, or not an accepted type returns accepted: false with the reason.

A real rejection from the live system: a UAE trade licence uploaded to a Brazilian customer. The model names the actual issuer and explains why it doesn't qualify — a document that mentions something does not prove it.

Path parameters

customerIdstringrequired

Body

fileNamestringrequired
contentBase64stringrequired
Raw base64 or data: URL. JPG, PNG, or PDF (PDF read natively).
declaredTypestring | nulloptional
What the customer believes this is — treated as an authoritative signal, per the classification policy
Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/documents \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "trade_licence.png",
    "contentBase64": "data:image/png;base64,iVBORw0KG…"
  }'
Response · 201
{
  "documentId": "doc_6i6mp3nb",
  "fileName": "trade_licence.png",
  "detectedType": "other",
  "confidence": 0.97,
  "reasoning": "This is a UAE Trade License issued by Fujairah Creative City - Media Free Zone, not a Brazilian corporate document. None of the candidate types apply.",
  "entityName": "ATLANTIC GLOBAL FZE",
  "issueDate": null,
  "freshness": {
    "ruleDays": null,
    "status": "NOT_APPLICABLE"
  },
  "proofsSatisfied": [],
  "accepted": false
}
Documents

List documents

GET/api/v1/onboarding/customers/{customerId}/documents

Path parameters

customerIdstringrequired
Request
curl https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/documents \
  -H "Authorization: Bearer <token>"
Response · 200
{
  "data": [
    {
      "documentId": "doc_6i6mp3nb",
      "detectedType": "other",
      "accepted": false
    }
  ],
  "hasMore": false
}
Documents

Upload a person's identity document

POST/api/v1/onboarding/customers/{customerId}/persons/{personId}/identity-document

Passport / national ID / licence. Extracts holder details, enforces the identity standards (all four corners visible, not expiring within 6 months) and name-matches against the screened person.

Enforced at the boundary: all four corners visible, not expiring within 6 months, and the extracted name must match the screened person. Failures return verified: false with each issue named.

Path parameters

customerIdstringrequired
personIdstringrequired

Body

fileNamestringrequired
contentBase64stringrequired
Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/persons/per_x1k9c2ab/identity-document \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "passport.jpg",
    "contentBase64": "data:image/jpeg;base64,/9j/4AAQ…"
  }'
Response · 201
{
  "personId": "per_x1k9c2ab",
  "verified": true,
  "docType": "passport",
  "holderName": "DAVID VELEZ OSORNO",
  "expiryDate": "2031-04-22",
  "confidence": 0.98,
  "issues": []
}
Decisions

Submit for decision

POST/api/v1/onboarding/customers/{customerId}/submit

Requires attestation. If every GATE proof is satisfied the customer is approved with a capped facility (APPROVED_CAPPED) sized to expected volume and appears immediately in GET /api/v1/customers; open gates instead open an RFI (RFI_REQUIRED) whose items auto-close as proofs satisfy.

Two outcomes, both immediate. All gates satisfied → APPROVED_CAPPED with a facility sized to expected volume, visible in GET /api/v1/customers at once. Open gates → an RFI generated from exactly those proofs.

Path parameters

customerIdstringrequired

Body

attestedBystringrequired
Full name of the authorized signer
Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/submit \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "attestedBy": "David Velez Osorno"
  }'
Response · 200
{
  "customerId": "cust_ob_he8wuf7n",
  "status": "RFI_REQUIRED",
  "riskTier": "LOW",
  "facilityUsd": null,
  "openGates": [
    "governance_framework_documented",
    "ownership_structure_documented",
    "directors_list_documented",
    "principal_identity_verified"
  ],
  "rfiId": "rfi_21q23sgd"
}
RFIs

List RFIs

GET/api/v1/onboarding/customers/{customerId}/rfis

RFIs are generated from open proofs at submit; items reference the proof they exist to satisfy.

Path parameters

customerIdstringrequired
Request
curl https://codex-api.omniboarder.com/api/v1/onboarding/customers/cust_ob_he8wuf7n/rfis \
  -H "Authorization: Bearer <token>"
Response · 200
{
  "data": [
    {
      "rfiId": "rfi_21q23sgd",
      "status": "OPEN",
      "createdAt": "2026-08-10T11:14:02.000Z",
      "items": [
        {
          "proof": "governance_framework_documented",
          "status": "OPEN"
        },
        {
          "proof": "ownership_structure_documented",
          "status": "OPEN"
        },
        {
          "proof": "directors_list_documented",
          "status": "OPEN"
        },
        {
          "proof": "principal_identity_verified",
          "status": "OPEN"
        }
      ]
    }
  ],
  "hasMore": false
}
Onboarding Links

Create a hosted onboarding link

POST/api/v1/onboarding/links

Returns a magic link to the hosted onboarding flow (no portal account needed) for customers who won't integrate the API.

Body

legalEntityNamestring | nulloptional
countryCodestring | nulloptional
Request
curl -X POST https://codex-api.omniboarder.com/api/v1/onboarding/links \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "legalEntityName": "TappaPay Ltd",
    "countryCode": "CM"
  }'
Response · 201
{
  "linkId": "oblnk_wxdtbl9d",
  "url": "https://codex-ui.omniboarder.com/?link=oblnk_wxdtbl9d",
  "expiresAt": "2026-08-17T11:20:54.553Z"
}

Glossary

TermMeaning
ProofThe atomic unit of a requirement — a fact that must be established (entity_formation, tax_registered). Satisfied by a document, a registry enrichment, or a structured field.
GateA proof in the GATE phase. Open gates block a decision; all gates satisfied means an immediate capped approval.
Deferred proofA proof collected after first movement, while capped rails run — e.g. sof_verified, financial_position. Never blocks the first approval.
Smart unionThe requirement set for a customer: Codex's compliance floor ∪ whatever their rails, volume and entity type additionally demand. Computed at create, recomputed on every change.
Registry enrichmentA live public-registry lookup at create time (Receita Federal, FINTRAC, Companies House). Proofs it satisfies never become document requests.
ClassificationThe synchronous AI step on every upload: document type, entity name, dates, freshness — with reasoning, so a rejection explains itself.
Freshness ruleA per-document-type maximum age (Certidão Simplificada: 90 days). Stale documents are rejected at upload, not at review.
ScreeningSanctions/PEP check that runs when a person is added. HIT returns EDD questions inline.
AttestationThe authorized signer's declaration at submit — the legal anchor for the application.
APPROVED_CAPPEDThe progressive-onboarding decision: transactable now, with a facility sized to expected volume, while deferred proofs collect.
RFIRequest for information — generated from open proofs at submit, items auto-close as proofs satisfy.
FacilityThe prefunding credit limit granted on approval; visible to the prefunding desk immediately.
Canonical recordThe single customer record shared by the API, hosted flow, compliance console, RFI manager and prefunding desk. Everything reads and writes the same spine.
UBOUltimate beneficial owner. Principals (UBOs and signers) must pass identity verification for principal_identity_verified to close.
Live

Sandbox

Fires real requests at codex-api.omniboarder.com — the same instance behind these docs. Any bearer token works. Create a customer and the sandbox remembers its customerId (and personId) for the follow-up calls, so you can walk the whole quickstart from this panel.

Request body
Response
// pick an endpoint and hit Send