Overview
Provider W cards require a cardholder before a card can be issued. The cardholder goes through a two-stage approval:
- PayCA review — cardholder is created with
pending_reviewstatus and must be approved by a PayCA admin. - Provider audit — after PayCA approves, the cardholder is submitted to Provider W and moves through
wait_audit→pass_audit(orreject).
Only cardholders with status: "pass_audit" can be used to issue cards.
Cardholder Statuses
| Status | Meaning |
|---|---|
pending_review |
Created, waiting for PayCA admin approval. |
rejected_by_admin |
Rejected by PayCA admin (see description for reason). |
wait_audit |
Submitted to Provider W, waiting for provider audit. |
under_review |
Provider W is reviewing the cardholder. |
pass_audit |
Approved — ready for card issuance. |
reject |
Rejected by Provider W (see description and statusFlowLocation). |
B2B Flow
B2B cardholders require basic personal data only. No KYC documents are needed — file IDs are uploaded manually via the file upload endpoint.
Step 1 — Create user
curl -s -X POST "$PAYCA_BASE_URL/v1/users" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{
"externalId": "usr-001",
"meta": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "5551234567",
"phoneCode": "+1",
"birthday": "1990-01-15",
"country": "US",
"town": "NYC",
"address": "123 Main St",
"postCode": "10001"
}
}'
Use /v1/cardholders/regions for country codes and /v1/cardholders/cities for town codes.
Step 2 — Upload identity documents (optional, B2B only)
curl -s -X POST "$PAYCA_BASE_URL/v1/cardholders/files" \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-F "file=@passport_front.jpg"
Save the returned fileId values and include them in the user meta as idFrontImg, idBackImg, idHoldImg.
Step 3 — Create cardholder
curl -s -X POST "$PAYCA_BASE_URL/v1/users/{userId}/cardholder" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{
"accountId": "<account-uuid>",
"bin": "537100"
}'
Response returns a CardholderResponse with status: "pending_review".
Step 4 — Wait for approval
Poll GET /v1/cardholders/{id} until status changes:
pass_audit— proceed to card issuance.rejected_by_admin— rejected by PayCA; checkdescriptionfor the reason.reject— rejected by Provider W; checkdescriptionandstatusFlowLocation.
Step 5 — Issue card
curl -s -X POST "$PAYCA_BASE_URL/v1/cards" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{
"accountId": "<account-uuid>",
"bin": "537100",
"balance": "100.00",
"idempotencyKey": "issue-001",
"cardholderId": "<cardholder-uuid>"
}'
3DS OTP codes are sent to the cardholder's email and phone.
B2C Flow
B2C cardholders require extended personal data and completed KYC verification. KYC documents are uploaded automatically from the KYC system when the cardholder is submitted to Provider W.
Step 1 — Create user
Include all B2B fields plus B2C-specific fields:
curl -s -X POST "$PAYCA_BASE_URL/v1/users" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{
"externalId": "usr-002",
"meta": {
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"phone": "5559876543",
"phoneCode": "+1",
"birthday": "1988-05-20",
"gender": "F",
"idType": "PASSPORT",
"idNo": "AB1234567",
"nationality": "US",
"country": "US",
"town": "LAX",
"address": "456 Oak Ave",
"postCode": "90001",
"occupation": "IT"
}
}'
Use /v1/cardholders/occupations for occupation codes.
Step 2 — KYC verification
Upload identity documents and a selfie, then submit for review. Documents are sent as base64-encoded JSON (not multipart). Max 10 MB decoded per file.
# Helper — base64-encode a file and POST it
upload_kyc() {
local USER_ID="$1" DOC_TYPE="$2" FILE="$3"
local CONTENT=$(base64 < "$FILE" | tr -d '\n')
curl -s -X POST "$PAYCA_BASE_URL/v1/users/$USER_ID/kyc/documents" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d "{
\"documentType\": \"$DOC_TYPE\",
\"fileName\": \"${FILE##*/}\",
\"fileContent\": \"$CONTENT\",
\"mimeType\": \"image/jpeg\"
}"
}
upload_kyc "$USER_ID" passport passport.jpg
upload_kyc "$USER_ID" selfie_with_document selfie.jpg
# Submit KYC for review
curl -s -X POST "$PAYCA_BASE_URL/v1/users/$USER_ID/kyc/submit" \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET"
Valid documentType values: passport, driving_licence, id_card, residence_permit, proof_of_address, selfie_with_document.
Wait for status: "completed" on GET /v1/users/{userId}/kyc (or subscribe to the kyc webhook) before proceeding.
Step 3 — Create cardholder
Same as B2B Step 3. KYC must be completed — the endpoint rejects requests if KYC is not completed.
Step 4 — Wait for approval
Same as B2B Step 4. After PayCA admin approves, KYC documents are automatically uploaded to Provider W.
Step 5 — Issue card
Same as B2B Step 5 — pass the cardholderId in the card creation request.
Cardholder Is Always Required for Provider W
Every Provider W card issue requires the requesting client to already have a pass_audit cardholder for the BIN's card type. The check runs as a preflight on POST /v1/cards; failures return HTTP 412 (Failed Precondition) with a kyc required: ... message before any funds are held or any workflow is started.
Once the client-level KYC is in place, the provider-side holderId for the actual card issuance is resolved in this priority:
- Explicit
cardholderIdin the request — must belong to your client, match the BIN's card type, and bepass_audit. - User's approved cardholder for the card type (
/v1/users/{id}/cardholder). - BIN-internal fallbacks (b2b holder pool, BIN default holder) — used when no per-user cardholder is set; never bypasses the client-level KYC preflight.
In practice: create the cardholder, wait for pass_audit, then issue. There is no longer a path that lets a client issue a Provider W card before completing its own KYC.
Required User Meta Fields
B2B (minimum)
| Field | Type | Description |
|---|---|---|
firstName |
string | First name. |
lastName |
string | Last name. |
email |
string | Email address. |
phone |
string | Phone number (digits only). |
phoneCode |
string | Country dial code (e.g. +1). Normalized to include + prefix. |
birthday |
string | Date of birth (YYYY-MM-DD). |
country |
string | Country code (from /v1/cardholders/regions). |
town |
string | City code (from /v1/cardholders/cities). |
address |
string | Street address. |
postCode |
string | Postal code. |
B2C (additional)
| Field | Type | Description |
|---|---|---|
gender |
string | M or F. |
idType |
string | Document type: PASSPORT, DLN, etc. |
idNo |
string | Document number. |
nationality |
string | Nationality code (from /v1/cardholders/regions). |
occupation |
string | Occupation code (from /v1/cardholders/occupations). |
Optional fields (B2B and B2C)
| Field | Type | Description |
|---|---|---|
idIssueDate |
string | Document issue date. |
idExpiryDate |
string | Document expiry date. |
idFrontImg |
string | File ID of document front (B2B only, via /v1/cardholders/files). |
idBackImg |
string | File ID of document back (B2B only). |
idHoldImg |
string | File ID of selfie holding document (B2B only). |
annualSalary |
string | Annual salary range. |
accountPurpose |
string | Purpose of account. |
expectedMonthlyVolume |
string | Expected monthly transaction volume. |
ipAddress |
string | User's IP address. |
API Endpoints Reference
| Method | Endpoint | Description |
|---|---|---|
POST |
/v1/users/{id}/cardholder |
Create cardholder for a user. |
GET |
/v1/users/{id}/cardholder |
Get cardholder status for a user. |
GET |
/v1/cardholders |
List all cardholders for the client. |
GET |
/v1/cardholders/{id} |
Get cardholder by ID. |
GET |
/v1/cardholders/cities |
List city codes for registration. |
GET |
/v1/cardholders/occupations |
List occupation codes (B2C). |
GET |
/v1/cardholders/regions |
List country/region codes. |
POST |
/v1/cardholders/{id}/photos |
Upload passport photo and/or selfie for admin review (multipart/form-data). |
POST |
/v1/cardholders/files |
Upload file for B2B cardholder registration. |
Error Handling
| Error | Cause | Resolution |
|---|---|---|
cardholder requires user meta fields: ... |
Missing required fields in user meta. | Update user meta with the listed fields. |
B2C cardholder requires user meta fields: ... |
Missing B2C-specific fields. | Add gender, idType, idNo, nationality, occupation. |
bin does not support provider w cardholders |
BIN has no cardholder type configured for Provider W. | Use a BIN that supports Provider W cardholders. |
KYC must be completed before creating a B2C cardholder |
KYC not finished. | Complete KYC verification first. |
kyc required: no pass_audit cardholder for this bin |
Client has no approved cardholder matching this BIN's card type. | Create a cardholder via POST /v1/users/{id}/cardholder for this BIN and wait for pass_audit. |
kyc required: cardholder does not belong to this client |
The cardholderId in the issue request belongs to a different client. |
Use a cardholderId owned by the requesting client. |
kyc required: cardholder is not registered for this bin's card type |
The cardholderId was created against a different BIN's card type. |
Use a cardholder whose card type matches the issuance BIN, or omit cardholderId to fall through to client-level lookup. |
kyc required: cardholder status is X, expected pass_audit |
The cardholderId exists but is not yet approved. |
Wait until the cardholder reaches pass_audit. |
cardholder not approved (status: ...) |
Provider-layer fallback for the same condition (only seen if the adapter preflight is bypassed in tests). | Wait for cardholder approval. |