Skip to main content
BVNK

Create individual customers

This guide describes how to create and onboard individual customers (natural persons).

Customers API v.2

This is a new customer onboarding flow, which is recommended for all new integrations.

If you already have an existing integration with Customers API v.1, you can continue using it. BVNK will continue to support this API v.1 going forward.

The onboarding flow is asynchronous: after you create a customer, BVNK processes the application and notifies you via webhooks when action is required or when verification is complete. There is no separate "submit" call: onboarding moves to review automatically once all the required data, documents, and agreements are in place.

The data and documents you must submit depend on the onboarding model, the customer's country of residence, and the BVNK entity (US or EU) your account is assigned to. See the compliance requirements for individuals for the full list.

Individual customer creation and onboarding is only available for the embedded delivery model. The specific data and document requirements depend on the onboarding model:

  • BVNK-Managed: You submit the customer's personal and due diligence data, and BVNK verifies the customer's identity. The customer's identity documents and selfie are collected through the API or BVNK's hosted onboarding flow. Recommended when you don't have an existing KYC programme or want BVNK to handle the full verification lifecycle.
  • Partner-Managed: Available to regulated partners with an appropriate AML programme. You verify the customer's identity with your own processes and share the verification results with BVNK in the reliance object, along with a risk score and EDD attestation. No identity documents, selfies, or proofs of address are uploaded at creation. To learn more, contact your BVNK account manager.

When onboarding a customer, they pass through the following statuses:

StatusDescription
AGREEMENTS_PENDINGThe customer was created through agreements and the agreements are not signed yet.
INFO_REQUIREDCustomer data, documents, or agreements are still missing. Check requiredActions.
PENDINGAll information is submitted and the customer is under review.
ACTIONS_REQUIREDBVNK needs additional information during or after the review. Check requiredActions.
VERIFIEDThe customer is verified.
REJECTEDThe customer failed verification.
TERMINATEDThe customer was offboarded by BVNK or by you.

A typical customer moves from INFO_REQUIRED to PENDING and then to VERIFIED or REJECTED. During or after the review, BVNK may request more information, in which case the customer moves to ACTIONS_REQUIRED until you provide it. A verified customer can also move back to ACTIONS_REQUIRED or PENDING, for example, during a periodic review.

Prerequisites​

Before you start, make sure you have:

warning

Before running the onboarding flow, make sure you are not onboarding customers from restricted jurisdictions, and that your platform has collected all required KYC, screening, and risk assessment data. For more information, see BVNK Acceptable Use Policy.

Create agreements​

The customer must accept BVNK's agreements before onboarding can proceed. We recommend collecting the agreements before creating the customer, for example, during your sign-up flow.

  1. Create the agreements for the prospective customer by sending the POST platform/v2/agreements request with "customerType": "INDIVIDUAL".

    Supply a reference that is unique within your account (up to 36 characters). You will reuse it to respond to the agreements and to create the customer later.

    {
    "reference": "partner-ind-001",
    "useCase": "EMBEDDED_FIAT_ACCOUNTS",
    "customerType": "INDIVIDUAL",
    "countryCode": "DE",
    "customerName": "Jane Mueller",
    "applicantEmail": "jane.mueller@example.com"
    }

    The response returns the agreements that apply to the customer, each in the PENDING status, and a signingUrl:

    {
    "id": "5f2d9c1a-7b3e-4e8a-a1c4-0d6e8f9b2a37",
    "reference": "partner-ind-001",
    "agreements": [
    {
    "id": "c2cbc333-8c4c-4bd7-a3d3-92a377772cbb",
    "name": "Partner Platform Agreement (Non-US)",
    "description": "Terms and conditions for Partner Platform customers outside the US",
    "declinable": false,
    "status": "PENDING"
    }
    ],
    "signingUrl": "https://onboarding.layer1.com#token=jgeTw5WhqzDdnaRUqLxyYdb5XJ4QqvK-4OENqPgjFKc&userId=fb870b23-17a8-4cd7-bc00-7eadc6b07561"
    }

    At this point, the customer record exists with the AGREEMENTS_PENDING status.

  2. Collect the customer's decisions in one of the following ways:

    • Hosted signing: Redirect the customer to the signingUrl. The customer reviews and signs the agreements in BVNK's hosted interface. To send the customer back to your platform after signing, append a redirectUri parameter as described in Create business customers.

    • Your own interface: Show the agreements to the customer. Use GET platform/v2/agreements/{id}/content to get a download link for each agreement. Then submit the customer's decisions by sending the POST platform/v2/agreements/actions request with the same reference:

      {
      "reference": "partner-ind-001",
      "actions": [
      {
      "agreementId": "c2cbc333-8c4c-4bd7-a3d3-92a377772cbb",
      "type": "ACCEPT"
      }
      ]
      }

    Once the agreements are signed, the customer moves to INFO_REQUIRED.

  3. Create the customer with the same reference. The signed agreements are bound to the customer automatically. If the agreements are still pending, or a non-declinable agreement was rejected, the request is rejected with 400.

