---
title: "List a customer's supporting documents"
method: GET
path: "/customers/{customer_id}/documents"
tags: ["Customers"]
---

# List a customer's supporting documents

`GET /customers/{customer_id}/documents`

## Request

- Base URL: `https://developers.ozav.io/v2`
- URL: `https://developers.ozav.io/v2/customers/{customer_id}/documents`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Lists a customer's uploaded supporting documents, using the standard cursor
pagination. Returns a `Page` of `CustomerDocument`. Never returns the raw
storage location or a download URL.

## Query parameters

- `limit` integer
- `starting_after` string

## Response `200`

Customer documents.

- object: The uniform envelope every list endpoint returns.
  - `data` CustomerDocument[], required: The page of results. Each list endpoint documents its own item type.
    - `id` string: Opaque document id.
    - `object` 'document': Resource type discriminator; always the literal `document`.
    - `customer_id` string: The customer this document belongs to.
    - `subject_type` 'customer' | 'associated_person': Whose document this is. `customer` - the customer itself. `associated_person` - one of a business customer's associated persons, named in `associated_person_id`.
    - `associated_person_id` string: Present when subject_type is associated_person.
    - `doc_type` 'identity_document' | 'selfie' | 'liveness' | 'proof_of_address' | 'company_registration' | 'ownership_structure' | 'financial_statement' | 'source_of_funds' | 'bank_statement' | 'authorized_signature_form' | 'banking_reference' | 'corporate_resolution' | 'good_standing_certificate' | 'source_of_funds_certification' | 'tax_registration' | 'pep_declaration' | 'income_tax_return' | 'other': The kind of KYC/KYB document. Jurisdiction-agnostic - generic categories, never country-specific artifact names - because this API onboards your customers worldwide. **What this list is, and what it is not.** It is the vocabulary for declaring what you are uploading. It is not a per-country list of which artifact satisfies which category: whether a given national ID, driving licence or utility bill is accepted for a specific customer is decided in review, not by this enum, and OZAV does not publish a per-jurisdiction acceptance table. Declare the category that best describes the artifact and upload it; if something is missing or unusable you get a named outcome on the verification, not a silent rejection. The fields a customer must supply are a separate question, answered by `requirements.missing_fields` on the customer itself. That set is driven by the country on `tax_id` / `registration`, and it does not vary by the currency of any account you later open. **Required versus optional, and why nothing here blocks.** For an international account the receiving institution asks for a defined set of company information. Some categories below satisfy part of that set and some do not apply to every company - a Brazilian company, for example, has no separate memorandum of association, because its Contrato Social is both the constitutive act and the articles. So this API accepts every category at any time and refuses none of them for being absent. Upload what the company has, under the category that describes it. **What this API does not do yet, said plainly.** `requirements` on `GET /customers/{id}` reports missing onboarding *fields*, not missing documents - it will not tell you which of the categories above are still outstanding. Document review happens at the receiving institution, and today its outcome reaches you through your account contact, not through this API. Publishing the list here without saying that would suggest a completeness signal that does not exist. A company that cannot produce one of these is not blocked from being created. It is blocked, later and visibly, from having an account opened - and the difference matters: the first is a wall at the door, the second is a list you can work through. **Three categories for the Brazilian named account.** `tax_registration` is the company's tax registration certificate (in Brazil, the Cartão CNPJ). `pep_declaration` is a politically-exposed-person declaration, one per partner or legal representative (`subject_type: associated_person`), optional except for a BRL account. `income_tax_return` is the company's income tax return, accepted as evidence of economic capacity alongside `financial_statement`. A bank statement is **not** accepted as economic capacity by the Brazilian receiving institution; send a balance sheet or the tax return instead.
    - `side` 'front' | 'back' | 'single' | 'null', nullable: Which face of a two-sided document these bytes are. `single` for a document captured in one image. `null` where the document type has no sides (a PDF statement).
    - `issuing_country` string, nullable: ISO 3166-1 alpha-2 country that issued the document, when known. `null` when the document type has no issuer (a proof of address) or it was not supplied.
    - `status` 'pending_upload' | 'received' | 'rejected': `pending_upload` = grant issued, bytes not yet confirmed; `received` = bytes present; `rejected` = failed review.
    - `mime` string, nullable: Content type of the stored bytes, as observed on upload. `null` while `status` is `pending_upload`.
    - `size_bytes` integer, nullable: Size of the stored bytes. `null` while `status` is `pending_upload`.
    - `sha256` string, nullable: SHA-256 of the stored bytes, computed by OZAV on receipt - not a value you send. Compare it against your own digest to prove the upload was not truncated. `null` while `status` is `pending_upload`.
    - `review` DocumentReview: OZAV's own review of a supporting document (proof of address, bank statement, proof of income, company document). The identity document and the selfie are read by OZAV too (the selfie only for a face being present, not liveness or a match with the document); liveness and face match remain a hosted-session result. `rejected` comes with a closed `reason_code` and its English label; a fresh `requirements.documents_due` line follows with a new deadline. The `document.reviewed` webhook carries the same outcome. Omitted (not `not_reviewed`) when the review could not be read at response time.
      - `status` 'accepted' | 'rejected' | 'in_review' | 'not_reviewed', required: - `not_reviewed` - no review yet (or not a document OZAV reviews). - `in_review` - waiting on a compliance officer. - `accepted` - the document satisfies the requirement. - `rejected` - refused; see `reason_code`. Send a new file.
      - `reason_code` 'unreadable' | 'not_the_requested_document' | 'holder_mismatch' | 'holder_is_not_the_company' | 'holder_in_other_script' | 'expired' | 'address_mismatch' | 'low_confidence' | 'tampering_signals' | 'missing_period' | 'not_registered' | 'registry_mismatch' | 'outdated_version' | 'ownership_not_evidenced' | 'holder_shares_address' | 'officer_decision': Why a document was rejected. Closed vocabulary; each code has a fixed English label in `review.reason`.
      - `reason` string: English label of `reason_code`. Present only with `reason_code`.
      - `reviewed_at` string, date-time: When the current review was recorded. Absent while `not_reviewed`.
    - `created_at` string, date-time: When the upload grant was created (RFC 3339, UTC), not when bytes arrived.
    - `updated_at` string, date-time: When the record last changed (RFC 3339, UTC) - the receipt of bytes and a review outcome both move it.
  - `has_more` boolean, required: True when more results exist beyond this page - keep paging with `starting_after`.
  - `next_cursor` string, nullable: Opaque cursor for the next page; pass it as `starting_after`. Null on the last page.
  - `still_required_by_person` PendingDocumentsOfPerson[]: What each associated person still owes. A Brazilian business owes, per ≥25% owner or signer, `identity_document`, `selfie` (or `liveness`) and `proof_of_address`; upload them with `subject_type: associated_person`. `pep_declaration` is optional and never listed here (only a BRL account asks for it, in the `POST /accounts` `missing` list). One entry per person (empty `still_required` when that person is complete). `[]` when this customer has no per-person requirement (individuals, non-Brazilian businesses) - and also when a Brazilian business has not declared any associated person yet: declare them first, then this list fills in (`POST /accounts` keeps asking for `associated_persons.ubo_25_plus` meanwhile). Omitted, like `still_required`, when it could not be measured. `POST /accounts` refuses with the same names, `associated_persons[i].documents.<doc_type>`.
    - `associated_person_id` string, required: The person's public id (`ozav_per_…`), as returned by `GET /customers/{customer_id}/associated-persons`.
    - `name` string: The person's full name, when recorded.
    - `still_required` CustomerDocumentType[], required
    - `pending` PendingDocument[], required
      - `doc_type` 'identity_document' | 'selfie' | 'liveness' | 'proof_of_address' | 'company_registration' | 'ownership_structure' | 'financial_statement' | 'source_of_funds' | 'bank_statement' | 'authorized_signature_form' | 'banking_reference' | 'corporate_resolution' | 'good_standing_certificate' | 'source_of_funds_certification' | 'tax_registration' | 'pep_declaration' | 'income_tax_return' | 'other', required: The kind of KYC/KYB document. Jurisdiction-agnostic - generic categories, never country-specific artifact names - because this API onboards your customers worldwide. **What this list is, and what it is not.** It is the vocabulary for declaring what you are uploading. It is not a per-country list of which artifact satisfies which category: whether a given national ID, driving licence or utility bill is accepted for a specific customer is decided in review, not by this enum, and OZAV does not publish a per-jurisdiction acceptance table. Declare the category that best describes the artifact and upload it; if something is missing or unusable you get a named outcome on the verification, not a silent rejection. The fields a customer must supply are a separate question, answered by `requirements.missing_fields` on the customer itself. That set is driven by the country on `tax_id` / `registration`, and it does not vary by the currency of any account you later open. **Required versus optional, and why nothing here blocks.** For an international account the receiving institution asks for a defined set of company information. Some categories below satisfy part of that set and some do not apply to every company - a Brazilian company, for example, has no separate memorandum of association, because its Contrato Social is both the constitutive act and the articles. So this API accepts every category at any time and refuses none of them for being absent. Upload what the company has, under the category that describes it. **What this API does not do yet, said plainly.** `requirements` on `GET /customers/{id}` reports missing onboarding *fields*, not missing documents - it will not tell you which of the categories above are still outstanding. Document review happens at the receiving institution, and today its outcome reaches you through your account contact, not through this API. Publishing the list here without saying that would suggest a completeness signal that does not exist. A company that cannot produce one of these is not blocked from being created. It is blocked, later and visibly, from having an account opened - and the difference matters: the first is a wall at the door, the second is a list you can work through. **Three categories for the Brazilian named account.** `tax_registration` is the company's tax registration certificate (in Brazil, the Cartão CNPJ). `pep_declaration` is a politically-exposed-person declaration, one per partner or legal representative (`subject_type: associated_person`), optional except for a BRL account. `income_tax_return` is the company's income tax return, accepted as evidence of economic capacity alongside `financial_statement`. A bank statement is **not** accepted as economic capacity by the Brazilian receiving institution; send a balance sheet or the tax return instead.
      - `state` 'missing' | 'rejected', required: `missing` - never uploaded, or declared and never uploaded. A document counts only once its bytes arrive, so one you created but never finished uploading is `missing`, not `rejected`. `rejected` - uploaded and failed review. Do not resend the same file; correct it first.
      - `reason` string: Why the document was rejected, in text meant for a person to act on. Present only when `state` is `rejected`, and only when a reason was recorded: some older rejections have none, and OZAV does not invent one. Its absence never means the document was accepted - `state` is what says that.
  - `still_required` CustomerDocumentType[]: The document categories this customer still owes, computed over all of its documents rather than the page you are reading, so paging does not change the answer. A document counts only once its bytes have arrived: one you declared but never uploaded, and one that failed review, both still appear here. **Read its absence carefully. An empty array and a missing field mean opposite things.** An empty array says the customer owes nothing. The field is omitted when OZAV does not model a required set for this customer, which today means only a `type` that is neither `individual` nor `business`. Individuals and businesses of any country are modelled. Omitted never means "nothing is required": it means we will not guess. It is also omitted if the summary could not be computed, so that a failed read can never be read as a clean bill. The set depends on the customer and the jurisdiction. An individual owes a photo ID and a proof of address (plus a tax return above the high-volume threshold). A business owes its incorporation papers, and its ownership registry where the country and legal form keep them as separate documents. A business registered in Brazil additionally owes the financial pack the local account rail requires. This is guidance, not the gate. The `422` from account opening remains the authoritative checklist, and this list is built to never ask for less than that `422` will.
  - `pending` PendingDocument[]: The same categories as `still_required`, in the same order, each carrying why it is still pending. `still_required` is unchanged and is not deprecated, so an existing integration needs no edit. **Use this one when you resend.** `still_required` cannot tell "never arrived" from "arrived and was rejected", and those ask for opposite actions: the first means upload, the second means fix the document and upload again. Reading only `still_required`, an integrator resends the same rejected file over and over. Omitted under exactly the same conditions as `still_required`, and for the same reason.
    - `doc_type` 'identity_document' | 'selfie' | 'liveness' | 'proof_of_address' | 'company_registration' | 'ownership_structure' | 'financial_statement' | 'source_of_funds' | 'bank_statement' | 'authorized_signature_form' | 'banking_reference' | 'corporate_resolution' | 'good_standing_certificate' | 'source_of_funds_certification' | 'tax_registration' | 'pep_declaration' | 'income_tax_return' | 'other', required: The kind of KYC/KYB document. Jurisdiction-agnostic - generic categories, never country-specific artifact names - because this API onboards your customers worldwide. **What this list is, and what it is not.** It is the vocabulary for declaring what you are uploading. It is not a per-country list of which artifact satisfies which category: whether a given national ID, driving licence or utility bill is accepted for a specific customer is decided in review, not by this enum, and OZAV does not publish a per-jurisdiction acceptance table. Declare the category that best describes the artifact and upload it; if something is missing or unusable you get a named outcome on the verification, not a silent rejection. The fields a customer must supply are a separate question, answered by `requirements.missing_fields` on the customer itself. That set is driven by the country on `tax_id` / `registration`, and it does not vary by the currency of any account you later open. **Required versus optional, and why nothing here blocks.** For an international account the receiving institution asks for a defined set of company information. Some categories below satisfy part of that set and some do not apply to every company - a Brazilian company, for example, has no separate memorandum of association, because its Contrato Social is both the constitutive act and the articles. So this API accepts every category at any time and refuses none of them for being absent. Upload what the company has, under the category that describes it. **What this API does not do yet, said plainly.** `requirements` on `GET /customers/{id}` reports missing onboarding *fields*, not missing documents - it will not tell you which of the categories above are still outstanding. Document review happens at the receiving institution, and today its outcome reaches you through your account contact, not through this API. Publishing the list here without saying that would suggest a completeness signal that does not exist. A company that cannot produce one of these is not blocked from being created. It is blocked, later and visibly, from having an account opened - and the difference matters: the first is a wall at the door, the second is a list you can work through. **Three categories for the Brazilian named account.** `tax_registration` is the company's tax registration certificate (in Brazil, the Cartão CNPJ). `pep_declaration` is a politically-exposed-person declaration, one per partner or legal representative (`subject_type: associated_person`), optional except for a BRL account. `income_tax_return` is the company's income tax return, accepted as evidence of economic capacity alongside `financial_statement`. A bank statement is **not** accepted as economic capacity by the Brazilian receiving institution; send a balance sheet or the tax return instead.
    - `state` 'missing' | 'rejected', required: `missing` - never uploaded, or declared and never uploaded. A document counts only once its bytes arrive, so one you created but never finished uploading is `missing`, not `rejected`. `rejected` - uploaded and failed review. Do not resend the same file; correct it first.
    - `reason` string: Why the document was rejected, in text meant for a person to act on. Present only when `state` is `rejected`, and only when a reason was recorded: some older rejections have none, and OZAV does not invent one. Its absence never means the document was accepted - `state` is what says that.

## Other responses

- `401`: Missing or invalid API key.
- `404`: The resource does not exist, is not visible to the caller, or the id was malformed for this position (see "Resource ids" under Errors). These share one status; `code` and `message` tell them apart.

---

[API](https://skmtc.dev/ozav/apis/ozav-api.md) - [All operations](https://skmtc.dev/ozav/apis/ozav-api/llms.txt) - [OpenAPI document](https://skmtc.dev/ozav/apis/ozav-api/revisions/4133dc2109d7?raw)
