1. Onboard a verified customer
Business and individual customers follow the same sequence: create the customer record, upload documents, capture terms acceptance, then submit onboarding. Only the customer payload and the documents differ. Follow the guide for your customer type, then continue with Capture terms acceptance and Submit onboarding, which are the same for both.
The examples carry only the fields this corridor requires; every other field is optional.
Onboard the customer (BUSINESS)
A business sends a kyb block with its registration details, risk profile, and at least one control person.
Create the customer record
POST /v1/payments/customers
curl -X POST https://api-staging.checker.finance/v1/payments/customers \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"client_reference": "acme-roasters-customer-001",
"legal_name": "Acme Coffee Roasters LLC",
"customer_type": "BUSINESS",
"country": "US",
"email": "[email protected]",
"phone": "+14155559876",
"address": {
"line1": "500 Coffee Ave",
"city": "San Francisco",
"state": "CA",
"postal_code": "94107",
"country": "US"
},
"kyb": {
"attestation_ref": "KYB-2026-001",
"business_entity_type": "LIMITED_LIABILITY_COMPANY",
"business_industry": "FOOD_AND_BEVERAGE",
"business_description": "Specialty coffee roasting, wholesale and ecommerce",
"formation_date": "2021-03-10",
"tax_id_type": "EIN",
"tax_id_number": "981234567",
"operating_address": {
"line1": "500 Coffee Ave",
"city": "San Francisco",
"state": "CA",
"postal_code": "94107",
"country": "US"
},
"risk_profile": {
"account_purpose": "ECOMMERCE_PAYMENTS",
"source_of_funds": ["SALES_OF_GOODS_AND_SERVICES"],
"source_of_wealth": ["BUSINESS_DIVIDENDS_OR_PROFITS"],
"ownership_structure": "PRIVATELY_OWNED",
"estimated_annual_revenue": "1M_TO_10M",
"estimated_monthly_volume": "100K_TO_1M"
},
"control_persons": [
{
"first_name": "Dana",
"last_name": "Okafor",
"nationality": "US",
"email": "[email protected]",
"date_of_birth": "1985-06-15",
"residential_address": {
"line1": "789 Oak Avenue",
"city": "San Francisco",
"state": "CA",
"postal_code": "94104",
"country": "US"
},
"tax_id_type": "SSN",
"tax_id_number": "123456789",
"tax_residence_country": "US",
"identification_type": "PASSPORT",
"identification_number": "A12345678",
"identification_issuing_country": "US",
"is_control_person": true
}
]
}
}'Response — 201 Created:
{
"customer_id": "cus_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"client_reference": "acme-roasters-customer-001",
"legal_name": "Acme Coffee Roasters LLC",
"customer_type": "BUSINESS",
"country": "US",
"status": "PENDING",
"kyb": {
"attestation_ref": "KYB-2026-001",
"business_entity_type": "LIMITED_LIABILITY_COMPANY",
"control_persons": [
{ "control_person_id": "cpn_01ARZ3NDEKTSV4RRFFQ69G5FAV", "first_name": "Dana", "last_name": "Okafor" }
]
},
"created_at": "2026-09-23T15:04:05Z",
"updated_at": "2026-09-23T15:04:05Z"
}Store the customer_id and each control_person_id.
Notes:
client_referenceis your own identifier for the customer and is echoed back in responses.- Every
kybfield shown is required on this corridor. Onboarding without one returns422 MISSING_KYB_FIELDSwithdetails.missingnaming the gaps — field paths such askyb.business_industry, document paths such asfiles.BUSINESS_REGISTRATION, andtermsorterms.<AGREEMENT>for outstanding agreements. - Each control person needs every field shown: name,
email,nationality,date_of_birth(at least 18 years old),residential_address, tax details, and identification. At least one must haveis_control_person: true. - Add owners as further
control_personsentries withis_beneficial_owner: trueand anownership_percentage, which is required wheneveris_beneficial_owneristrue.is_authorized_signerandis_directordefault tofalse. stateis required on every US address, as a USPS code or full state name.- Personal and tax identifiers are write-only and never returned.
Upload supporting documents
Upload the entity's documents, then an identity document and proof of address for each control person. file_type and control_person_id are multipart form fields.
POST /v1/payments/customers/{customer_id}/upload
curl -X POST https://api-staging.checker.finance/v1/payments/customers/cus_01ARZ3NDEKTSV4RRFFQ69G5FAV/upload \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@certificate_of_incorporation.pdf" \
-F "file_type=BUSINESS_REGISTRATION"curl -X POST https://api-staging.checker.finance/v1/payments/customers/cus_01ARZ3NDEKTSV4RRFFQ69G5FAV/upload \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@dana_passport.pdf" \
-F "file_type=IDENTITY_DOCUMENT" \
-F "control_person_id=cpn_01ARZ3NDEKTSV4RRFFQ69G5FAV"The entity documents this corridor requires depend on kyb.business_entity_type:
business_entity_type | Required entity file types |
|---|---|
LIMITED_LIABILITY_COMPANY | BUSINESS_REGISTRATION, ARTICLE_OF_ASSOCIATION, OPERATING_AGREEMENT, OWNERSHIP_STRUCTURE, AUTHORIZED_REPRESENTATIVE_LIST, PROOF_OF_BUSINESS_ADDRESS |
CORPORATION, PUBLICLY_TRADED_COMPANY | BUSINESS_REGISTRATION, ARTICLE_OF_ASSOCIATION, OWNERSHIP_STRUCTURE, AUTHORIZED_REPRESENTATIVE_LIST, PROOF_OF_BUSINESS_ADDRESS |
GENERAL_PARTNERSHIP, LIMITED_LIABILITY_PARTNERSHIP | BUSINESS_REGISTRATION, OPERATING_AGREEMENT (the partnership agreement), OWNERSHIP_STRUCTURE, AUTHORIZED_REPRESENTATIVE_LIST, PROOF_OF_BUSINESS_ADDRESS |
SOLE_PROPRIETORSHIP | BUSINESS_LICENSE, TAX_FILINGS, PROOF_OF_BUSINESS_ADDRESS |
What each file type should contain:
| File type | Document |
|---|---|
BUSINESS_REGISTRATION | Certificate of formation or incorporation, or the equivalent registry extract |
ARTICLE_OF_ASSOCIATION | Articles of organization or incorporation, bylaws, or memorandum of association |
OPERATING_AGREEMENT | Operating agreement (LLC) or partnership agreement |
OWNERSHIP_STRUCTURE | Ownership chart or register showing every owner of 25% or more |
AUTHORIZED_REPRESENTATIVE_LIST | List of directors or authorized signers |
PROOF_OF_BUSINESS_ADDRESS | Utility bill, bank statement, or lease for the operating address, dated within the last 3 months |
BUSINESS_LICENSE | Business license or trade registration of the sole proprietor |
TAX_FILINGS | Most recent business tax return or tax registration certificate |
Every control person also needs an IDENTITY_DOCUMENT (front or photo page) and a PROOF_OF_ADDRESS, whatever the entity type, plus an IDENTITY_DOCUMENT_BACK (reverse side) when their identification_type is a two-sided document such as NATIONAL_ID or DRIVERS_LICENSE. A passport needs no back. Onboarding with any document missing returns 422 MISSING_KYB_FIELDS, listing the gaps as files.<TYPE> and control_person.files.<TYPE> in details.missing.
Notes:
control_person_idis required forIDENTITY_DOCUMENT,IDENTITY_DOCUMENT_BACKandPROOF_OF_ADDRESSon aBUSINESScustomer, and must be omitted for every other type. Sending a person-level document without it returns400.SELFIEis for individual customers only; uploading one for a business returns400.- Accepted formats are PDF, JPG, JPEG, and PNG, up to 10 MB each.
- The table is theContinuing — the tail of section 9, then the rest.
9 (continued) — final two bullets of ##### Upload supporting documents
##### Upload supporting documents- The table is the baseline. Compliance review may request additional documents before approval, for example proof of funds or more evidence for a higher-risk industry or region.
- If onboarding fails part-way (for example an upload error), fix the cause and call `/onboard` again. The retry continues the same application rather than starting a new one.
Continue with **Capture terms acceptance** below.
#### Onboard the customer (INDIVIDUAL)
A natural person sends a `kyc` block with their identity, tax, and identification details. There are no control persons.
##### Create the customer record
`POST /v1/payments/customers`
```bash
curl -X POST https://api-staging.checker.finance/v1/payments/customers \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"client_reference": "jane-doe-customer-001",
"legal_name": "Jane Doe",
"customer_type": "INDIVIDUAL",
"country": "US",
"email": "[email protected]",
"phone": "+14155559123",
"address": {
"line1": "1 Market Street",
"city": "San Francisco",
"state": "CA",
"postal_code": "94105",
"country": "US"
},
"kyc": {
"first_name": "Jane",
"last_name": "Doe",
"date_of_birth": "1990-05-20",
"nationality": "US",
"tax_id_type": "SSN",
"tax_id_number": "123456789",
"tax_residence_country": "US",
"identification_type": "PASSPORT",
"identification_number": "A98765432",
"identification_issuing_country": "US"
}
}'
```
Response — `201 Created`:
```json
{
"customer_id": "cus_01ARZ3NDEKTSV4RRFFQ69G5KYC",
"client_reference": "jane-doe-customer-001",
"legal_name": "Jane Doe",
"customer_type": "INDIVIDUAL",
"country": "US",
"status": "PENDING",
"kyc": {
"first_name": "Jane",
"last_name": "Doe",
"nationality": "US",
"tax_residence_country": "US",
"tax_id_type": "SSN",
"identification_type": "PASSPORT",
"identification_issuing_country": "US"
},
"created_at": "2026-09-23T15:04:05Z",
"updated_at": "2026-09-23T15:04:05Z"
}
```
Store the `customer_id`.
Notes:
- Every `kyc` field shown is required on this corridor. Onboarding without one returns `422 MISSING_KYC_FIELDS` with `details.missing` naming the gaps.
- The person must be at least 18 years old.
- The customer `address` doubles as the residential address. Send `kyc.residential_address` only when the person lives somewhere else.
- `state` is required on every US address, as a USPS code or full state name.
- `date_of_birth`, `tax_id_number`, and `identification_number` are write-only and never returned.
##### Upload the identity documents
An individual needs an `IDENTITY_DOCUMENT` (front or photo page) and a `SELFIE` (a live photo of their face), plus an `IDENTITY_DOCUMENT_BACK` when `kyc.identification_type` is a two-sided document such as `NATIONAL_ID` or `DRIVERS_LICENSE`. A passport needs no back. Upload each file separately with its own `file_type`, and omit `control_person_id`.
`POST /v1/payments/customers/{customer_id}/upload`
```bash
curl -X POST https://api-staging.checker.finance/v1/payments/customers/cus_01ARZ3NDEKTSV4RRFFQ69G5KYC/upload \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@jane_passport.pdf" \
-F "file_type=IDENTITY_DOCUMENT"
```
Notes:
- Repeat the upload with `file_type=SELFIE` (and `file_type=IDENTITY_DOCUMENT_BACK` for a two-sided document).
- Onboarding with a document missing returns `422 MISSING_KYC_FIELDS` listing the gaps, e.g. `files.IDENTITY_DOCUMENT`, `files.SELFIE`, `files.IDENTITY_DOCUMENT_BACK`, in `details.missing`.
- Sending `control_person_id` on an individual's upload returns `400`.
- Accepted formats are PDF, JPG, JPEG, and PNG, up to 10 MB each.
#### Capture terms acceptance
This step and the next apply to both customer types. The customer accepts the corridor's agreements on a hosted page. Mint the page, redirect the customer to the returned `url`, and continue once they return to your `redirect_url`.
`POST /v1/payments/customers/{customer_id}/terms`
```bash
curl -X POST https://api-staging.checker.finance/v1/payments/customers/cus_01ARZ3NDEKTSV4RRFFQ69G5FAV/terms \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"provider": "provider-a",
"redirect_url": "https://example.com/onboarding/terms-complete"
}'
```
Response — `201 Created`:
```json
{
"url": "https://terms.example.com/sign/abc123",
"expires_at": "2026-09-24T15:04:05Z"
}
```
Read the acceptance state at any time with `GET /v1/payments/customers/{customer_id}/terms`, which returns the latest signature per corridor together with the agreement documents:
```json
{
"data": [
{
"provider": "provider-a",
"status": "PENDING",
"expires_at": "2026-09-24T15:04:05Z",
"documents": [
{ "type": "TERMS_AND_CONDITIONS", "url": "https://static.example.com/legal/terms.pdf" },
{ "type": "PRIVACY_POLICY", "url": "https://static.example.com/legal/privacy.pdf" }
]
}
]
}
```
Notes:
- `status` is `PENDING` until acceptance is confirmed, then `ACCEPTED`.
- `redirect_url` is required (`400 VALIDATION_ERROR` without it).
- Terms can only be minted before onboarding is submitted; afterwards the call returns `400 VALIDATION_ERROR`.
- Corridors that do not require terms acceptance reject this call with `400 VALIDATION_ERROR`.
#### Submit onboarding
`POST /v1/payments/customers/{customer_id}/onboard`
```bash
curl -X POST https://api-staging.checker.finance/v1/payments/customers/cus_01ARZ3NDEKTSV4RRFFQ69G5FAV/onboard \
-H "X-Api-Key: $API_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Signature: $SIGNATURE" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "provider": "provider-a" }'
```
Response — `201 Created`:
```json
{
"provider": "provider-a",
"status": "PENDING"
}
```
Onboarding is asynchronous and includes a compliance review. Poll `GET /v1/payments/customers/{customer_id}` and read the `onboarding` array until the entry reports `ACTIVE`:
```json
{
"customer_id": "cus_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"status": "ACTIVE",
"onboarding": [
{ "provider": "provider-a", "status": "ACTIVE" }
]
}
```
Onboarding submitted before the documents are uploaded or the terms accepted returns `422 MISSING_KYB_FIELDS` (or `MISSING_KYC_FIELDS` for a natural person), with the outstanding items in `details.missing`. Complete them and call onboard again.
<br />Updated 3 days ago
