KYC
Transaction endpoints need a verified user. Submit each end user's KYC once, wait for it to reach
VERIFIED, then transact on their behalf.
Every endpoint here is keyed by userEmail. Within your partner account, that email is the user's
identity.
Status values
| Status | Meaning |
|---|---|
NONE | Nothing submitted. |
PROCESSING | Submitted and under review. |
VERIFIED | Approved. The user can transact. |
REJECTED | Not approved. rejectReason explains why; correct it and submit again. |
Submit KYC
POST /api/v2/user/submit-kyc
Signed string
partnerCode|userEmail|nationality|<secretKey>
Request
The identity document, the two document photos, the selfie and the KYC report are all required. If
a submission is missing one, it comes back as errorCode 5
(INVALID_REQUEST_DATA), and the message names what was missing:
The submitted data is invalid. Please check and try again. Missing required fields: national ID, front image, selfie image.
| Field | Type | Required | Notes |
|---|---|---|---|
partnerCode | string | Yes | |
userEmail | string | Yes | The user's identity. |
signature | string | Yes | |
firstName | string | Yes | Stored uppercase, with accents removed — it reads back that way. |
lastName | string | Yes | Same. |
nationality | string | Yes | ISO 3166-1 alpha-2, uppercase — VN, PH, BR. Checked against a list. |
type | string | Yes | PASSPORT, ID_CARD, DRIVERS_LICENSE, or RESIDENCE_PERMIT. |
nationalId | string | Yes | The ID or passport number, accepted in any format. Reusing one that belongs to another of your users is rejected with 52. |
dateOfBirth | string | Yes | YYYY-MM-DD. |
gender | string | No | male or female. |
issueDate | string | No | YYYY-MM-DD. |
expiryDate | string | Yes | YYYY-MM-DD. Accepted as sent, including a date in the past. For a document that never expires, send its issue date. |
frontIdImage | string | Yes | A base64 data URL — see below. |
backIdImage | string | Yes, unless PASSPORT | Only a passport has no back side, so it is ignored for one. Omitting it for any other type fails with 58 rather than 5 — see below. |
holdIdImage | string | Yes | A selfie of the user, as a base64 data URL. |
kycReport | string | Yes | A KYC report PDF, as a base64 data URL — data:application/pdf;base64,…. |
addressLine1 | string | No | |
addressLine2 | string | No | |
city | string | No | |
state | string | No | Falls back to city if empty. |
zipCode | string | No | |
phoneNumber | string | No | |
phoneCountryCode | string | No | Digits only, without the + — 84, 63, 1. Checked against a list of calling codes. |
Format the images and the report as data URLs, prefix included: data:image/jpeg;base64,/9j/4AAQ….
A bare base64 string is rejected with 58.
The same format check gives a missing backIdImage its error code: a non-passport document without
one returns 58 rather than 5, and data.invalid names the document concerned.
Response data
| Field | Type | Notes |
|---|---|---|
id | number | The KYC record id, distinct from the user's nationalId. |
kycStatus | string | PROCESSING — every submission goes to review. |
signature | string | Over id|kycStatus|secretKey. |
{
"success": true,
"message": "Your request has been successful",
"data": { "id": 90412, "kycStatus": "PROCESSING", "signature": "K3tQ...==" },
"errorCode": 0,
"semanticCode": "SUCCESS"
}
Errors — plus the shared ones.
27 USER_EMAIL_INVALID | userEmail is malformed. |
5 INVALID_REQUEST_DATA | A required field or document is missing — the message names them. |
35 USER_KYC_UNDER_PROCESSING | A submission is already in flight. |
52 KYC_DOCUMENT_ALREADY_REGISTERED | Already verified, or that nationalId belongs to another of your users. |
58 KYC_DOCUMENT_FORMAT_INVALID | A document is not a valid data URL. |
Supplement a KYC profile
POST /api/v2/user/submit-kyc-supplement
Adds fields that a VERIFIED user's profile is missing, typically fields that joined the required set
in Submit KYC after the user was verified. A user who has not been verified goes
through Submit KYC instead.
Send only the fields you are filling in; the fields you leave out keep their current values. A
request with no fields to update returns errorCode 5.
kycStatus is untouched, so the user stays VERIFIED and keeps transacting while the supplement is
reviewed. What moves is kycSupplementStatus, which reads SUBMITTED once the call succeeds.
Signed string — it does not cover the fields you are patching.
partnerCode|userEmail|<secretKey>
Request — every field below carries the same format and meaning as in Submit KYC. Required here means this call rejects the request without it.
| Field | Type | Required | Notes |
|---|---|---|---|
partnerCode | string | Yes | |
userEmail | string | Yes | Identifies the profile to patch. |
signature | string | Yes | |
firstName, lastName | string | No | Uppercased and stripped of accents, as on submit. |
type | string | No | PASSPORT, ID_CARD, DRIVERS_LICENSE, or RESIDENCE_PERMIT. Switching to PASSPORT clears the stored back image, and a backIdImage sent with it is ignored. |
nationalId | string | No | Changing it to one already held by another of your users is rejected with 52. |
dateOfBirth, issueDate, expiryDate | string | No | YYYY-MM-DD. For a document that never expires, expiryDate is its issue date. |
gender | string | No | male or female. |
nationality | string | No | ISO 3166-1 alpha-2. |
frontIdImage, backIdImage, holdIdImage | string | No | Base64 data URLs — data:image/jpeg;base64,…. Each one you send replaces the stored image. |
kycReport | string | No | A KYC report PDF, as a base64 data URL — data:application/pdf;base64,…. |
addressLine1, addressLine2, city, state, zipCode | string | No | |
phoneNumber, phoneCountryCode | string | No |
Response data
| Field | Type | Notes |
|---|---|---|
id | number | The KYC record id. |
kycStatus | string | The main status. This call does not change it. |
kycSupplementStatus | string | SUBMITTED. |
signature | string | Over id|kycStatus|secretKey, using the main status rather than the supplement one. |
{
"success": true,
"message": "Your request has been successful",
"data": {
"id": 90412,
"kycStatus": "VERIFIED",
"kycSupplementStatus": "SUBMITTED",
"signature": "K3tQ...=="
},
"errorCode": 0,
"semanticCode": "SUCCESS"
}
Errors — plus the shared ones.
27 USER_EMAIL_INVALID | userEmail is malformed. |
5 INVALID_REQUEST_DATA | You sent no patchable field at all. |
50 REQUESTED_INFO_NOT_FOUND | No such user, or the user has no KYC profile — they were never submitted. |
52 KYC_DOCUMENT_ALREADY_REGISTERED | The new nationalId belongs to another of your users. |
58 KYC_DOCUMENT_FORMAT_INVALID | A document you sent is not a valid data URL. The message names which. |
Markets that need extra profile fields
Scan-to-pay in BRL, ARS and PEN needs the user's address and gender when the user pays in the
currency of their own nationality: a Brazilian user paying in BRL, an Argentine user in ARS, or a Peruvian user in
PEN. If either is missing, review-sell-order with a userEmail and create-sell-order fail with errorCode 142
KYC_EXTRA_INFO_REQUIRED_FOR_LATAM_LOCAL_USER, and the message names the fields to add:
Additional KYC information is required for payments in your local currency. Missing: address, gender. Please update your profile and try again.
The fields after Missing: are comma-separated.
Send them with Submit KYC to avoid this. For a user who is already verified, add the missing ones
(addressLine1, city, state and zipCode for the address; gender) with
Supplement a KYC profile and retry the order. While we complete the user's verification
for that market, the order fails with 61 KYC_PROFILE_UNDER_REVIEW; retry it after a few minutes.
Get KYC information
POST /api/v2/user/get-kyc-information
Signed string
partnerCode|userEmail|<secretKey>
Request — partnerCode, userEmail, signature. All required.
Response data
| Field | Type | Notes |
|---|---|---|
kycStatus | string | PROCESSING, VERIFIED, or REJECTED. |
kycSupplementStatus | string | NONE, SUBMITTED, REJECTED, or APPROVED. The state of a supplement, independent of kycStatus. |
rejectReason | string | Only when REJECTED. |
firstName, lastName | string | |
dateOfBirth, issueDate, expiryDate | string | YYYY-MM-DD. |
gender | string | |
nationality | string | |
nationalId | string | |
addressLine1, addressLine2, city, state, zipCode | string | |
phoneNumber, phoneCountryCode | string | |
frontUrl, backUrl, holdUrl | string | URLs, not the base64 you uploaded. backUrl is absent for a passport. |
signature | string | Over nationalId|kycStatus|secretKey. |
The images come back as URLs rather than the base64 you uploaded.
The profile does not carry the document type, so keep your own record of whether you submitted a
passport or an ID card.
A REJECTED profile returns kycStatus, kycSupplementStatus, rejectReason and signature
only. Because nationalId belongs to the signed string, its segment is signed as the literal text
undefined:
undefined|REJECTED|<secretKey>
Response signature verification needs to handle this case.
Errors — 50 REQUESTED_INFO_NOT_FOUND when the user does not exist or has never submitted KYC,
plus the shared ones. A rejected profile arrives as a success envelope, with the outcome in
kycStatus.
Check service access
POST /api/v2/user/check-access-service
Asks whether a user is allowed to use a service in a given currency — verified KYC, permitted nationality. Call it before quoting, so you fail early with a reason instead of failing at order creation.
Signed string
partnerCode|userEmail|fiatCurrency|service|<secretKey>
Request
| Field | Type | Required | Notes |
|---|---|---|---|
partnerCode | string | Yes | |
userEmail | string | Yes | |
fiatCurrency | string | Yes | Accepted as sent, so send a valid currency code — VND, PHP, BRL. |
service | string | Yes | One of SCAN_TO_PAY, ON_RAMP, OFF_RAMP. |
signature | string | Yes |
Response data
| Field | Type | Notes |
|---|---|---|
access | boolean | true on success. |
signature | string | Over access|secretKey — so the signed string is true|<secretKey>. |
A permitted user answers access: true. A user who may not transact answers with an error envelope
that names the reason:
34 USER_KYC_VERIFICATION_REQUIRED | No profile, or KYC is not VERIFIED. |
59 NATIONALITY_NOT_SUPPORTED | Nationality missing, or blocked for this service and currency. |
So branch on success rather than on data.access.