KYC
Flow Summary
The current KYC flow is:
Step 1. Upload KYC filesPOST /api/v1/media/file
Step 2. Validate document numberPOST /api/v1/cardholder-kyc/validate
Step 3. Submit KYC requestPOST /api/v1/cardholder-kyc
Step 4. Read latest KYC resultGET /api/v1/cardholder-kyc/latest
Step 5. Confirm cardholder creationGET /api/v1/cardholder/latest
All KYC routes below should be treated as protected routes and sent with:
Authorization: Bearer <access_token>
The two POST endpoints also pass through DecryptField(), so they support either plain JSON or the encrypted wrapper described in Overview.
1. Upload KYC Files First
The KYC create request expects file references, not raw file bytes.
Upload the document images first with File Upload, then pass the returned object path or internal URL into the KYC payload.
Typical files:
- front document image
- back document image for
id_number - optional selfie or supporting photo
2. Validate Document Number
POST /api/v1/cardholder-kyc/validate
This checks whether the same document number is already in use or already pending verification for another account.
Request
{
"document_type": "passport",
"number_id": "P12345678"
}
Success response
{
"status_code": 200,
"message": "Document is valid",
"data": null
}
Rules
document_typemust be one ofpassport,visa, orid_numbernumber_idis trimmed before duplicate checks- Duplicate checks ignore old records in
FAILED,DECLINED, andNOT_VERIFY
Common failures
409: the document is already pending on another account409: the document is already verified on another account
3. Submit KYC Request
POST /api/v1/cardholder-kyc
This creates a new cardholder KYC request for the authenticated mobile user. The backend fills agent_id and mobile_user_id from the auth context.
Request
{
"document_type": "passport",
"number_id": "P12345678",
"front_kyc_url": "media/private/kyc/front-passport.jpg",
"photo_urls": "media/private/kyc/selfie.jpg",
"first_name": "Sok",
"last_name": "Dara",
"country_id": 114,
"date_of_birth": "1995-04-18",
"issue_date": "2022-01-01",
"expire_date": "2032-01-01",
"gender": "MALE",
"file_type": "jpg"
}
If document_type is id_number, back_kyc_url is also required.
Required field rules
document_type:passport,visa,id_numbergender:MALE,FEMALEfile_type:jpg,png,pdf,jpegfront_kyc_url: always requiredback_kyc_url: required only forid_number
Important backend rules
- The mobile user must have both
is_phone_verified = trueandis_email_verified = true first_nameandlast_namemust contain letters only- If
date_of_birthis provided, the user must be at least 18 years old - The same user cannot submit another KYC while they already have
PENDINGorAPPROVED - The same document number cannot be reused if another active KYC already owns it
- Incoming internal file URLs are sanitized before storage
Success response
{
"status_code": 201,
"message": "Success",
"data": {
"id": 87,
"uuid": "0d65f58d-47f6-43d7-84b2-4bdc8d44e391",
"first_name": "Sok",
"last_name": "Dara",
"document_type": "passport",
"number_id": "P12345678",
"status": "PENDING",
"file_type": "jpg",
"kyc_urls": {
"front_kyc_url": "https://...",
"back_kyc_url": ""
},
"photo_urls": "https://..."
}
}
Notes
- The response returns transformed file URLs, not just the raw stored object keys
- A manager notification is created in the background after submission
- If the server setting
IsAutoApproveKYCis enabled, the KYC may be auto-approved immediately instead of remainingPENDING
4. Read Latest KYC
GET /api/v1/cardholder-kyc/latest
This returns the latest KYC record for the current user, with transformed URLs and translated reject reasons.
Success response shape
{
"status_code": 200,
"message": "Success",
"data": {
"uuid": "0d65f58d-47f6-43d7-84b2-4bdc8d44e391",
"status": "PENDING",
"card_status": "PENDING",
"is_kyc_expire": false,
"reject_reason": "",
"kyc_urls": {
"front_kyc_url": "https://...",
"back_kyc_url": ""
},
"mobile_user": {
"phone": "012345678",
"email": "user@example.com"
}
}
}
Notes
- Send
Accept-Language: zhif you want translatedreject_reasontext when available - The handler comment says this returns pending/approved KYC, but the current repository implementation returns the most recent KYC row for the user
- If the latest KYC is
DECLINEDand no card status exists yet, the API mapscard_statusto a failed state in the response
5. Confirm Cardholder Creation
GET /api/v1/cardholder/latest
Once KYC is approved, call this endpoint to confirm the backend has already created a successful cardholder record.
Use the dedicated Cardholder page for the full response shape and behavior details.
Integration Notes
- Upload media first, then validate the document number before creating KYC.
- Treat
validateas a pre-check only; thecreateendpoint still performs the real conflict checks. - Poll or reload
latestafter submission to show status changes in the app. - After approval, use Cardholder to confirm the successful cardholder record exists before entering card flows.