Submit user KYC
[POST] /api/v2/user/kyc
Submits one KYC document for a user. Pick the document from the kycDocuments array that Get user KYC state returned, collect its requiredFields, and post them here.
Request
type RequestBody = {
userEmail: string; // required
documentId: string; // required, the _id of a document from kycDocuments
userFields: Record<string, any>; // required, keyed by the document's requiredFields keys
countryIsoCode?: string; // country of the order this KYC is for
kycCountryIsoCode?: string; // country the user chose to verify in, if they chose one
}countryIsoCode and kycCountryIsoCode mirror the read endpoint. Both are optional and fall back to the user's stored country. Pass the same pair you passed to Get user KYC state so the document you submit is one that endpoint offered.
The country rules only run when you send kycCountryIsoCode. When you do, a basic document is accepted only if kycCountryIsoCode equals countryIsoCode and we process payments in that country; otherwise it is a 400 and the user needs an advanced document. Omit kycCountryIsoCode and neither check applies. Separately, and always: a basic pass only satisfies orders in the country it was earned in.
Basic KYC
Basic documents ask for a name, a date of birth and an ID number. Honour each field's regexp and format — they are the provider's own rules and a mismatch is rejected before we ever reach them.
Request body example. This is Nigeria's BVN, the only enabled Nigerian document:
Advanced KYC
Advanced documents replace the ID number with an images array. Its field has type: "smile-identity-images" in requiredFields. Each entry is an object with an image_type_id and an image, forwarded to Smile Identity unchanged:
image_type_id
What it is
2
Selfie
3
Front of the document
7
Back of the document
Send the back of the document only when it has one.
Request body example, using Kenya's national ID:
Response
The response is the same shape as Get user KYC state, re-read after the submission. Verification is asynchronous, so it comes back with currentKycStatus: "initiated".
One difference worth knowing: the re-read carries no amounts, so its requiredKycType is the history-only answer. It is not a verdict on the order you are about to create. Ask Get user KYC state again with the four amount params when you need that.
Poll Get user KYC state until passedKycType reaches the tier you need, or subscribe to the kyc event on KYC and auth webhooks instead of polling. Watch passedKycType rather than currentKycStatus === "approved": some approved records carry the status approved-legacy.
When it is rejected
currentKycStatus: "rejected"
The provider declined it; the reason is in currentKycStatusDescription. The user may try again while reachedKycLimit is false.
400 User already passed basic KYC
The user already holds a basic pass and you submitted another basic document. Submit an advanced one.
400 User already passed advanced KYC
Advanced is the top tier and it is global — there is nothing left to submit. Note this also blocks the Nigerian BVN: an advanced-verified user with isNgBvnBlocked: true cannot put a BVN on file themselves and has to go through support.
400 This document is not supported in the selected country
The documentId belongs to another country.
400 KYC is not available in the selected country
kycCountryIsoCode is a country we do not verify in.
400 Basic verification is not available in the selected country...
A basic document with a kycCountryIsoCode that is not the order country.
400 KYC is not required for this merchant.
KYC is switched off for your account. Skip the flow.
Last updated

