Skip to main content

Account verification before creating payins

Authenticated collection requires kyc_status: approved for person accounts and approved KYB for companies; until then, POST /v1/payins (QR/card/checkout/bank_transfer/fintoc), collect OTP/collect, dedicated deposit accounts, stored-card charges and subscriptions return 403 verification_required. Existing public links, passive deposits, org-admin assignment and system-created instruments remain available; request POST /v1/me/verification/link, then retry after approval. Identity verification proves a person (KYC) or company (KYB) is who they claim to be, with real evidence: a complete form, document uploads validated by OCR and a video liveness check. It has two sides:
  1. Your own verification (onboarding) — mandatory: until approved, your account can only fund (payins, crypto deposits, incoming transfers) and read. Person ⇒ KYC; company ⇒ KYB.
  2. Verifying your customers (company accounts only) — generate hosted links or send data through the API to verify your own end customers, with a fixed fee per verification.

Your own verification (onboarding)

When you register, your account starts unverified (kyc_status: none) and can only fund and read. Any outgoing-money action (payouts, transfers, withdrawals, banking, cards) answers 403 verification_required until you are approved.
New self-service requests require a confirmed TOTP or registered passkey; see Profile & security for enrollment steps and retry. Open links are re-served before this gate; API-key automation remains exempt.
1

Request your verification link

201 response (if you already have an open link, the same one is returned with 200):
The kind derives from your account type: person ⇒ kyc, company ⇒ kyb. Onboarding is free for you.
2

Complete the wizard

Open the url: the hosted wizard guides you through the form, document uploads (identity, proof of residence; corporate documents for companies) and — for KYC — the camera liveness check.
3

Wait for review

Check your state any time:
The four optional submission fields are returned when available: pending_documents, changes_requested_comments, documents_received and documents_gate; rejection_reason is intentionally not exposed and a temporary core refresh failure may omit them.When compliance approves, your kyc_status becomes approved automatically, every service unlocks, and you receive the kyc_verification_status_changed webhook with self_onboarding: true.
Automatic decision engine: a fully clean application (documents read correctly, liveness passed, no sanctions or PEP matches, no risk signals) is approved in seconds without human intervention. Grey areas go to the operator’s human review queue. Severe AML sanctions (including a KYB party), prohibited jurisdictions, explicit document fraud/expiry and explicit provider rejection signals also go to the human queue with their reasons preserved; the engine no longer auto-rejects. The decision_source field of the status webhook ("auto" / "admin") tells you who decided.
The approval also backfills your account profile with the verified identity: display_name (person = first + last name; company = legal name), tax_id and country are taken from the verification and from then on are immutable via PATCH /v1/me (409 identity_locked) — the verified identity is the source of truth.
While waiting you can fund normally: payins, crypto deposits and incoming transfers work from day one. If verification is rejected (kyc_status: rejected), contact your operator about a new link.

Verifying your customers (company accounts only)

A verified company account can verify its own end customers. Each created verification bills the configured fixed fee (kyc_verification / kyb_verification; 0 = free), automatically refunded if creation fails. Person accounts receive 403 company_account_required. Your customer completes EVERYTHING in the white-label wizard: form, documents and liveness. You only generate the link and wait for the webhook.
  • external_customer_id (required): YOUR reference for the verified customer — echoed back on every webhook and query. Values equal to self or ending in :self are reserved for account onboarding and are rejected with 400 invalid_payload.
  • idempotency_key (required): a retry with the same key returns the original link and never double-charges.
  • country (KYB only): us, cl, ve, br, mx, co, pe, bo, py, ar or generic (with generic_country ISO alpha-2, e.g. "ES"). Individual KYC takes no country.
  • expires_in_days (optional, 1–30): omitted, the link never expires.
201 response:
Query and history (every POST has its GET):

Option B — Data through the API

If you already hold the customer’s data, create the verification directly (no wizard). The submission enters the same review queue:
Data-mode notes:
  • Countries in ISO alpha-3 (CHL, USA, VEN…); dates YYYY-MM-DD; id_type: passport | id_card | drivers_license.
  • KYB: body { external_customer_id, country?, business: {…}, ubos?, directors?, signers?, bank_info?, metadata? } on POST /v1/kyb/submissions.
  • No liveness is required at creation: the KYC submission carries liveness_pending: true; close it with a liveness link.
  • Re-sending with the same external_customer_id while the submission is open (pending_review, changes_requested, more_info_required) updates the same submission and does not charge again.
