Gravv docs
Docs Agent Sign in Get API keys

Upload a KYC document

Stores one document file for a customer. This is step 1 of server-to-server verification. Upload each document, and each side of a two-sided card, in a separate call. Nothing is sent for review until you call POST /v1/customers/kyc/start-s2s.

Individual and UBO customers need one identity document (PASSPORT, ID_CARD or DRIVERS) with FRONT_SIDE, plus a SELFIE. Identity documents also need country, number and validUntil. A selfie must be a JPEG or PNG.

Business customers use idDocType: COMPANY_DOC for every file and set idDocSubType to INCORPORATION_CERT, SHAREHOLDER_REGISTRY, PROOF_OF_ADDRESS and TAX_DOCUMENT (or OTHER).

Uploading the same type and subtype again replaces the earlier file. On tenants with automatic document verification, the upload that completes the required set also completes KYC.

POSThttps://api.gravv.xyz/v1/customers/kyc/upload-document

Headers

Required for POST requests. 1 to 255 printable ASCII characters. A retry with the same key and body within 24 hours returns the first response.

Request Body

ID of the customer this document belongs to

Type of document. Individual and UBO customers may upload PASSPORT, ID_CARD, DRIVERS, SELFIE, PROOF_OF_ADDRESS or TAX_DECLARATION. To start verification they need one identity document (PASSPORT, ID_CARD or DRIVERS) with a front image, plus a SELFIE. Business customers upload only COMPANY_DOC, once for each idDocSubType.

Possible values: PASSPORT, ID_CARD, DRIVERS, SELFIE, PROOF_OF_ADDRESS, TAX_DECLARATION, COMPANY_DOC

Which part of the document this is. Values are case-sensitive. Identity documents need FRONT_SIDE, and BACK_SIDE as a separate upload for two-sided cards. COMPANY_DOC needs one of INCORPORATION_CERT, SHAREHOLDER_REGISTRY, PROOF_OF_ADDRESS, TAX_DOCUMENT or OTHER. A business needs each of the first three, plus TAX_DOCUMENT or OTHER. Omit it for SELFIE, PROOF_OF_ADDRESS and TAX_DECLARATION.

Possible values: FRONT_SIDE, BACK_SIDE, INCORPORATION_CERT, SHAREHOLDER_REGISTRY, PROOF_OF_ADDRESS, TAX_DOCUMENT, OTHER

Issuing country as an ISO 3166-1 alpha-2 or alpha-3 code. Required for every type except SELFIE. The response gives it as alpha-3.

Document number. Required for PASSPORT, ID_CARD and DRIVERS.

Expiry date as YYYY-MM-DD. Required for PASSPORT, ID_CARD and DRIVERS. The format is not checked here, but the verification review may reject a bad date.

Issue date as YYYY-MM-DD.

First name as shown on the document

Middle name as shown on the document

Last name as shown on the document

Date of birth as YYYY-MM-DD.

Place of birth as shown on the document

The file as plain standard base64, with no data: prefix. The decoded file must be a JPEG, PNG or PDF. The service checks the file bytes, not the name. The maximum decoded size is 10 MB. In sandbox, keep files under about 500 KB.

