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

# List a customer's associated persons

`GET /customers/{customer_id}/associated-persons`

## Request

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

Lists the associated persons of a business customer, using the standard
cursor pagination. Returns a `Page` of `AssociatedPerson`.

## Query parameters

- `limit` integer
- `starting_after` string

## Response `200`

Associated persons.

- object: The uniform envelope every list endpoint returns.
  - `data` AssociatedPerson[], required: The page of results. Each list endpoint documents its own item type.
    - `id` string, required: Opaque associated-person identifier, prefixed `ozav_ap_`.
    - `object` 'associated_person', required: Resource type discriminator; always the literal `associated_person`.
    - `customer_id` string: The `ozav_cus_` id of the business customer this person belongs to.
    - `roles` string[], required: Roles this person holds - one or more of `ubo`, `director`, `officer`, `authorized_signer` (see `AssociatedPersonCreateRequest.roles`).
    - `ownership_percent` number: Percentage of the business this person owns, 0-100.
    - `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.
    - `email` string, email: Contact email for this person.
    - `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 associated person was added (RFC 3339 / ISO 8601, UTC).
  - `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.

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