# Documents and media

The Tenant API exposes endpoints to fetch information about supported document types and to access uploaded media files (document photos, selfies). Files are uploaded automatically during verification on the OneKYC side.

## Supported document types [#supported-document-types]

Returns the list of document types supported by the platform, with the country list and accepted formats. Required scope: **documents:read**.

API operations:

- `GET /v1/kyc/documents/supported`

## Fetching a media file [#fetching-a-media-file]

Returns a signed download URL for a media file by its identifier. Required scope: **media:read**.

API operations:

- `GET /v1/kyc/media/{media_id}`

> **Note.** Media identifiers are included in the applicant record and in the applicant document list. The signed URL has a limited lifetime.

## How to download files after a webhook [#how-to-download-files-after-a-webhook]

When you receive a webhook (for example, `session.completed` or `verification.approved`), the payload contains:
- `applicant_id` — applicant identifier
- `media` — an object with storage keys (informational; not API identifiers)

**Recommended order:**

1. **Fetch applicant data** — `GET /v1/kyc/applicants/{applicant_id}`. The response contains ready-to-use signed URLs:
   - `document_photo_url`, `document_front_url`, `document_back_url` — document photos
   - `portrait_photo_url` — portrait from the document
   - `selfie_photo_url` — selfie
   - `avatar_photo_url` — avatar
   - `photo_url_expires_at` — URL expiration

2. **Or fetch the document list** — `GET /v1/kyc/applicants/{applicant_id}/documents`. If you only have your own identifier, use `GET /v1/kyc/applicants/external/{external_id}/documents`. The response contains the `id` of each document (UUID), which you can pass to `GET /v1/kyc/media/{media_id}`.

3. **Download the file** through the signed URL (HTTP GET, no auth). The URL is valid for a limited time.

> **Important.** The `media` field in the webhook holds internal storage keys, not API identifiers. To download files, use the signed URLs from the `GET /v1/kyc/applicants/{applicant_id}` response.

## Applicant documents [#applicant-documents]

Returns the documents uploaded by a specific applicant during verification. Required scope: **documents:read**.

API operations:

- `GET /v1/kyc/applicants/{applicant_id}/documents`

## Applicant documents by external ID [#applicant-documents-by-external-id]

Returns the same document list by the `external_id` you supplied when creating the verification link. Required scope: **documents:read**.

API operations:

- `GET /v1/kyc/applicants/external/{external_id}/documents`

## Document collections and document sides [#document-collections-and-document-sides]

A document step remains in compatible single-document mode when `collection` is absent. Configure `collection.min_documents`, `max_documents` (`0` means unlimited), accepted countries/types and review rules in the Flow Builder. Empty accepted lists permit all provider-supported values; suggested values do not restrict recognition.

Each logical document has an immutable ID and incrementing revision. Two sides of an ID card or residence permit count as one document. Upload both files with `file_type: document` and optional `file_side: front` / `back`, then send `collection_submit` with `document_storage_key` / `file_id` plus optional `back_document_storage_key` / `back_file_id`. Older collection clients using `document_back_storage_key` / `document_back_file_id` remain compatible; conflicting aliases are rejected. Legacy `execute_step` continues accepting `document_back_storage_key` / `document_back_file_id`. Single-file requests are unchanged. The current KBY AI multipage endpoint accepts a first image and one optional second image; it does not accept an arbitrary number of pages.

Results retain separate citizenship and residence-right claims, each linked to source document ID and revision. An issuing country alone never establishes citizenship. Residence permits establish the stated residence right, not a verified current address. Unknown owner identity and contradictory evidence remain explicit and follow configured review behavior. Failed documents remain visible for review and do not contribute confirmed claims.

Automatic document requests support global conditions, issuer/nationality conditions, and targeted local overrides. Conditions within a rule match the same source document. Country sources can use a fixed country, the source issuing country, or source nationality. Rule requirements must be satisfied before collection completion.

Admin document views display each document's issuer, explicit nationality, recognized fields, original sides and separate authenticity results. `document_order` keeps displayed numbering stable even when a primary document is selected for compatible legacy fields. `doc_liveness_sides` preserves front/back scores separately.

KYB document analysis archives the full evidence and exact AI HTTP response bytes, including unknown fields, with a private tenant/session/execution binding. Cached analysis retains these bytes. A committed attempt does not publish a second completion on message redelivery. Authorized operators download archives through the KYB raw-response endpoint with regulated-media step-up and an audit event. Historical responses that were never archived cannot be reconstructed and are shown as unavailable.

The admin session-documents response includes `steps[].document_collection` while a collection is active, including processing and failed documents. Latest revisions are merged into the current attempt without duplicating original files. Expired documents require review unless `expiry_check.reject_expired` explicitly enables rejection; a strict validation failure is never downgraded by another review reason.

Finite collection limits must leave room for each active conditional rule’s source document and its requested documents. A permit-to-passport rule requiring one additional document needs a maximum of at least two; unconditional requirements do not reserve a source slot. Disabled rules do not consume capacity. The Builder shows this error before saving, and the backend validates the same constraint.
