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_reference is your own identifier for the customer and is echoed back in responses.
  • Every kyb field shown is required on this corridor. Onboarding without one returns 422 MISSING_KYB_FIELDS with details.missing naming the gaps — field paths such as kyb.business_industry, document paths such as files.BUSINESS_REGISTRATION, and terms or terms.<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 have is_control_person: true.
  • Add owners as further control_persons entries with is_beneficial_owner: true and an ownership_percentage, which is required whenever is_beneficial_owner is true. is_authorized_signer and is_director default to false.
  • state is 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_typeRequired entity file types
LIMITED_LIABILITY_COMPANYBUSINESS_REGISTRATION, ARTICLE_OF_ASSOCIATION, OPERATING_AGREEMENT, OWNERSHIP_STRUCTURE, AUTHORIZED_REPRESENTATIVE_LIST, PROOF_OF_BUSINESS_ADDRESS
CORPORATION, PUBLICLY_TRADED_COMPANYBUSINESS_REGISTRATION, ARTICLE_OF_ASSOCIATION, OWNERSHIP_STRUCTURE, AUTHORIZED_REPRESENTATIVE_LIST, PROOF_OF_BUSINESS_ADDRESS
GENERAL_PARTNERSHIP, LIMITED_LIABILITY_PARTNERSHIPBUSINESS_REGISTRATION, OPERATING_AGREEMENT (the partnership agreement), OWNERSHIP_STRUCTURE, AUTHORIZED_REPRESENTATIVE_LIST, PROOF_OF_BUSINESS_ADDRESS
SOLE_PROPRIETORSHIPBUSINESS_LICENSE, TAX_FILINGS, PROOF_OF_BUSINESS_ADDRESS

What each file type should contain:

File typeDocument
BUSINESS_REGISTRATIONCertificate of formation or incorporation, or the equivalent registry extract
ARTICLE_OF_ASSOCIATIONArticles of organization or incorporation, bylaws, or memorandum of association
OPERATING_AGREEMENTOperating agreement (LLC) or partnership agreement
OWNERSHIP_STRUCTUREOwnership chart or register showing every owner of 25% or more
AUTHORIZED_REPRESENTATIVE_LISTList of directors or authorized signers
PROOF_OF_BUSINESS_ADDRESSUtility bill, bank statement, or lease for the operating address, dated within the last 3 months
BUSINESS_LICENSEBusiness license or trade registration of the sole proprietor
TAX_FILINGSMost 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_id is required for IDENTITY_DOCUMENT, IDENTITY_DOCUMENT_BACK and PROOF_OF_ADDRESS on a BUSINESS customer, and must be omitted for every other type. Sending a person-level document without it returns 400.
  • SELFIE is for individual customers only; uploading one for a business returns 400.
  • 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

- 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 />

Did this page help you?