---
title: "Retrieve a customer"
method: GET
path: "/customers/{customer_id}"
tags: ["Customers"]
---

# Retrieve a customer

`GET /customers/{customer_id}`

## Request

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

Retrieves a single customer by its `ozav_cus_` id, including its current
`status`, `verification` posture, and outstanding `requirements`. An id
that does not exist or belongs to another organization returns `404`.

## Response `200`

The customer.

- Customer: An end-user you serve - an individual or a business. The anchor resource that every other object references by its `customer_id`. There is no top-level `name`. The customer's name is `individual.legal_name` (type `individual`) or `business.legal_name` (type `business`). An individual registered outside `POST /customers` (for example, through the onboarding link) has an `individual` block only when OZAV has a name on file, and that block then holds `legal_name` alone.
  - `limits` object, nullable: The limits that apply to this customer, returned only by `GET /customers/{customer_id}`; lists and create responses leave it out. `null` means the limits could not be read right now - retry, and never read it as zero. Amounts are in USD.
    - `organization` object, nullable, required: The limits contracted for your organization, shared by all your customers. `null` when no organization limit is set.
      - `onramp_request_max_usd` number, nullable, required: The largest single purchase request.
      - `withdraw_tier_usd` number, nullable, required: Withdrawals up to this amount need no manual approval.
      - `withdraw_daily_max_usd` number, nullable, required: The most that can be withdrawn per day.
      - `since` string, date-time, nullable, required: When these limits took effect.
    - `customer` object, nullable, required: This customer's own limit currently in force. `null` when none is in force.
      - `monthly_usd` number, nullable, required: The most this customer can move per calendar month.
      - `per_transaction_usd` number, nullable, required: The most per single operation.
      - `since` string, date-time, nullable, required: When this limit took effect.
  - `id` string, required: Opaque customer identifier, prefixed `ozav_cus_`.
  - `object` 'customer', required: Resource type discriminator; always the literal `customer`.
  - `type` 'individual' | 'business', required: Whether the customer is a natural person (`individual`) or a company (`business`).
  - `status` 'pending' | 'in_review' | 'approved' | 'rejected' | 'suspended', required: Onboarding / KYC lifecycle state of a customer. | Value | Meaning | |---|---| | `pending` | Created; verification has not completed. The customer cannot transact yet. | | `in_review` | Checks were submitted and are under (automated or manual) review. | | `approved` | Verification passed. The customer may transact once OZAV has everything a payout will need; see the note below. | | `rejected` | Verification failed; the customer cannot be onboarded. | | `suspended` | A previously active customer has been blocked by a compliance or operational action. | **`approved` is about the checks, not about being able to operate.** OZAV activates a customer only when it also holds everything a payout will need: the holder's name, a valid tax document for the country declared on it, and an address. A Brazilian company with a valid CNPJ is exempt from the address, because the public registry supplies it. A customer can therefore sit at `approved` and still be refused, with `422` on `POST /accounts` and on the money paths. The gap shows up there, before any funds are committed, and not at payout time. The request body cannot resolve it. Send the missing field through the customer update path, or ask OZAV: the blocked queue names each customer and the exact field it is waiting on.
  - `account_status` 'active' | 'pending': Whether the account can operate - a different question from whether the verification passed, which is `status`. `active` means named accounts, cards and payouts are available. `pending` means they are not, and the calls that need them refuse with `422 ACCOUNT_NOT_ACTIVATED`. An approved customer can sit at `pending` for two different reasons. When data is missing, `requirements.due` names it and `requirements.owner` reads `integrator` - send the field with `PATCH /customers/{customer_id}` and the account activates in the same response. When `due` is empty, the account is simply not provisioned yet and nothing is owed by you. Read this field instead of inferring operability from `status`: an approved customer is not necessarily one that can move money.
  - `decision` CustomerDecision: OZAV's compliance decision on this customer, as it stands - the answer to "was it approved, and if not, why, and whose move is it". `status` is finer than the customer's `status`: a customer reads `pending` while OZAV is reviewing and while OZAV is waiting on documents from you, and only `decision.status` tells those apart (`pending_review` vs `information_requested`). Read `requirements.owner` for whose move it is. `reason_code` is a closed vocabulary (below) and `reason` is its English label. Free-text analyst notes never travel: a rejection recorded before this vocabulary existed reads `policy`. Present on `GET /customers/{customer_id}`, on every record of `GET /customers`, and in the `customer.updated` webhook body - which is emitted on approval, on rejection, and when information is requested.
    - `status` 'pending_review' | 'information_requested' | 'approved' | 'rejected' | 'expired', required: - `pending_review` - OZAV is reviewing; nothing is owed by you right now. - `information_requested` - OZAV asked for documents or corrections; `requirements.documents_due` lists them with the deadline. - `approved` / `rejected` - final. - `expired` - nothing was sent for this customer for 30 days while it was your move. Not final and not a rejection: it reopens on its own as soon as you send anything (a document, a profile update, an identity session), and `requirements.owner` stays `integrator`. Treat any status you don't recognise as not final.
    - `reason_code` 'sanctions_match' | 'pep_unmitigated' | 'identity_failed' | 'document_invalid' | 'capacity_unproven' | 'inconsistent_data' | 'restricted_jurisdiction' | 'no_response' | 'fraud_suspected' | 'policy' | 'document_missing' | 'data_inconsistent' | 'identity_session_needed' | 'half_of_limit_reached' | 'registration_incomplete' | 'destination_unidentified': Why a customer was rejected, or why information was requested. Closed vocabulary; each code has a fixed English label in `decision.reason`. Rejection codes: `sanctions_match`, `pep_unmitigated`, `identity_failed`, `document_invalid`, `capacity_unproven`, `inconsistent_data`, `restricted_jurisdiction`, `no_response`, `fraud_suspected`, `policy`. Request codes (also on `requirements.documents_due[].reason_code` and on the `requirement.due` webhook `reason`): `document_missing`, `document_invalid`, `data_inconsistent`, `capacity_unproven`, `identity_session_needed`, `half_of_limit_reached`, `registration_incomplete`, `destination_unidentified`. `destination_unidentified` asks who holds a wallet the customer sent crypto to: the customer's own self-hosted wallet, or the exchange where the customer has an account. Answer it with `POST /customers/{customer_id}/destination-identifications`.
    - `reason` string: English label of `reason_code`. Present only with `reason_code`.
    - `decided_at` string, date-time: When the current decision was recorded. Absent while `pending_review`.
  - `sla` CustomerSla: OZAV's response-time commitment on this customer, as a clock: whose move it is, until when, and whether the deadline has passed. - **Customers you created through this API get a decision within 10 minutes.** Once the record is complete - profile fields filled and the required documents accepted - OZAV answers with `approved`, `rejected` or `information_requested` within 10 minutes of the clock starting. While that clock runs `owner` is `ozav` and `due_at` is the start of OZAV's move plus 10 minutes. - Customers created on OZAV's own desk (not through this API) keep the older commitment of **48 business hours** (Mon-Fri, Brazilian national holidays excluded). - When OZAV asks you for a document or a correction the clock is yours: **30 days** for a document, **15 days** for a correction (`owner` `integrator`; `due_at` mirrors `requirements.documents_due[].due_at`). - `owner` is `none` when the decision is final, when no onboarding case is open, or while OZAV is waiting on a third party (that wait counts against no one). Present on `GET /customers/{customer_id}`, on every record of `GET /customers`, and in the `customer.updated` webhook body.
    - `owner` 'ozav' | 'integrator' | 'none', required: Whose clock is running.
    - `due_at` string, date-time, nullable, required: When the running clock expires. For a customer created through this API while `owner` is `ozav`, that is 10 minutes after OZAV's move began. `null` when `owner` is `none`.
    - `breached` boolean, required: `true` when `due_at` is in the past and the move is still owed. Always `false` when `owner` is `none`. Read at the moment the response is built, so a stored or replayed payload carries the value it had then. Derive it from `due_at` if you need it fresh. For customers created through this API it measures the 10-minute commitment above, so a case that once reported `false` for two days now reports `true` after 10 minutes. It says OZAV is late, and nothing about the customer's own standing.
  - `external_ref` string: Your own identifier for this customer, as supplied at creation.
  - `email` string, email: Contact email for the customer.
  - `phone` string: Contact phone number (E.164).
  - `individual` CustomerIndividual: Identity attributes for an `individual` customer or associated person. Every field except `legal_name` is optional at creation so multi-jurisdiction and incremental onboarding are supported - but onboarding a customer to completion needs more. What is still owed is reported per jurisdiction in `Customer.requirements.missing_fields`; for a Brazilian individual that is `date_of_birth`, `phone`, `email`, a full structured `residential_address`, and `tax_id`.
    - `legal_name` string, required: Full legal name as it appears on the identity document.
    - `mother_name` string: Mother's full name. Accepted and stored; not required for onboarding.
    - `date_of_birth` string, date: Date of birth (ISO 8601 date, YYYY-MM-DD). Validated as a real calendar date, not in the future, at least 18 years ago and not more than 120 - a structurally valid string that is none of those is rejected at create, not accepted and passed on.
    - `nationality` string: Nationality as an ISO 3166-1 alpha-2 country code. It feeds risk assessment, and it does not change which fields are required: the required-field set is driven by the country on `tax_id` (for an individual) or `registration` (for a business), not by nationality and not by the currency of any account you later open. It does not change which other fields are required (the tax-id country drives that branch), but `nationality` is itself reported in `requirements.missing_fields` until you supply it.
    - `phone` Phone: A contact phone split into an international dialing code and the national number, so onboarding requirements that need the phone's country (Brazil among them) can read it without parsing an E.164 string. Both parts are required when a phone is supplied.
      - `country_code` string, required: International dialing code, e.g. `+55` or `55`.
      - `number` string, required: National phone number. At most 20 digits.
    - `is_pep` boolean: Politically-exposed-person self-declaration. Optional. When omitted, OZAV's own screening records the answer: not a PEP when its recent screening read the PEP lists without a match. `true`, or a PEP match, goes to OZAV compliance review.
    - `gender` 'male' | 'female' | 'other': Gender as the person declares it. Optional; never defaulted. Required on the authorized person of a business before USD, EUR or GBP accounts can open. A `PATCH` that leaves it out keeps the value already given.
    - `tax_id` TaxId: A national tax or personal identifier for an individual.
      - `country` string, required: Issuing country as an ISO 3166-1 alpha-2 code.
      - `type` 'cpf' | 'ssn' | 'nif' | 'passport' | 'other', required: The kind of identifier carried in `value`: - `cpf` - Brazilian individual taxpayer registry (Cadastro de Pessoas Físicas). - `ssn` - US Social Security Number. - `nif` - Iberian/European tax identification number (Número de Identificação Fiscal). - `passport` - passport number. - `other` - any identifier not covered above.
      - `value` string, required: The identifier itself, as issued. Formatting characters are not required.
    - `residential_address` Address: A postal address. `line1`, `city`, and `country` are required; the rest are optional. Jurisdictions that structure the address (Brazil among them) need it broken out - provide the discrete `number`, `neighborhood`, and (where relevant) `complement` fields, with `line1` then holding the street name alone. Which of these are required to onboard depends on jurisdiction and is reported in `Customer.requirements.missing_fields` (a Brazilian customer needs `line1`, `number`, `neighborhood`, `city`, `state`, and `postal_code`).
      - `line1` string, required: Street. Where an address is not structured into discrete components, the building number may be included here; when the `number` field is provided, `line1` is the street name alone.
      - `line2` string: Second address line - unit or suite. Optional. See also `complement`.
      - `number` string: Building / house number, kept separate from the street. Optional in general; required for Brazilian onboarding.
      - `neighborhood` string: Neighborhood or district (e.g. Brazilian `bairro`). Optional in general; required for Brazilian onboarding.
      - `complement` string: Address complement - apartment, block, or reference (e.g. Brazilian `complemento`). Optional.
      - `city` string, required: City or municipality.
      - `state` string: State or province - an ISO 3166-2 subdivision code where applicable.
      - `postal_code` string: Postal or ZIP code.
      - `country` string, required: Country as an ISO 3166-1 alpha-2 code (e.g. BR, US).
    - `occupation` string: The person's profession or occupation.
    - `source_of_funds` string: Declared origin of the funds the person will move (e.g. salary, savings, sale of property). Reported in `requirements.missing_fields` until supplied.
    - `monthly_income_usd` number: Self-declared monthly income in USD (amounts are USD-denominated). This is the capacity the expected monthly volume is measured against: without it the economic-capacity assessment has nothing to compare and returns no verdict. `0` is a valid answer (declared no income) and is not treated as absent. Reported in `requirements.missing_fields` until supplied.
    - `expected_monthly_volume_usd` number: Expected monthly transaction movement in USD. This is the amount the economic-capacity assessment measures; what it measures against is the declared capacity (`monthly_income_usd` for an individual, `annual_revenue_usd` for a business). Supplying this alone yields no assessment. Reported in `requirements.missing_fields` until supplied.
    - `account_purpose` string: Intended use of the account / purpose of the relationship.
  - `business` CustomerBusiness: Identity attributes for a `business` customer. Only `legal_name` and `registration` are required at creation; onboarding a business to completion needs more, reported per jurisdiction in `Customer.requirements.missing_fields`. For a Brazilian business that is `phone`, a business email (the customer `email` serves as the business email), a full structured `registered_address`, `company_type`, and `owners` - at least one associated person owning ≥25%, added via `POST /customers/{customer_id}/associated_persons`.
    - `legal_name` string, required: Registered legal name of the company.
    - `trade_name` string: Trading / doing-business-as name, if different from the legal name.
    - `registration` Registration, required: A company registration or incorporation identifier for a business.
      - `country` string, required: Country of registration as an ISO 3166-1 alpha-2 code.
      - `type` 'cnpj' | 'ein' | 'nif' | 'company_number' | 'other', required: The kind of registration identifier carried in `value`: - `cnpj` - Brazilian company registry (Cadastro Nacional da Pessoa Jurídica). - `ein` - US Employer Identification Number. - `nif` - Iberian tax/registry number (Número de Identificação Fiscal). - `company_number` - a national company/registry number (e.g. Companies House). - `other` - any registration identifier not covered above.
      - `value` string, required: The registration number itself, as issued.
    - `incorporation_date` string, date: Date the company was incorporated (ISO 8601 date).
    - `legal_structure` string: Legal form of the entity (e.g. Ltda, LLC, S.A.). Accepted and stored on the customer record, and it is not requested in `requirements.missing_fields`: no automated assessment reads it today, so it is offered for completeness rather than asked for. For Brazil the field an onboarding actually reads is `company_type`.
    - `company_type` string: Company classification. For Brazil the onboarding values are `MEI` (microempreendedor individual) and `PJ` (pessoa jurídica). Required for Brazilian onboarding; optional at creation.
    - `industry` string: Primary industry or line of business.
    - `cnae_codes` string[]: Brazilian CNAE activity codes the company declares, either as 7 digits (`6201501`) or in the notation of the CNPJ card (`6201-5/01`). They are stored as digits only; `6201-5/01` and `6201501` are the same code, and the stored list never repeats one (`uniqueItems` describes the stored list: a request that repeats a code is accepted and stored once, and the 20-code limit counts codes after repeats are removed). The first one is the primary activity. Send an empty array to clear the list. Used only when the public registry (Receita Federal) does not return an activity code for the company's CNPJ; the registry always prevails, and a declared code is recorded as declared by the customer, never as official.
    - `website` string, uri: Company website URL.
    - `phone` Phone: A contact phone split into an international dialing code and the national number, so onboarding requirements that need the phone's country (Brazil among them) can read it without parsing an E.164 string. Both parts are required when a phone is supplied.
      - `country_code` string, required: International dialing code, e.g. `+55` or `55`.
      - `number` string, required: National phone number. At most 20 digits.
    - `registered_address` Address: A postal address. `line1`, `city`, and `country` are required; the rest are optional. Jurisdictions that structure the address (Brazil among them) need it broken out - provide the discrete `number`, `neighborhood`, and (where relevant) `complement` fields, with `line1` then holding the street name alone. Which of these are required to onboard depends on jurisdiction and is reported in `Customer.requirements.missing_fields` (a Brazilian customer needs `line1`, `number`, `neighborhood`, `city`, `state`, and `postal_code`).
      - `line1` string, required: Street. Where an address is not structured into discrete components, the building number may be included here; when the `number` field is provided, `line1` is the street name alone.
      - `line2` string: Second address line - unit or suite. Optional. See also `complement`.
      - `number` string: Building / house number, kept separate from the street. Optional in general; required for Brazilian onboarding.
      - `neighborhood` string: Neighborhood or district (e.g. Brazilian `bairro`). Optional in general; required for Brazilian onboarding.
      - `complement` string: Address complement - apartment, block, or reference (e.g. Brazilian `complemento`). Optional.
      - `city` string, required: City or municipality.
      - `state` string: State or province - an ISO 3166-2 subdivision code where applicable.
      - `postal_code` string: Postal or ZIP code.
      - `country` string, required: Country as an ISO 3166-1 alpha-2 code (e.g. BR, US).
    - `source_of_funds` string: Declared source of the funds the business will move. Reported in `requirements.missing_fields` until supplied.
    - `expected_monthly_volume_usd` number: Expected monthly transaction movement in USD. This is the amount the economic-capacity assessment measures; what it measures against is the declared capacity (`monthly_income_usd` for an individual, `annual_revenue_usd` for a business). Supplying this alone yields no assessment. Reported in `requirements.missing_fields` until supplied.
    - `annual_revenue_usd` number: Declared annual revenue in USD (amounts are USD-denominated). This is the capacity the expected monthly volume is measured against (the assessment reads it as `annual_revenue_usd / 12`): without it the economic-capacity assessment has nothing to compare and returns no verdict. `0` is a valid answer and is not treated as absent. Reported in `requirements.missing_fields` until supplied.
    - `expected_daily_volume_usd` number: Expected daily transaction movement in USD. A separate declaration, not the monthly figure divided by anything: account opening asks for both, and a derived number would be a value the customer never declared. Reported in `requirements.missing_fields` until supplied.
    - `is_regulated_activity` boolean: Whether the business performs a regulated activity. A declared value (including `false`) counts as answered; absent is reported in `requirements.missing_fields`. When `true`, supply `regulator` and `regulated_activity_details`.
    - `regulator` string: Name of the supervising regulator when the activity is regulated.
    - `regulated_activity_details` string: Detail of the regulated activity (licence, scope) when regulated.
    - `nature_of_business` string: Description of what the business does / the services it provides.
    - `aml_program` string: Description of the business's compliance / AML program. Accepted and stored on the customer record, and it is not requested in `requirements.missing_fields`: no automated assessment reads it today, so it is offered for completeness rather than asked for.
    - `operating_jurisdictions` string[]: Countries the business operates in (ISO 3166-1 alpha-2 codes). Accepted and stored on the customer record, and it is not requested in `requirements.missing_fields`: no automated assessment reads it today, so it is offered for completeness rather than asked for. Sanctions screening does not currently read this list.
    - `account_purpose` string: Intended use of the account / purpose of the relationship.
    - `bc_protege_disabled` boolean: Brazilian business only: the company declares it has turned off BC Protege+ (the Central Bank of Brazil service that blocks institutions from opening accounts in the company's name; see https://www.gov.br/pt-br/servicos/proteger-se-com-o-bc-protege), or never turned it on. Required before a BRL account is requested for a business: without `true`, `POST /accounts` refuses with `422 ACCOUNT_PROFILE_INCOMPLETE` and `missing` lists `business.bc_protege_disabled` (sandbox too). Only the company can turn the service off, with its own gov.br login; send `true` once it has. Absent = not declared.
  - `verification` CustomerVerification: The customer's verification posture - the mode applied, the overall status, and per-check results.
    - `mode` 'full' | 'reliance': How this customer is verified: - `full` - OZAV performs the verification checks directly. - `reliance` - you attest, under a reliance arrangement, that verification was performed on your side; available only to organizations holding the `kyc_reliance` entitlement (contractual attestation). Reliance mode does not auto-approve a customer in the public API response; the customer's `status` reflects the current onboarding state. Designated-list screening is always performed by OZAV regardless of mode. **When `mode` is absent.** This field is optional, and its absence is meaningful: OZAV cannot state a verification posture for this record. Treat it as "posture unknown". It is not equivalent to `full`, and must never be read as one. Two different internal situations produce it, and the API does not distinguish them: either no verification posture was ever recorded (typical for records created outside the API v2 onboarding path, which carry no canonical verification block), or a posture exists internally but is not expressible in this API version. Both mean the same thing to you: do not treat this record as verified on the strength of this object. **Precedence against `status`.** `status` is required and is always present. When `mode` is absent, `status` describes the internal onboarding state only and is not a verification attestation - a record can read `status: approved` with no `mode`, and that combination must not be used as evidence that verification was performed. **`requirements.missing_fields` is a lower bound here.** It is still emitted and still useful, but when the canonical block is missing OZAV cannot resolve the record's jurisdiction, so jurisdiction-specific requirements are not listed. Satisfying every field it names may still leave the record incomplete. The record is still returned in full, and other records are unaffected: one incomplete record never fails a call.
    - `status` 'pending' | 'in_review' | 'approved' | 'rejected' | 'suspended': Onboarding / KYC lifecycle state of a customer. | Value | Meaning | |---|---| | `pending` | Created; verification has not completed. The customer cannot transact yet. | | `in_review` | Checks were submitted and are under (automated or manual) review. | | `approved` | Verification passed. The customer may transact once OZAV has everything a payout will need; see the note below. | | `rejected` | Verification failed; the customer cannot be onboarded. | | `suspended` | A previously active customer has been blocked by a compliance or operational action. | **`approved` is about the checks, not about being able to operate.** OZAV activates a customer only when it also holds everything a payout will need: the holder's name, a valid tax document for the country declared on it, and an address. A Brazilian company with a valid CNPJ is exempt from the address, because the public registry supplies it. A customer can therefore sit at `approved` and still be refused, with `422` on `POST /accounts` and on the money paths. The gap shows up there, before any funds are committed, and not at payout time. The request body cannot resolve it. Send the missing field through the customer update path, or ask OZAV: the blocked queue names each customer and the exact field it is waiting on.
    - `checks` object: Per-capability check results, keyed by `CapabilityKind`. Empty until checks have run.
  - `requirements` Requirements: Outstanding items blocking the customer from advancing through verification / onboarding - the honest "here's what's still needed". Present on the create response, on `GET /customers/{customer_id}`, and on every record in `GET /customers`. The list case matters: `CustomerVerification.mode` points here as the signal to read when a verification posture is absent, and that guidance would be unbacked if the field were not guaranteed on the list.
    - `owner` 'integrator' | 'ozav' | 'none': Whose move it is. `integrator` - OZAV is waiting on you: documents due, information requested, an approved customer with a non-empty `due` (see the next field), or a `pending_review` customer whose analysis cannot start yet because the record is incomplete (`missing_fields`) or the identity of a `full` customer is not complete (the hosted verification session has not completed, or its identity document or selfie is still missing or refused). A missing `identity_document` - the individual's own, or an associated person's - also puts `owner` at `integrator`, under `reliance` too. `ozav` - OZAV is reviewing; nothing to do. `none` - the decision is final and nothing is owed: a rejection, or an approval whose `due` is empty. Read `documents_due`, `due`, `missing_fields` and `decision.status` together for what is actually owed. In a list read, `owners` and `representative` are reported in `missing_fields` for every business (associated persons are not counted there) and do not flip `owner` on their own; read the single customer for those two. An approved customer with a non-empty `due` returns `integrator`, not `none`: there is still something for you to send. A rejected customer keeps `none` even with a non-empty `due`: the list survives the verdict, and no field reverses a rejection.
    - `documents_due` DocumentDue[]: Every document OZAV's compliance has asked for on this customer - the ask, its current `status` and its deadline - and why (`reason_code`). It does not shrink. A line stays after the file lands (`submitted`), after it is accepted (`accepted`) and after it is refused (`rejected`): this is the history of the asks, with where each one stands. To know whether anything is owed, read `status` per line, or read `owner` - `owner: none` means nobody owes anything right now. The `requirement.due` webhook announces a new ask; this field is the pull side of the same fact.
      - `key` string, required: The requirement key (e.g. `proof_of_address`, `bank_statement`, `government_id_front`; and `tax_registration` and `pep_declaration`, the two a Brazilian payment institution requires for a BRL account).
      - `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.
      - `label` string, required: Human label of the requirement.
      - `status` 'due' | 'submitted' | 'accepted' | 'rejected', required: `due` - owed by you, the clock runs. `submitted` - a file landed and stopped the clock; OZAV still has to accept it. `accepted` - done. `rejected` - the file was refused; a new `due` line follows with the reason.
      - `due_at` string, date-time, nullable: Deadline for `due` items; `null` when none applies.
      - `reason_code` 'sanctions_match' | 'pep_unmitigated' | 'identity_failed' | 'document_invalid' | 'capacity_unproven' | 'inconsistent_data' | 'restricted_jurisdiction' | 'no_response' | 'fraud_suspected' | 'policy' | 'document_missing' | 'data_inconsistent' | 'identity_session_needed' | 'half_of_limit_reached' | 'registration_incomplete' | 'destination_unidentified': Why a customer was rejected, or why information was requested. Closed vocabulary; each code has a fixed English label in `decision.reason`. Rejection codes: `sanctions_match`, `pep_unmitigated`, `identity_failed`, `document_invalid`, `capacity_unproven`, `inconsistent_data`, `restricted_jurisdiction`, `no_response`, `fraud_suspected`, `policy`. Request codes (also on `requirements.documents_due[].reason_code` and on the `requirement.due` webhook `reason`): `document_missing`, `document_invalid`, `data_inconsistent`, `capacity_unproven`, `identity_session_needed`, `half_of_limit_reached`, `registration_incomplete`, `destination_unidentified`. `destination_unidentified` asks who holds a wallet the customer sent crypto to: the customer's own self-hosted wallet, or the exchange where the customer has an account. Answer it with `POST /customers/{customer_id}/destination-identifications`.
      - `purpose` 'limit_increase' | 'account_opening' | 'registration_data' | 'travel_rule': `limit_increase` - this item does not hold the approval back. It is what OZAV needs to raise the customer's limit above the entry level (economic capacity not yet proven: bank statements, financial statements, tax return). `account_opening` - this item does not hold the approval back either. It is needed to open the customer's named account. `registration_data` - a registration field (declared income or revenue, expected volume) that completes the profile. It does not hold the approval back: until it arrives the risk score uses a conservative provisional value. `travel_rule` - who holds a destination wallet the customer sent crypto to (see `destination`). It does not hold the approval back and blocks no send. Answer it with `POST /customers/{customer_id}/destination-identifications`. A `due` line with any of these purposes does not turn `owner` to `integrator` nor `decision.status` to `information_requested`. Absent - the item is needed for approval.
      - `destination` object: Only on a `travel_rule` item - the wallet the item asks about.
        - `chain` string, required: The network, lowercase (e.g. `tron`, `ethereum`).
        - `address` string, required: The destination address.
      - `requested_by` 'institution': Present only when the institution that opens the customer's account is the one asking for this item (it refused or wants a new copy of the document), not OZAV. Absent when OZAV asked. The item is answered like any other: send the document with `POST /customers/{customer_id}/documents`. Accepted file types are JPG, PNG and PDF.
    - `associated_persons_due` object[]: Which associated persons are holding the business back, and which field is missing on each. Present only when there is at least one; absent means either nothing is missing or the people could not be read, and those are already covered elsewhere (`missing_fields` carries `owners` when none is registered). An empty array would claim "we checked and all of them are complete", which is more than is known when the read did not happen. OZAV screens every person who owns 25% or more, carries the role `ubo`, or signs for the company, and a screening needs a name, a tax id and a date of birth. While any of those is missing on any such person the business stays in review. The field names match what you send to `POST /customers/{customer_id}/associated-persons`, so the fix is a direct write.
      - `id` string: The `associated_person_id` to correct.
      - `missing_fields` string[]: Field names still needed on that person.
    - `due` string[]: What the customer still owes before the account can operate - which is a different question from whether the verification passed. Empty is the normal state; a non-empty `due` on an `approved` customer means the verification is done and the account is still blocked, and `owner` reads `integrator`. The set below is closed and comes from the check that guards withdrawals. The strings are human-readable Portuguese today, not machine tokens, so match them exactly rather than parsing them: `nome do titular` (holder name), `CPF valido` (a valid Brazilian individual tax id), `CNPJ valido` (a valid Brazilian company registration), `documento` (a tax id of any kind, for a non-Brazilian holder), `endereco (logradouro e cidade)` (street and city). A Brazilian company with a valid CNPJ is exempt from the address - the public registry already carries it. One more entry exists only on the organization's own account (the customer that represents your organization itself): `org_self_case_review`. It means OZAV compliance has not yet finished reviewing that account; there is nothing for you to send, and `owner` reads `ozav` while it is the only entry. Capability kinds and document requests can also appear here from the onboarding path; `documents_due` is the structured view of those, with deadline and reason. To clear an entry, send the corresponding field with `PATCH /customers/{customer_id}`. The check re-runs on that call: when it passes, the account activates and this array comes back empty in the same response. It is never removed, only emptied - an absent `due` would be indistinguishable from one nobody computed.
    - `due_codes` string[]: The same thing as `due`, in stable machine tokens - added because `due` carries human-readable Portuguese and some integrators compare it by equality. `due` is unchanged and stays; read whichever fits your code. The vocabulary is the same as `missing_fields`: `legal_name`, `tax_id`, `registration`, `residential_address`, `registered_address`. Two names for the same thing inside one object would be worse than prose. The token depends on the customer type - for a business, a missing document is `registration` and a missing address is `registered_address`. `org_self_case_review` (see `due`) appears here unchanged - it is already a token. Only entries that come from the account check get a code. A document request also lands in `due`, and its structured home is `documents_due` (with deadline and reason) - inventing a token for it here would promise vocabulary nothing emits. So `due_codes` can be shorter than `due`; it is not positional.
    - `missing_fields` string[]: Identity/onboarding fields still required for this customer, computed from its own data and jurisdiction-aware from the tax-id / registration country. Each token is the path of the request field you must supply, minus the `individual.` / `business.` prefix - so `date_of_birth` means `individual.date_of_birth`, and `residential_address` means `individual.residential_address`. Match them exactly; the set below is closed and generated from the code that emits it. For a Brazilian individual: `date_of_birth`, `phone`, `email`, `residential_address` (a full structured address), and `tax_id`. `mother_name` is not in this set - it is still accepted and stored, it just does not hold a customer at `pending`. For a Brazilian business: `phone`, `email`, `registered_address`, `company_type`, and `owners` (at least one associated person owning ≥25%). Outside Brazil a base set applies (`legal_name`, `tax_id` / `registration`, `email`, and the address of the matching block). Independently of jurisdiction, the risk profile is also reported here until answered: `source_of_funds` and `expected_monthly_volume_usd` for both customer types, plus `is_regulated_activity` and `expected_daily_volume_usd` for a business. A declared `false` counts as answered and is not reported. The economic-capacity declaration is reported alongside the expected volume, and the pair is what makes the assessment possible: `monthly_income_usd` for an individual and `annual_revenue_usd` for a business. The expected volume is the amount being measured; the income or revenue is what it is measured against. Supplying only the volume produces no verdict. `0` is a valid answer for either and is not reported as still owed. An individual also owes `occupation`, `nationality` and `account_purpose`. `is_pep` is not reported: the politically-exposed-person declaration is optional, and without it OZAV's own screening records the answer. `regulator` and `regulated_activity_details` are conditional: they are reported only while `is_regulated_activity` is `true`. A `false` answer does not ask for them, and an absent `is_regulated_activity` asks for that question itself rather than for its follow-up. `expected_daily_volume_usd` is a separate declaration, not the monthly figure divided by anything: account provisioning asks for both, and a derived number would be a value the customer never declared. A business also owes `nature_of_business`, `website`, `account_purpose` and `incorporation_date`. They stay optional on `POST /customers` - the customer is created and completed over time - but account opening asks for them later, so they are reported here from the start rather than surfacing as a refusal once your own customer is already waiting. An empty array means nothing is outstanding for the tokens that can be cleared. `owners` is currently not one of them: it is reported for every business regardless of the associated persons on file, because neither read path supplies the qualifying-owner count. An empty array is therefore unreachable for a business, and gating your flow on it will block every business customer permanently. Read the tokens you care about instead of testing the array for emptiness.
  - `created_at` string, date-time: When the customer was created (RFC 3339 / ISO 8601, UTC).
  - `updated_at` string, date-time: When the customer was last modified (RFC 3339 / ISO 8601, UTC).
  - `metadata` Metadata: Free-form string map (max 50 keys).
  - `still_required` CustomerDocumentType[]: The documents this customer will need before an account can be opened for them, given their type and country of registration. Returned on `POST /customers` so you learn the list when the customer is created, not when the account is refused. This is not `requirements.documents_due`, and the difference matters. `documents_due` is what OZAV has formally asked for, each item with a deadline and a reason; right after creation it is empty, because nothing has been asked yet. `still_required` is what the corridor will demand regardless - a Brazilian company needs seven documents (plus four per associated person, reported by `GET /customers/{customer_id}/documents` in `still_required_by_person`) whether or not anyone has asked. Absent, never `[]`, for a customer whose requirement set is not modelled: an empty list would assert "nothing is missing", and that promise is only safe to make for the shapes the gate actually models. The same field, computed the same way, is returned by `GET /customers/{customer_id}/documents` once documents start arriving.

## 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)