201 response:
Query and history:
The detail adds pending_documents, rejection_reason, changes_requested_comments, documents_received, documents_gate and aml_decision for KYB.

Documents through the API

Documents are optional at creation (if missing, compliance will request them via more_info_required). 3-step flow:
1

Presign

Categories — KYC: identity, proofOfResidence; KYB: legalPresence, ownershipStructure, controlStructure, companyDetails. Types: application/pdf, image/png, image/jpeg; 15 MB max; the upload URL expires in 15 minutes.
2

Upload

PUT the binary straight to upload_url with the same Content-Type.
3

Confirm

Confirming queues the OCR validation; the result arrives via the kyc_document_validated / kyb_document_validated webhook and is queryable with GET:
outcome: MATCH, REVIEW (manual review), NO_MATCH. Each item also exposes:
  • id: the validation identifier (used by the compliance team to review it).
  • effective_outcome: the outcome that currently governs — the admin’s manual resolution from the admin panel if one exists, otherwise the OCR engine outcome. In the submission detail (GET /v1/kyc/submissions/{id}) the documents_gate block summarizes whether ALL documents are already resolved (ok: true), with matched/total and the unresolved list of pending ones.
  • manual_review: present only when an admin manually resolved the validation from the admin panel. The account view carries outcome and reviewed_at (without the internal note or the reviewer).
Manually resolving a document validation is an admin-panel-only operation (CBPay Admin); it is not exposed through the public API. When the compliance team applies it, your account sees the updated result in effective_outcome and manual_review, plus the corresponding kyc_document_validated / kyb_document_validated webhook.
KYC submissions created through the API are born with liveness_pending: true (the liveness check is a browser camera flow). Generate a minimal hosted link for your customer to complete it:
  • Free (the service was billed when the submission was created). If an open link exists, the POST returns the same one; if the check already passed, 400 liveness_already_completed.
  • GET .../liveness_link returns the latest link and the current check state ({ "liveness": { "status", "outcome", "passed" } }).
  • On pass (outcome PASS or REVIEW): the submission clears liveness_pending and the kyc_liveness_completed webhook fires.

One verification for everything (reusable identity)

A customer’s approved verification is their single identity inside CBPay: you never re-type their data or re-upload their documents in any other product.
  • Third-party banking: POST /v1/banking/third-parties requires the verification_id of an approved verification of the third party. The type (INDIVIDUAL/COMPANY) comes from the kind (KYC ⇒ person, KYB ⇒ company), the data (name, email, address) auto-fills from the verified profile, and the already-validated documents are re-delivered automatically to the banking provider (documents_synced in the response). Details in Banking.
  • Cards for designated persons: POST /v1/cards with a person cardholder requires cardholder.verification_id of that person’s approved KYC. The cardholder’s identity and documents come from the verification; you only add the issuer-specific fields (occupation, salary_usd). Details in Cards.
  • Your own account: your approved onboarding is reused too — when creating your banking customer or your first card, missing data and documents auto-fill from your verification.
Explicit fields in your request always win over the autofill.
Without an approved verification of the third party, the banking registration and designated card issuance answer 422 verification_required. Verify first (hosted links or API data) and use the approved submission_id as verification_id.

Compliance report (KYB only)

For every KYB verification you can download the signed compliance report (PDF, evidence for your own auditors):
It is free (the service was billed when the verification was created).

Verification report (PDF + JSON)

Besides the processor’s report, every decided KYC or KYB submission has its verification report generated by the platform. It is the full file, not a summary: verified identity (person or company), declared economic profile, risk attestations, masked bank account, decision lifecycle, documents with their document validation, liveness check, related parties with their own screening (KYB) and the AML screening with the detail of every match — all with an integrity hash and a public verification code. Two formats (?format=pdf|json, default pdf) and three languages (?lang=en|es|zh, default en). It is free: it is a read of a verification you already paid for. Report sections:
Liveness has one entry per session, not one per subject. A gate session is the check that unlocked onboarding — it usually only carries the selfie. A media_recapture session is a later evidence capture and is the one that carries the full package (selfie + one frame per requested gesture
  • video). A media_recapture that ended outcome: "FAIL" still matters — it can be the only session with usable video — so iterate the whole liveness[] array instead of reading liveness[0]; the subject’s current verdict is always the gate session’s outcome. In a KYB, parties[].liveness (singular) stays for compatibility and always points to that party’s gate session, while parties[].liveness_sessions[] carries every session for that party.