Create individual customers​

After the relevant agreements are accepted, create a customer.

  1. Create the customer by sending the POST platform/v2/customers request with the individual object. The company and individual objects are mutually exclusive, so don't include company in the same request.

    {
    "useCase": "EMBEDDED_FIAT_ACCOUNTS",
    "reference": "partner-ind-001",
    "individual": {
    "firstName": "Jane",
    "lastName": "Mueller",
    "emailAddress": "jane.mueller@example.com",
    "phoneNumber": "+49301234567",
    "dateOfBirth": "1990-06-15",
    "placeOfBirth": "Berlin",
    "birthCountryCode": "DE",
    "nationality": "DE",
    "address": {
    "addressLine1": "Unter den Linden 77",
    "city": "Berlin",
    "postalCode": "10117",
    "countryCode": "DE"
    },
    "taxIdentification": {
    "number": "21/815/09150",
    "taxResidenceCountryCode": "DE"
    },
    "cdd": {
    "employmentStatus": "SALARIED",
    "sourceOfFunds": "SALARY",
    "pepStatus": "NOT_PEP",
    "intendedUseOfAccount": "TRANSFERS_OWN_WALLET",
    "expectedMonthlyVolume": {
    "amount": 5000,
    "currency": "EUR"
    }
    }
    }
    }

    Note the following jurisdiction-specific requirements:

    • EEA customers: nationality, birthCountryCode, and cdd.expectedMonthlyVolume are required.
    • US persons (US nationality or US tax residence): taxIdentification.number must be a valid SSN or ITIN, and address.stateCode is required for US addresses. Residents of some US states must also provide cdd.estimatedYearlyIncome and cdd.employmentIndustrySector.

    The response returns the customer's id and current status. Save the id for the subsequent steps.

    {
    "id": "8d3f2a71-5c1e-4b9a-9f0d-2e6b7c4a1d95",
    "reference": "partner-ind-001",
    "status": "INFO_REQUIRED",
    "type": "INDIVIDUAL",
    "useCase": "EMBEDDED_FIAT_ACCOUNTS",
    "name": "Jane Mueller",
    "createdAt": "2026-04-22T10:57:35.056865Z",
    "updatedAt": "2026-04-22T10:57:35.056865Z"
    }
  2. Retrieve the customer's outstanding requirements by sending the GET platform/v2/customers/{id} request. The requiredActions array lists everything that is still needed before the customer can move to review. See Resolve required actions.

    {
    "id": "8d3f2a71-5c1e-4b9a-9f0d-2e6b7c4a1d95",
    "reference": "partner-ind-001",
    "status": "INFO_REQUIRED",
    "type": "INDIVIDUAL",
    "name": "Jane Mueller",
    "authenticatedLink": {
    "link": "https://onboarding.bvnk.com/link/eyJhbGciOi...",
    "expiresAt": "2026-04-22T11:57:35.056865Z"
    },
    "requiredActions": [
    {
    "type": "DATA",
    "code": "IDENTITY",
    "category": "DOCUMENT_REQUIREMENTS",
    "description": "Identity document and selfie are required.",
    "status": "REQUIRED"
    }
    ]
    }
  3. Upload the customer's identity documents by sending the POST platform/v2/customers/{customerId}/documents request as multipart/form-data.

    The metadata part is a JSON array with one entry per file in the files part, in the same order. You can upload up to three documents in one request. For each individual document:

    • countryCode (the country that issued the document) is required.
    • For two-sided documents (ID_CARD, DRIVERS, RESIDENCE_PERMIT), upload each side as a separate file with subType set to FRONT_SIDE or BACK_SIDE.
    • Always include a SELFIE with the identity document.
    curl -X POST "https://api.sandbox.bvnk.com/platform/v2/customers/8d3f2a71-5c1e-4b9a-9f0d-2e6b7c4a1d95/documents" \
    -H "Authorization: Bearer <token>" \
    -F 'metadata=[
    {"type": "ID_CARD", "subType": "FRONT_SIDE", "filename": "id-front.jpg", "countryCode": "DE"},
    {"type": "ID_CARD", "subType": "BACK_SIDE", "filename": "id-back.jpg", "countryCode": "DE"},
    {"type": "SELFIE", "filename": "selfie.jpg", "countryCode": "DE"}
    ];type=application/json' \
    -F "files=@id-front.jpg" \
    -F "files=@id-back.jpg" \
    -F "files=@selfie.jpg"

    In the response, you will see the documents accepted for processing:

    {
    "id": "9b0c4f1d-7aa2-4c7e-8d55-f8b4a1d6c210",
    "customerId": "8d3f2a71-5c1e-4b9a-9f0d-2e6b7c4a1d95",
    "totalDocuments": 3,
    "successCount": 3,
    "failureCount": 0,
    "documents": [
    {
    "documentId": "b8f8a6b5-8f44-4b7c-8d1e-4a6a5a9b4a21",
    "type": "ID_CARD",
    "filename": "id-front.jpg",
    "status": "STORED",
    "uploadedAt": "2026-04-22T10:58:35.056865Z"
    }
    ],
    "createdAt": "2026-04-22T10:58:35.056865Z"
    }

    For the accepted document types and quality requirements, see Required documents.

    Hosted onboarding

    Instead of collecting documents in your own interface, you can redirect the customer to the authenticatedLink.link returned for the customer. The customer completes the outstanding steps in BVNK's hosted interface. The link expires at authenticatedLink.expiresAt.

  4. If BVNK cannot verify the customer's address automatically, a proof-of-address document is requested in requiredActions. Upload it to the same endpoint with type set to UTILITY_BILL or BANK_STATEMENT. For details, see Address verification.

  5. Wait for the webhook or poll the status by sending the GET platform/v2/customers/{id} request. Once all requirements are met, the customer moves to PENDING and then to VERIFIED or REJECTED.

