KYC (new)
Know Your Customer (KYC) verification confirms a customer's identity before you create an account or activate certain features. Gravv requires KYC to comply with financial regulations and prevent fraud.
You can submit documents for verification using two methods: a dedicated interface or the API. Whichever method you use, check the outcome with the Get applicant verification status endpoint.
Required documents
Individual customers must provide a selfie and one government-issued identity document:
- Passport
- National ID card
- Driver's license
Business customers must provide the business's registration certificate. Before you start verification for a business, complete KYC verification for every associated person linked to it.
Submit through a dedicated interface
Use the Start KYC verification endpoint to have a customer upload their identity documents (IDs) through a dedicated interface. The endpoint requires only the customer's ID:
curl --request POST \
--url https://api.gravv.xyz/v1/customers/kyc/start \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: order_789_attempt_1' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"customer_id": "c239895a-0436-4287-a37a-c2664f68d187"
}
'
You'll receive a response that includes the verification status and a web_url link. Share this link with the customer so they can upload their ID documents:
{
"data": {
"review_status": "pending",
"status": "success",
"web_url": "https://in.kyc.com/websdk/p/Cz3WKRuxvlmuS2kp"
},
"error": null
}
Submit through the API
To submit documents through the API, Base64-encode each document, upload it with the Upload document endpoint, then call the Initiate server-to-server verification endpoint.
Upload documents
Upload each required document with the Upload document endpoint. Encode the document in Base64 and send it as the content value. The Idempotency-Key header is required.
curl --request POST \
--url https://api.gravv.xyz/v1/customers/kyc/upload-document \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: passport_upload_001' \
--header 'content-type: application/json' \
--data '
{
"customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c",
"metadata": {
"idDocType": "PASSPORT",
"idDocSubType": "FRONT_SIDE",
"country": "NGA",
"firstName": "John",
"lastName": "Doe",
"number": "A12345678",
"issuedDate": "2020-01-15",
"validUntil": "2030-01-15"
},
"content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAY...",
"filename": "passport_front.jpg"
}
'
A successful upload returns a confirmation:
{
"data": {
"status": "success",
"customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c",
"document_type": "PASSPORT",
"country": "NGA",
"message": "Document saved. Use /start-s2s to submit for verification."
},
"error": null
}
The metadata object describes the document. All idDocType and idDocSubType values are uppercase, and country is a 3-letter uppercase ISO code.
| Field | Required for | Description |
|---|---|---|
idDocType |
All documents | Document type. Individual customers: PASSPORT, ID_CARD, DRIVERS, or SELFIE. Business customers: REGISTRATION_CERTIFICATE. |
country |
All documents | Issuing country as a 3-letter uppercase ISO code, such as NGA. |
idDocSubType |
Identity documents | Document side: FRONT_SIDE or BACK_SIDE. Omit for SELFIE. |
number |
Identity documents and registration certificates | Document identification number. |
issuedDate |
Identity documents | Issue date in YYYY-MM-DD format. |
validUntil |
Identity documents | Expiration date in YYYY-MM-DD format. |
firstName |
Identity documents | First name as shown on the document. |
lastName |
Identity documents | Last name as shown on the document. |
middleName |
Optional | Middle name as shown on the document. |
dob |
Optional | Date of birth in YYYY-MM-DD format. |
placeOfBirth |
Optional | Place of birth as shown on the document. |
Keep these requirements in mind:
- The maximum document size is 10 MB.
- The
filenameextension and the decodedcontenttype must agree, and must be.jpg,.jpeg,.png, or.pdf. - A selfie must be an image (JPEG or PNG); PDF selfies are rejected.
- Omit the
idDocSubTypefield for selfie uploads.
A business customer uploads a registration certificate, which may be a formation document or an ownership document. The idDocType value is always REGISTRATION_CERTIFICATE:
curl --request POST \
--url https://api.gravv.xyz/v1/customers/kyc/upload-document \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: reg_cert_upload_001' \
--header 'content-type: application/json' \
--data '
{
"customer_id": "c84d9a52-2978-43e5-9b86-eac9839e1147",
"metadata": {
"idDocType": "REGISTRATION_CERTIFICATE",
"country": "NGA",
"number": "RC123456"
},
"content": "JVBERi0xLjcKJ...",
"filename": "registration_certificate.pdf"
}
'
Initiate verification
After uploading all required documents, call the Initiate server-to-server verification endpoint using the customer's id:
curl --request POST \
--url https://api.gravv.xyz/v1/customers/kyc/start-s2s \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: kyc_initiate_001' \
--header 'content-type: application/json' \
--data '
{
"customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c"
}
'
Gravv starts the review and delivers the outcome through a webhook.
The API response for a pending verification is as follows:
{
"data": {
"status": "pending",
"message": "KYC verification initiated. Status will be updated via webhook."
},
"error": null
}
For a business customer, the request fails if no registration certificate has been uploaded:
{
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "business customers must upload a REGISTRATION_CERTIFICATE document before starting KYC verification"
}
}
Check verification status
Whether the customer completed verification through the dedicated interface or the API, check the outcome with the Get applicant verification status endpoint. Gravv also notifies you through a webhook when the review finishes.
While the review is in progress, the value of the reviewStatus field is pending and there's no reviewResult field:
{
"data": {
"reviewStatus": "pending",
"priority": 0
},
"error": null
}
Once the review finishes, reviewStatus is completed and reviewResult holds the decision:
{
"data": {
"reviewStatus": "completed",
"reviewResult": {
"reviewAnswer": "approved"
},
"reviewDate": "2026-06-19 21:59:00+0000",
"priority": 0
},
"error": null
}
A rejected review lists the reason codes in rejectLabels, and reviewRejectType indicates whether the customer can resubmit:
{
"data": {
"reviewStatus": "completed",
"reviewResult": {
"reviewAnswer": "rejected",
"rejectLabels": [
"BAD_SELFIE",
"SCREENSHOTS",
"UNSATISFACTORY_PHOTOS"
],
"reviewRejectType": "RETRY"
},
"reviewDate": "2026-06-19 20:44:19+0000",
"priority": 0
},
"error": null
}
The status data object contains:
reviewStatus:pendingwhile the review is in progress, orcompletedonce a decision is returned.reviewResult: the decision, present once the review is completed.reviewAnswer:approvedorrejected.rejectLabels: reason codes for a rejection, present only whenreviewAnswerisrejected.reviewRejectType: the rejection category, such asRETRY, present only whenreviewAnswerisrejected.
reviewDate: the decision timestamp, present once the review is completed.priority: the applicant's review priority.
Get uploaded documents
To see which documents a customer has uploaded, call the Get KYC document data endpoint. It returns the metadata for each uploaded document:
{
"data": {
"customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c",
"documents": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"IDType": "PASSPORT",
"IDSubType": "FRONT_SIDE",
"IDNumber": "A12345678",
"IssuingCountry": "NGA",
"IssuedDate": "2020-01-01",
"ExpiryDate": "2030-01-01",
"has_front_image": true,
"has_back_image": false,
"OriginalFilename": "passport_front.jpg"
}
],
"count": 1
},
"error": null
}