KYC Docs Get credentials
Docs › Blink API › Documents API

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.

list GET …/documents download GET …/documents/bundle auth client key + secret headers
Personal data

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:

POST /api/blink/session/create → 200 OKjson
{
  "sessionId":    "[SESSION_ID]",      ← store this against your customer
  "sessionToken": "[SESSION_TOKEN]",   ← give this to the SDK
  "expiresAt":    "[EXPIRES_AT]",
  "purpose":      "FULL_CAPTURE"
}

List documents

Returns metadata for every file stored for the session. It does not return the bytes.

GET https://kyc-api.blink-pay.net/api/blink/session/{sessionId}/documentshttp
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
200 OKjson
[
  {
    "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
  }
]
FieldTypeDescription
idUUIDThe file's id.
kindstringWhat was captured: DOCUMENT for a document scan, SELFIE for a face capture.
sidestring or nullFRONT or BACK for documents; null for selfies.
seqintegerOrder within the same kind, starting at 1.
contentTypestringMIME type, e.g. image/jpeg.
sizeBytesintegerSize of the stored file.
sha256stringSHA-256 of the stored bytes — use it to verify the download.
createdAtdate-timeWhen Blink stored the file.
purgedbooleantrue once the bytes were deleted by retention. The metadata stays.

Download documents

Returns every stored file for the session as one compressed zip bundle.

GET https://kyc-api.blink-pay.net/api/blink/session/{sessionId}/documents/bundlehttp
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/bundle

The 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

terminalshell
unzip -o blink-documents.zip -d blink-documents
cat blink-documents/MANIFEST.txt
shasum -a 256 blink-documents/*/*   # compare with sha256 from the list call

Example: archive a verification

Run it after the Result API reports COMPLETED:

archive.jsjavascript
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

HTTPMeaningWhat to do
404Unknown session, another client's session, or wrong credentials — deliberately indistinguishable, as on the Result API.Check the stored sessionId and your credentials.
410The files were purged by retention.Nothing to download; the list call still shows the metadata with purged: true.
429Rate 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.