Resolve required actions​

While a customer is in the INFO_REQUIRED or ACTIONS_REQUIRED status, the requiredActions array of GET platform/v2/customers/{id} lists what is still needed. Each action has the following fields:

FieldDescription
typeDATA: information, a document, or an agreement response is missing. USER_ROLE: an action is required from a specific user role. BLOCKER: BVNK must complete an internal step, such as a manual review; no action is required from you.
codeMachine-readable identifier of the action.
categoryArea the action belongs to, for example DOCUMENT_REQUIREMENTS or AGREEMENTS. Present only for DATA actions.
statusREQUIRED if the action is outstanding, or PROCESSING if you have submitted the information and it is being processed. Present only for DATA actions.
targetThe specific item the action refers to, when available.

The target.kind field tells you how to resolve the action:

target.kindHow to resolve
DOCUMENTUpload one of the document types listed in target.types to POST platform/v2/customers/{customerId}/documents.
AGREEMENTHave the customer accept the agreement identified by target.assignedAgreementId.
FIELDProvide or correct the field at target.path by updating the customer.

Update customer details​

To correct or complete the customer's data, see Update customer details.

Document rejection and resubmission​

When a document is rejected, the status response includes rejection details:

{
"docSetType": "SELFIE",
"types": ["SELFIE"],
"status": "REJECTED",
"rejectLabels": ["BAD_SELFIE"],
"clientComment": "A new liveness check has been requested."
}

Handle rejections programmatically. Parse rejectLabels to determine whether to resubmit or surface a failure to your user. To resubmit, upload the corrected document to the same endpoint. The rejected document is replaced.

Webhooks​

Webhooks must be used as the primary mechanism for tracking status changes. Use polling only as a fallback.

To receive updates on each step of the onboarding process, configure your webhook endpoint in the BVNK Portal. BVNK sends notifications for key lifecycle events.

Event URNTrigger
platform:accounts:customer:status-changeCustomer's onboarding status changes.
platform:accounts:customer:agreement-status-changeAgreement is accepted or rejected by the customer.
platform:accounts:customer:document-status-changeStatus of one or more of the customer's documents changes.

Use platform:accounts:customer:status-change as the primary driver for your onboarding flow. When you receive a status change, call GET platform/v2/customers/{id} to retrieve the current status and requiredActions.

Error handling​

ScenarioResponse status codeResolution
Missing required field or invalid value400Check details.errors for the field paths and messages.
Missing jurisdiction-specific field400EEA customers need nationality, birthCountryCode, and cdd.expectedMonthlyVolume. US persons need a valid SSN or ITIN in taxIdentification.number and address.stateCode.
Account not enabled for the customer type or use case403Contact BVNK to enable individual customers for your account.
Reference already bound or idempotency conflict409Reuse the same payload for the same idempotency key and ensure the reference is not already attached to another customer.
Onboarding request rejected422Check the error message, correct the request, and resubmit.
Onboarding temporarily unavailable502Retry with the same Idempotency-Key.
Was this page helpful?