Name of the file. The extension is not checked. The content decides the file type.

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.png"
}
{
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"metadata": {
"idDocType": "SELFIE"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "selfie.png"
}
{
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"metadata": {
"idDocType": "ID_CARD",
"idDocSubType": "FRONT_SIDE",
"country": "NG",
"firstName": "Jane",
"lastName": "Smith",
"number": "ID987654321",
"issuedDate": "2019-06-10",
"validUntil": "2029-06-10"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "id_card_front.png"
}
{
"customer_id": "c84d9a52-2978-43e5-9b86-eac9839e1147",
"metadata": {
"idDocType": "COMPANY_DOC",
"idDocSubType": "INCORPORATION_CERT",
"country": "NGA"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "incorporation_certificate.png"
}

Responses

Status Description
200 Document stored
400 Invalid request
404 Customer not found
409 A request with the same Idempotency-Key is still running. Retry after the Retry-After header.
413 The file or the request body is too large.
422 The Idempotency-Key was used before with a different body.
500 The document could not be stored
503 The service is not configured or not reachable. Retry later.

200 response

Always success.

Possible values: success

ID of the customer the document was saved for

The idDocType you sent, in upper case

The idDocSubType you sent

ID of the stored document. Uploading the same type and subtype again replaces the file and keeps this ID.

Issuing country as an alpha-3 code

Confirmation text. Despite its wording, the next call in the server-to-server flow is POST /v1/customers/kyc/start-s2s.

completed when this upload completed verification at once. Only tenants with automatic document verification see it.

Possible values: completed

{
"data": {
"status": "success",
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"document_type": "PASSPORT",
"document_sub_type": "FRONT_SIDE",
"document_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
"country": "USA",
"message": "Document saved. Use /start-kyc to submit for verification."
},
"error": null
}
{
"data": {
"status": "success",
"customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146",
"document_type": "SELFIE",
"document_sub_type": "",
"document_id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f6a",
"country": "NGA",
"message": "Document saved. KYC auto-approved: all required documents received.",
"kyc_status": "completed"
},
"error": null
}

400 response

Always null for error responses

Error message. Most messages start with an upper-case code and a colon, such as INVALID_REQUEST: ....

{
"data": null,
"error": "INVALID_REQUEST: customer_id is required"
}
{
"data": null,
"error": "INVALID_REQUEST: content (base64 image) is required"
}
{
"data": null,
"error": "INVALID_REQUEST: filename is required"
}
{
"data": null,
"error": "INVALID_REQUEST: metadata.country is required"
}
{
"data": null,
"error": "INVALID_REQUEST: metadata.number is required for identity document uploads"
}
{
"data": null,
"error": "INVALID_REQUEST: metadata.idDocSubType is required for business document uploads (e.g. INCORPORATION_CERT, SHAREHOLDER_REGISTRY, PROOF_OF_ADDRESS, TAX_DOCUMENT)"
}
{
"data": null,
"error": "INVALID_REQUEST: document type \"PASSPORT\" is not allowed for business customers; allowed type: COMPANY_DOC"
}
{
"data": null,
"error": "INVALID_REQUEST: document content is not valid base64"
}
{
"data": null,
"error": "INVALID_REQUEST: unsupported file type \"application/octet-stream\"; allowed types are JPEG, PNG, and PDF"
}
{
"data": null,
"error": "missing idempotency key in request headers"
}

404 response

Always null for error responses

Error message. Most messages start with an upper-case code and a colon, such as INVALID_REQUEST: ....

{
"data": null,
"error": "CUSTOMER_NOT_FOUND: Customer not found. Please verify the customer exists in our system."
}

409 response

Possible values: idempotency_in_flight, idempotency_key_reused

{
"data": null,
"error": {
"code": "idempotency_in_flight",
"message": "A request with the same Idempotency-Key is currently being processed"
}
}

413 response

Always null for error responses

Error message. Most messages start with an upper-case code and a colon, such as INVALID_REQUEST: ....

{
"data": null,
"error": "the uploaded file is too large — the maximum is 10MB"
}

422 response

Possible values: idempotency_in_flight, idempotency_key_reused

{
"data": null,
"error": {
"code": "idempotency_key_reused",
"message": "Idempotency-Key was previously used with a different request payload"
}
}

500 response

Always null for error responses

Error message. Most messages start with an upper-case code and a colon, such as INVALID_REQUEST: ....

{
"data": null,
"error": "failed to get KYC information"
}

503 response

Always null for error responses

Error message. Most messages start with an upper-case code and a colon, such as INVALID_REQUEST: ....

{
"data": null,
"error": "verification provider not configured"
}

Authorization

  • ApiKeyAuth
Was this page helpful?
ESC