How to read the PDF. The report opens with a navigable cover: an index of cards with icon, title and page number that are clickable and jump to their section. Every section carries its own icon and accent bar (the same visual language as the AML report), identity document and liveness photos keep their real aspect ratio, and no heading is ever left alone at the bottom of a page. Adverse media entries in the AML annex carry a “view source” chip and the public verification URL in the closing block is clickable — for safety only http and https links are embedded; any other scheme is dropped and the text stays unlinked.

For your third parties (company accounts)

A third party’s report is complete: the AML section includes the risk level, indicators and matches (names, sanctions lists, PEP, adverse media). You perform the due diligence on your customer and this report is your evidence. format=json response (shape summary — the PDF renders from the same model):
If the verification does not yet have a linked AML screening (older verifications), the first download runs it automatically at no cost. If the screening is unavailable at that moment, the report is generated anyway with "partial": ["aml_unavailable"] — the section is never fabricated.
In a company verification, every UBO, control person and signer in the file comes out as a parties[] entry with their full identity, their ownership, the documents and liveness check that belong to them, and their own AML screening with continuous monitoring enabled. The (source, index) pair is the party’s stable identity inside the file: it is what links their documents (uboIdentity:0) and what makes their screening always the same, no matter how many times you download the report. Screening the parties is free (it is a due-diligence duty, not a billable product) and stays monitored: if a UBO lands on a sanctions list after onboarding, the alert shows up on its own.
If a party does not have its screening yet at download time, the report is generated with "partial": ["party_aml_unavailable"] and the missing screening runs in the background: the next download already carries it.

For your own onboarding

In the report of your own verification the AML section is aggregated (aml_detail: false): you see the status per category — sanctions, pep and adverse_media as clear or under_review — without the match details. The same applies to the screening of your related parties: their AML sections are aggregated too. The rest of the file (identity, economic profile, documents, liveness, parties) is complete.

Public report verification

Every report carries a verification_code (printed on the PDF next to a QR). Anyone can confirm its authenticity without credentials:
The public page confirms only the type, the current decision status, the date and the issuing brand — never data about the subject. In a browser it responds with an HTML page carrying your brand.

Webhooks

Example payload (kyc_verification_status_changed):
Your own onboarding arrives with "self_onboarding": true instead of external_customer_id. Subscribe like every other event (see Webhooks).

Costs (configured by your operator, can be 0)

The charge comes out of your default settlement balance, is refunded if creation fails, and your own onboarding never bills. Re-sends of an open submission and liveness links do not charge again.

Errors

FAQ

Every account must approve identity verification before moving money out. Meanwhile you can fund and explore the API. Request your link with POST /v1/me/verification/link and complete it — approval unlocks everything.
An administrator may have deactivated or closed the account, or rejected its KYC/KYB. In those states, POST /v1/me/verification/link does not re-serve or create a self-service link. The administrator must restore the account or reopen verification first.
It bills when CREATING a third-party link or submission (live mode). Your onboarding, open-submission re-sends, liveness links, queries and documents are free; a failed creation is refunded automatically.
You will receive more_info_required with pending_documents. Use this page’s presign → upload → confirm flow; the submission then returns to review.
No: they complement each other. Verification proves identity with evidence; AML screening checks sanctions/PEP/adverse-media lists and can watch the identity continuously.
Yes — an approved verification is the reusable identity. Pass its submission_id as verification_id when registering a banking user or issuing a card for a designated person; data and documents auto-fill. See reusable identity.
If this is your own account (not a company verifying a third party), you get an automatic branded email when the decision is approved, rejected or changes-requested. It never includes detailed rejection reasons. Third parties do not receive this email; they receive the kyc_status_changed/kyb_status_changed webhook you integrated.
Last modified on September 20, 2026