Documentation menu
Documents API
After a session completes, your backend can list the files Blink stored for it — document scans and selfie captures — and download them. Two calls: one for the metadata, one for the files themselves.
These files are identity documents and faces. Call these endpoints only from your backend, store what you download encrypted, restrict who can open it, and delete it on your retention schedule.
Where the sessionId comes from
Both calls take the sessionId of a verification. Your backend receives it from the
Session API when it creates the session — the same response that
carries the sessionToken you hand to the app:
{
"sessionId": "[SESSION_ID]", ← store this against your customer
"sessionToken": "[SESSION_TOKEN]", ← give this to the SDK
"expiresAt": "[EXPIRES_AT]",
"purpose": "FULL_CAPTURE"
}- Save
sessionIdin your database when you create the session. The SDK and the device never return it to you, so this is the only place to get it. - Download documents only after the Result API shows
status: COMPLETEDfor thatsessionId. - The examples below use placeholders. Replace
[SESSION_ID]with your stored value, and set$KEYand$SECRETfrom your client credentials.
List documents
Returns metadata for every file stored for the session. It does not return the bytes.
curl -H "X-Blink-Client-Key: $KEY" -H "X-Blink-Client-Secret: $SECRET" \
https://kyc-api.blink-pay.net/api/blink/session/[SESSION_ID]/documents[
{
"id": "[DOCUMENT_ID]",
"kind": "DOCUMENT",
"side": "FRONT",
"seq": 1,
"contentType": "image/jpeg",
"sizeBytes": 418923,
"sha256": "9f2c…a71b",
"createdAt": "2026-09-14T10:22:41Z",
"purged": false
},
{
"id": "[DOCUMENT_ID]",
"kind": "SELFIE",
"side": null,
"seq": 1,
"contentType": "image/jpeg",
"sizeBytes": 96144,
"sha256": "41de…08c3",
"createdAt": "2026-09-14T10:23:02Z",
"purged": false
}
]| Field | Type | Description |
|---|---|---|
id | UUID | The file's id. |
kind | string | What was captured: DOCUMENT for a document scan, SELFIE for a face capture. |
side | string or null | FRONT or BACK for documents; null for selfies. |
seq | integer | Order within the same kind, starting at 1. |
contentType | string | MIME type, e.g. image/jpeg. |
sizeBytes | integer | Size of the stored file. |
sha256 | string | SHA-256 of the stored bytes — use it to verify the download. |
createdAt | date-time | When Blink stored the file. |
purged | boolean | true once the bytes were deleted by retention. The metadata stays. |
Download documents
Returns every stored file for the session as one compressed zip bundle.
curl -H "X-Blink-Client-Key: $KEY" -H "X-Blink-Client-Secret: $SECRET" \
-o blink-documents.zip \
https://kyc-api.blink-pay.net/api/blink/session/[SESSION_ID]/documents/bundleThe bundle contains the stored files and a MANIFEST.txt listing each file with its SHA-256. Match
files to the list response by id. Every download is recorded in Blink's audit log.
Verify the download
unzip -o blink-documents.zip -d blink-documents
cat blink-documents/MANIFEST.txt
shasum -a 256 blink-documents/*/* # compare with sha256 from the list callExample: archive a verification
Run it after the Result API reports COMPLETED:
const BLINK = 'https://kyc-api.blink-pay.net';
const auth = {
'X-Blink-Client-Key': process.env.BLINK_CLIENT_KEY,
'X-Blink-Client-Secret': process.env.BLINK_CLIENT_SECRET,
};
export async function archiveDocuments(sessionId) {
// 1. Metadata: what was stored, and is it still there?
const list = await fetch(`${BLINK}/api/blink/session/${sessionId}/documents`, { headers: auth });
if (!list.ok) throw new Error(`documents list: ${list.status}`);
const files = await list.json();
if (files.length === 0 || files.every((f) => f.purged)) return { files, bundle: null };
// 2. The files, as one zip.
const r = await fetch(`${BLINK}/api/blink/session/${sessionId}/documents/bundle`, { headers: auth });
if (r.status === 410) return { files, bundle: null }; // purged by retention
if (!r.ok) throw new Error(`documents bundle: ${r.status}`);
const zip = Buffer.from(await r.arrayBuffer());
// 3. Store encrypted, keyed by session — never in a public bucket.
await encryptedStore.put(`kyc/${sessionId}.zip`, zip, { contentType: 'application/zip' });
await db.kycDocuments.insertMany(files.map((f) => ({ sessionId, ...f })));
return { files, bundle: `kyc/${sessionId}.zip` };
}A Dart version follows the same two requests with package:http; send the same two headers.
Errors
| HTTP | Meaning | What to do |
|---|---|---|
404 | Unknown session, another client's session, or wrong credentials — deliberately indistinguishable, as on the Result API. | Check the stored sessionId and your credentials. |
410 | The files were purged by retention. | Nothing to download; the list call still shows the metadata with purged: true. |
429 | Rate limited. | Wait for Retry-After seconds. |
Retention
Blink keeps the stored files for a limited time and then deletes the bytes, keeping the metadata. Download what you
need soon after a session completes rather than relying on Blink as long-term storage. After purging,
purged is true and the bundle returns 410.