Card KYC

What Is Card KYC?

Before a customer can be given a virtual card, they must verify their identity — this is "KYC" (Know Your Customer). The customer submits their details (name, date of birth, an ID, address), our partner Miden checks them, and once the customer is approved, a card can be created.

No approval = no card. This is a compliance requirement, not a bug.

This guide covers submitting KYC, checking status, and receiving the asynchronous outcome by webhook.


How It Works

1

Submit the customer's identity details. Bitnob creates or uses a customer record and forwards the submission to the verification provider. You get back a customer_id and an initial status

2

Verification runs asynchronously. Simple submissions may return approved almost immediately; others sit at pending while documents and checks are processed

3

Learn the outcome either by polling the status, or by receiving a webhook, which is recommended. The webhook is the terminal signal

4

Once approved, proceed to card issuance

How Long Verification Takes

BVN and NIN are verified instantly. All other ID types return a result within 10 minutes.

The Customer Journey

The same flow as the customer experiences it.

Customer journey
1

The customer submits their identity information, usually through the partner app

2

Verification happens automatically. Some submissions come back approved in seconds, others sit under review for a while

3

The final result is approved or rejected

4

Only after approved can the card be created


Prerequisites

item
value
Environment
Sandbox for testing, production for live customers
Auth and endpoint
Use your assigned Bitnob endpoint and API credentials. Confirm the exact host and auth headers with your Bitnob contact
Field naming
Field names are snake_case. Tooling also accepts the camelCase equivalents — customer_id and customerId

Submitting KYC

field
type
required
notes
customer
object
Required
The identity to verify. A successful submission creates the customer and returns their customer_id
occupation
string
Required
The customer's occupation, in lowercase snake_case
employment_status
string
Required
For example employed
account_purpose
string
Required
What the customer intends to use the card for
annual_salary
string
Required
Declared annual income in whole major units, sent as a string
expected_monthly_volume
string
Required
Anticipated monthly spend in whole major units, sent as a string
terms_of_service_accepted
bool
Required
Must be true
place_of_birth
string
Optional
The customer's place of birth
webhook_url
string
Optional
Per-submission callback URL for the outcome. Empty fans out to your company's configured webhook
id_front_image
string
Conditional
Base64-encoded image of the ID front. Required for document id types — never for bvn or nin
redirect_url
string
Optional
A redirect link
The customer Object
field
required
notes
customer_type
Required
Lowercase individual or business. Capitalised values are rejected
first_name
Required
Required for businesses too — the natural person behind the account
last_name
Required
Required for businesses too
date_of_birth
Required
YYYY-MM-DD. Required for businesses too
id_type
Required
See Identity Document Rules
id_number
Required
bvn, nin, or document number. Must be unique. Stored encrypted at rest
email
Required
Per-company reconciliation key
phone_number
Optional
Without dial code. Send it with dial_code
dial_code
Optional
For example +234. Send it with phone_number
business_name
Required for businesses
Sent alongside the personal fields, not instead of them
registration_number
Required for businesses
Business or RC number
line1, city, state, postal_code, country
Required
Address. For bvn and nin, country must be Nigeria — NGA
line2
Optional
Address line 2
Name Must Match the Identity Document

first_name and last_name are verified against the identity document. Submit them exactly as they appear on the document.

Example — Individual
Example — Business
Response
field
notes
customer_id
The Bitnob customer id, persisted. Use this for status lookups and card issuance
normalized_status
Read this field. Provider-independent status — initiated, pending, approved, or rejected
status
The provider's own vocabulary
email, first_name, last_name
Echoed back for reconciliation
completion_link
A redirect link
Response

Checking Status

Poll the status if you are not using webhooks, or to reconcile.

field
notes
normalized_status
Read this. none, initiated, pending, approved, or rejected. none means no KYC on record
status
Provider vocabulary
reason
Rejection labels when pending or rejected
completion_link
A redirect link

Status Vocabulary

Always integrate against normalized_status — it is the same regardless of provider.

normalized_status
meaning
terminal
none
No KYC on record for this customer
No
initiated
Submission received and created
No
pending
Under review — documents or checks in progress
No
approved
Verified. The customer may be issued a card
Yes
rejected
Verification failed. See reason
Yes
Provider Mapping

How the raw status collapses into normalized_status.

raw provider status
normalized
action_required
pending
denied
rejected
Any other value
Passed through unchanged

Identity Document Rules

Accepted id_type values are passport, nin, bvn, national_id, vnin, drivers_license, voters_card, and ghana_card.

