Upload document
Upload a KYC document for individual and business customers. This is the first step of the server-to-server verification flow.
Documents are uploaded as base64-encoded strings along with metadata about the document type, customer information, and document details. The document is validated and stored in preparation for verification.
Supported document types
Individual customers must upload a SELFIE and one identification document: PASSPORT, ID_CARD, or DRIVERS.
Business customers upload a single REGISTRATION_CERTIFICATE, which may be a formation document or an ownership document. The idDocType value is always REGISTRATION_CERTIFICATE.
All idDocType values are uppercase.
Selfie documents must be an image (JPEG or PNG); PDF selfies are rejected.
Document subtypes
Identification documents use idDocSubType to indicate which side is uploaded (case-sensitive, uppercase):
- FRONT_SIDE: Front side of document
- BACK_SIDE: Back side of document (if applicable)
Omit idDocSubType for SELFIE uploads.
File format
The filename extension and the decoded content MIME type must agree and must be one of: image/jpeg, image/png, application/pdf.
Size limits
The maximum decoded document size is 10 MB.
Idempotency
The Idempotency-Key header is required on this endpoint. If you send the same key again with an identical request body, the API returns the original response instead of uploading the document a second time. If you send the same key with a different request body, then the API rejects the request with a 422.
After uploading all required documents, call the Initiate KYC S2S endpoint to start the verification process.
Headers
Unique key to dedupe retries. Required on this endpoint; 1–255 ASCII printable characters. Missing or malformed keys return 400.
Request Body
ID of the customer this document belongs to
Type of document being uploaded. Individual customers upload a SELFIE plus one identification document: PASSPORT, ID_CARD, or DRIVERS. Business customers upload REGISTRATION_CERTIFICATE only.
Possible values: PASSPORT, ID_CARD, DRIVERS, SELFIE, REGISTRATION_CERTIFICATE
Subtype indicating which side of the document. Must be uppercase; lowercase values are passed through to the upstream verifier and may be silently rejected.
Possible values: FRONT_SIDE, BACK_SIDE
Document issuing country (ISO 3166-1 alpha-3 code, uppercase)
First name as shown on document
Middle name as shown on document (optional)
Last name as shown on document
Document issue date (YYYY-MM-DD)
Document expiration date (YYYY-MM-DD)
Document identification number
Date of birth (YYYY-MM-DD)
Place of birth as shown on document
Base64-encoded document content. The decoded bytes must be a JPEG, PNG, or PDF; the service sniffs the leading bytes and rejects other MIME types. Maximum decoded size is 10 MB.
Original filename of the document. Extension must be one of .jpg, .jpeg, .png, .pdf (case-insensitive); the file extension is validated before the content MIME sniff.
Request Examples
{
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"metadata": {
"idDocType": "PASSPORT",
"idDocSubType": "FRONT_SIDE",
"country": "USA",
"firstName": "John",
"middleName": "Michael",
"lastName": "Doe",
"issuedDate": "2020-01-15",
"validUntil": "2030-01-15",
"number": "A12345678",
"dob": "1990-05-20",
"placeOfBirth": "New York, NY"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "passport_front.jpg"
}
{
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"metadata": {
"idDocType": "SELFIE",
"country": "NGA",
"firstName": "John",
"lastName": "Doe",
"dob": "1990-05-20"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "selfie.jpg"
}
{
"customer_id": "c84d9a52-2978-43e5-9b86-eac9839e1147",
"metadata": {
"idDocType": "REGISTRATION_CERTIFICATE",
"idDocSubType": "FRONT_SIDE",
"country": "NGA",
"firstName": "Acme",
"lastName": "Corporation",
"number": "RC123456",
"issuedDate": "2020-01-15"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "registration_certificate.pdf"
}
{
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"metadata": {
"idDocType": "ID_CARD",
"idDocSubType": "FRONT_SIDE",
"country": "NGA",
"firstName": "Jane",
"lastName": "Smith",
"number": "ID987654321",
"issuedDate": "2019-06-10",
"validUntil": "2029-06-10",
"dob": "1992-03-15",
"placeOfBirth": "Lagos"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "id_card_front.jpg"
}
Responses
| Status | Description |
|---|---|
200 |
Document uploaded successfully |
400 |
Invalid request |
404 |
Customer not found |
409 |
Concurrent in-flight request with the same Idempotency-Key (gateway-level) |
413 |
Payload too large. The decoded document exceeds the 10 MB service limit, or the request body exceeded the ingress 15 MB limit. The ingress-layer 413 is returned as HTML by nginx and does not match the schema below. (gateway-level) |
422 |
Idempotency-Key reused with a different request payload (gateway-level) |
500 |
Internal processing error during document upload |
200 response
Always "success" on a successful upload
Possible values: success
ID of the customer the document was saved for
Normalised document type derived from metadata.idDocType
ISO 3166-1 alpha-3 country code as supplied in metadata.country
Human-readable confirmation message
Error information if the request failed
{
"data": {
"status": "success",
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"document_type": "PASSPORT",
"country": "NGA",
"message": "Document saved. Use /start-s2s to submit for verification."
},
"error": null
}
{
"data": {
"status": "success",
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"document_type": "SELFIE",
"country": "NGA",
"message": "Document saved. Use /start-s2s to submit for verification."
},
"error": null
}
400 response
Always null for error responses
Human-readable error message
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "customer_id is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "content is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "filename is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "id_doc_type is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "country is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "number is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "valid_until is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "id_doc_sub_type is required"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "The customer ID provided is not valid"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "invalid document type for customer type"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "invalid document sub type"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "document exceeds maximum allowed size of 10 MB"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "document content is not valid base64"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "unsupported file type \"application/octet-stream\"; allowed types are JPEG, PNG, and PDF"
}
}
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "missing idempotency key in request headers"
}
}
404 response
Always null for error responses
Human-readable error message
{
"data": null,
"error": {
"code": "CUSTOMER_NOT_FOUND",
"message": "Customer not found. Please verify the customer exists in our system."
}
}
409 response
Always null for error responses
Human-readable error message
{
"data": null,
"error": {
"code": "CONFLICT",
"message": "A request with the same Idempotency-Key is currently being processed"
}
}
413 response
Always null for error responses
Human-readable error message
{
"data": null,
"error": {
"code": "PAYLOAD_TOO_LARGE",
"message": "file too large: content exceeds maximum allowed size of 10MB"
}
}
422 response
Always null for error responses
Human-readable error message
{
"data": null,
"error": {
"code": "UNPROCESSABLE_ENTITY",
"message": "Idempotency-Key was previously used with a different request payload"
}
}
500 response
Always null for error responses
Human-readable error message
{
"data": null,
"error": {
"code": "KYC_PROCESSING_ERROR",
"message": "Document processing failed. Please verify your documents and try again."
}
}
Authorization
- ApiKeyAuth