Download a KYC document

List a customer's KYC documents and save one side of one document to a compliance review file.

This recipe lists a customer's KYC documents and saves one side of one document to a compliance review file.

Who it is for. The compliance officer of a payroll platform, who must attach a contractor's uploaded passport to a periodic review file.

Status. Available, for documents uploaded through the API or taken on the hosted page. Not supported for documents uploaded through the card KYC flow.

Money example

No money moves. Before approving a contractor's first 3,000.00 USD monthly payout, the compliance officer re-checks the passport on file. You list the documents, download the passport front and save it to the review file.

Before you start

  • A key with customer:update. The download needs it, and the list does not.
  • The customer has uploaded documents, server to server or on the hosted page.
  • Agree who may see identity documents. The list holds the document number in plain text.

Find out what is required

The documents list returns each document's id and which sides have an image (has_front_image, has_back_image). For documents taken on the hosted page, the list also fetches the images first, so call it before downloading.

Steps

1. List the documents

curl --request GET \
 --url https://api.gravv.xyz/v1/customers/3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71/kyc/documents \
 --header 'Api-Key: <API_KEY>'
const response = await fetch("https://api.gravv.xyz/v1/customers/3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71/kyc/documents", {
method: "GET",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
},
});
console.log(response.status, await response.json());
import os

import requests

response = requests.get(
"https://api.gravv.xyz/v1/customers/3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71/kyc/documents",
headers={
    "Api-Key": os.environ["GRAVV_API_KEY"],
},
)
print(response.status_code, response.json())
{
  "data": {
    "customer_id": "3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71",
    "documents": [
      {
        "id": "a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d",
        "IDType": "PASSPORT",
        "IDSubType": "FRONT_SIDE",
        "IDNumber": "B04512398",
        "IssuingCountry": "NGA",
        "IssuedDate": "2020-02-11",
        "ExpiryDate": "2030-02-10",
        "has_front_image": true,
        "has_back_image": false,
        "OriginalFilename": "passport.jpg"
      }
    ],
    "count": 1
  },
  "error": null
}

Pick the document id and a side that has an image. Do not log IDNumber.

2. Download one side

curl --request GET \
 --url 'https://api.gravv.xyz/v1/customers/3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71/kyc/documents/a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d/download?side=front' \
 --header 'Api-Key: <API_KEY>'
const response = await fetch("https://api.gravv.xyz/v1/customers/3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71/kyc/documents/a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d/download?side=front", {
method: "GET",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
},
});
console.log(response.status, await response.json());
import os

import requests

response = requests.get(
"https://api.gravv.xyz/v1/customers/3f6b2a1e-8c4d-4e7f-9a2b-5c1d0e9f8a71/kyc/documents/a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d/download?side=front",
headers={
    "Api-Key": os.environ["GRAVV_API_KEY"],
},
)
print(response.status_code, response.json())
{
  "data": {
    "content": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBD...",
    "content_type": "image/jpeg",
    "document_id": "a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d",
    "filename": "passport_front-side_60287353-bc08-482b-871c-98305b9a42dd.jpg",
    "side": "front",
    "size_bytes": 57134
  },
  "error": null
}

3. Save the file

  • content is a data URI. Strip the data:image/jpeg;base64, prefix, decode the rest and write the bytes to a file. size_bytes is the file's size.
  • filename is a storage name. Use OriginalFilename from the list for the customer's own name.
  • A two-sided ID is two documents. Read the back image with side=back on the BACK_SIDE row's own id.

What can go wrong

Response Why Fix
400 side is required (front or back) or side must be "front" or "back" side is missing or wrong Send ?side=front or ?side=back
400 invalid document_id format The id is not a UUID Use documents[].id
404 document not found The id belongs to another customer, or does not exist List again for this customer
404 no back image available for this document That side was never uploaded on this row Use the BACK_SIDE row, or the other side
404 no KYC document data found for customer (on the list) Nothing was uploaded. With no documents, the list returns a 404, not an empty list Treat it as "no documents"
404 CUSTOMER_NOT_FOUND: … The customer id or the environment is wrong Check the id
403 {"reason": "forbidden", "required_any": ["customer:update"]} The key lacks customer:update Use a key with that permission
500 failed to download document image, or 503 storage service not configured A storage problem Retry later, and contact support if it persists
The download fails, or returns odd content The document was uploaded through the card KYC flow (/v1/risk/upload-document) Not supported. Keep one upload flow per customer

Webhooks you will receive

This recipe sends no webhooks.

Testing in sandbox

This recipe works with a grvSec_sandbox_ key. Upload a small JPEG for a test customer, list the documents, then download one. The decoded content is the file you uploaded.

Reference

Was this page helpful?