rule
requirement
bvn and nin
country must be Nigeria — NGA. Never send id_front_image
nin
date_of_birth is required, in YYYY-MM-DD format
Document types — passport, drivers_license, ghana_card, national_id, voters_card, vnin
Send id_front_image as a base64-encoded string
Country codes
ISO 3166-1 alpha-3 — for example NGA, GHA, USA

The photo must show the front of the document in full, in focus and in date. Blurred, partial, or expired images are the most common cause of rejection.


Webhooks

KYC is asynchronous. Rather than polling, receive the terminal outcome as a webhook. Configure a webhook_url per submission, or a company-wide webhook with Bitnob.

Events

virtualcard.user.kyc.pending — Verification is under review (not final).

virtualcard.user.kyc.complete — Approved (kycPassed: true).

virtualcard.user.kyc.failed — Rejected (kycPassed: false, see reason).

Payload
data field
notes
customerId
The Bitnob customer id. Your reconciliation key alongside customerEmail
customerEmail
Per-company reconciliation key
kycPassed
true on complete, false on failed
reason
Populated on failure, with rejection labels
Handling

Respond 2xx quickly. Treat delivery as at-least-once and make your handler idempotent, deduplicating on customerId and event

Do not treat pending as terminal. Only complete and failed are final

Verify signatures per your Bitnob webhook configuration


Common Errors

message
cause or fix
invalid created_by: must be a valid UUID
created_by must be a UUID, not an email
customer_type must be Individual or Business
Fix customer.customer_type
KYC is required before a card can be issued for this customer
Card issuance was attempted before KYC reached approved
card KYC is not available
The KYC service is unavailable for the requested environment. Ensure you are calling the correct one, and contact Bitnob if it persists

Rate limits apply to KYC calls. Retry with backoff on transient errors.


Support Notes

What To Tell The Customer

Plain-language phrasing for each status.

status
what the customer should hear
none
We have not received your verification yet — please submit your details to continue
initiated
We have your details and verification has started
pending
Your verification is under review. This can take a little time, and there is nothing you need to do right now — we will update you once it is complete
approved
You are verified. Your card can now be created
rejected
We could not verify your identity. Reason: the reason returned on the record. Please correct it and submit again
Note

Only approved and rejected are final. pending means wait — it is not an error.

"Card creation says KYC is required"

The customer is not approved yet. Check the status and branch on it.

pending — ask the customer to wait, it is still under review. Wait for the virtualcard.user.kyc.complete webhook

rejected — the customer must fix the reason and resubmit

none — no KYC has been submitted for this customer at all. Ask them to complete it

"KYC came back pending and never moved"

Verification is still processing, or a document was unreadable. Check for a virtualcard.user.kyc.failed webhook with a reason, and re-check the status.

"The verification was rejected"

The specific cause is in reason on the record and on the virtualcard.user.kyc.failed webhook — commonly a blurred document, mismatched details, or an expired ID. Give the customer the specific reason and how to fix it, then have them resubmit.

"Which id becomes the card owner?"

The customer_id returned on submission — always the Bitnob id, never the provider's internal id.

"nin or bvn submission rejected on country"

bvn and nin require country to be NGA, and no ID image. The photo is only needed for documents like a passport or driver's licence.

"The ID photo keeps failing"

The image is blurred, cropped or expired. Ask for a clear, in-date photo showing the front of the document in full — see Identity Document Rules.

"KYC is approved but the card still will not create"

Confirm normalized_status really is approved rather than pending. If it is approved and issuance still fails, this is not a KYC problem — escalate.

Expected Behaviour, Not A Bug

pending is normal. Verification is asynchronous, and some checks take longer than others

"KYC is required before a card can be issued for this customer" ahead of approval is the compliance rule working, not an error

Some customers are approved in seconds while others sit under review. Different id types and documents take different amounts of time


Escalation

Escalate to engineering when:

normalized_status is approved but card issuance still fails

A submission has been pending far beyond the expected window, with no webhook and no change on re-check

The customer hits an error that is not covered above — for example card KYC is not available

What To Collect First
item
detail
Company
company_id, and the partner name
Customer
The customer_id returned on submission, and the customer email
Status
The current normalized_status, the raw status, and any reason
Document
The id_type used, and whether an id_front_image was sent
Timing
Approximate time of the submission, or of the failed card creation
Error
The exact error message and HTTP status, if there was one

Hand this to the engineering on-call together with a link to this guide.


Share on
Did you find this page useful?

Join our Discord