---
title: "Start a verification session for a customer"
method: POST
path: "/verifications"
tags: ["Verifications"]
---

# Start a verification session for a customer

`POST /verifications`

## Request

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

Starts one or more verification checks (hosted session when the check
requires end-user interaction). Check kinds are the canonical OZAV
capability taxonomy.

Verification records persist and can be listed with `GET
/verifications`. Hosted `session_url` issuance is gated by your
organization's capabilities and may be absent.

Without hosted capture (it is off at OZAV), asking a hosted check for
an individual does not return an error: the verification answers
`in_progress`, the hosted checks stay at `review` (never `pass`), there
is no `session_url`, and `pending_documents` lists the documents to send
with `POST /customers/{customer_id}/documents` (`identity_document`,
`selfie`, `proof_of_address`). The field appears only on this response.

Sandbox: a test key (`ozav_sk_test_`) never receives a `session_url` and
never starts a real identity check - a sandbox verification has no real
end-user to redirect. Create the verification, then drive it to its end
state with `POST /verifications/{id}/simulate`.

Reliance: for a customer created with `verification_mode: reliance`
(your organization has the `kyc_reliance` product), your organization
already identified the end user, so OZAV does not start a hosted
identity, liveness or business verification session. Asking for any
hosted check (`kyc_document`, `liveness`, `face_match`,
`age_estimation`, `kyb`, `ubo`) answers `409
VERIFICATION_NOT_REQUIRED_UNDER_RELIANCE`, in sandbox and live alike,
and nothing is created or charged. Send the customer data and the
identity document images with `POST /customers/{customer_id}/documents`
instead; OZAV screens the customer and decides on its own. Checks OZAV
runs itself (`aml_name_screening`, `economic_capacity`) can still be
requested here.

## Request body

- object
  - `customer_id` string, required: The `ozav_cus_` id of the customer to verify.
  - `capabilities` CapabilityKind[], required: The checks to run, from the canonical capability taxonomy (see `CapabilityKind`).
  - `redirect_url` string, uri: Where to return the end-user after a hosted verification session, when one is used.

## Response `201`

Verification session.

- Verification: An identity / business verification session (KYC / KYB) for a customer. Completing or simulating a verification updates the customer's KYC status. In sandbox, approval can materialize simulated wallet and BRL/Pix account projections. Live wallet provisioning and live bank account opening remain separate enabled steps.
  - `id` string, required: Opaque verification identifier, prefixed `ozav_vrf_`.
  - `object` 'verification', required: Resource type discriminator; always the literal `verification`.
  - `customer_id` string, required: The `ozav_cus_` id of the customer being verified.
  - `session_url` string, uri: Hosted verification session URL - redirect the end-user here to complete identity capture (government ID, selfie, liveness; KYB/UBO for a business customer). **In sandbox this field never appears.** A `test` key never starts a hosted session, because a real session bills a third party per attempt. Exercise the outcome with `POST /verifications/{verification_id}/simulate` instead - that is the sandbox affordance, and it lets you drive `approved` or `rejected` yourself. **With a `live` key, two things must both be true**, and the second is not something you control: 1. Your `capabilities` include at least one that hosted capture covers: `kyc_document`, `liveness`, `face_match`, `age_estimation`, `kyb`, `ubo`. Asking only for list/data checks (`aml_name_screening`, `economic_capacity`, ...) never starts a session, because there is nothing for the end-user to do. 2. Hosted capture is switched on at OZAV. This is a single platform-wide setting, **not** something enabled per organization - there is nothing to request for your account. While it is off, no `live` key starts a session. When either condition is missing - or the key is a `test` key - the verification is still created and is a valid record with **no `session_url`**. That is not an error and retrying will not produce one. For an individual on a `live` key with hosted capture off, it is `in_progress` with the hosted checks at `review` and `pending_documents` saying what to upload instead; otherwise it stays `pending` with `checks` at `not_started`. The field is never fabricated: if the session cannot be created, the request fails with a `503` and nothing is persisted, rather than returning a record with a broken link. The `code` says whether retrying helps: `VERIFICATION_SESSION_UNAVAILABLE` (retry shortly), `VERIFICATION_TEMPORARILY_UNAVAILABLE` (on OZAV's side, not caused by your data - retry later with the same data), `VERIFICATION_PROVIDER_REJECTED` or `VERIFICATION_PROVIDER_NOT_CONFIGURED` (contact OZAV support with the `request_id`).
  - `status` 'pending' | 'in_progress' | 'completed' | 'expired', required: Overall state of the verification session: - `pending` - created; checks have not started. - `in_progress` - one or more checks are running. - `completed` - every check reached an outcome. - `expired` - the session expired before completing.
  - `pending_documents` object[]: Only on the `POST /verifications` response, and only when no hosted session was started for an individual: the documents still to send with `POST /customers/{customer_id}/documents`. Absent otherwise.
    - `doc_type` string, required: For example `identity_document`, `selfie`, `proof_of_address`.
    - `state` 'missing' | 'rejected', required
    - `reason` string: Why a rejected file was not accepted.
  - `checks` object: Per-capability check results, keyed by `CapabilityKind`.
  - `created_at` string, date-time: When the verification session was created (RFC 3339 / ISO 8601, UTC).

## Other responses

- `400`: The request is invalid.
- `401`: Missing or invalid API key.
- `403`: The organization lacks the entitlement (code ENTITLEMENT_REQUIRED) or the key lacks the scope (code INSUFFICIENT_SCOPE). On money-moving requests (POST /transfers, POST /wallets/{wallet_id}/transfers) a compliance hold on the customer answers with code COMPLIANCE_HOLD: the hold is reviewed and lifted on OZAV's side; retrying does not clear it. When the only hold is a pause until pending onboarding documents are received, a withdrawal answers CUSTOMER_DOCUMENTS_PENDING instead: send what `requirements` on GET /customers/{id} lists; OZAV lifts the pause after reviewing it. On OZAV Services (live without the platform's KYB), any API outside decisions, screenings, lists and self-custody wallets answers with code PRODUCTION_NOT_ACTIVATED.
- `409`: `VERIFICATION_NOT_REQUIRED_UNDER_RELIANCE` - the customer is under reliance and the request asked for a hosted check. No session was started and nothing was stored. `error.details.hosted_capabilities` lists the checks refused. Do not send the end user to a verification link: send the identity document images with `POST /customers/{customer_id}/documents`. Retrying the same body returns the same error. `VERIFICATION_ALREADY_EXISTS` - a verification already exists for this customer's hosted session; recover it with `GET /verifications?customer_id=`.
- `422`: `FEATURE_NOT_AVAILABLE` - the request asked for a check that OZAV does not offer (`email_verify`, `phone_verify`, `wallet_screening`, `travel_rule`). Nothing was created. `error.details.capabilities_not_available` lists them; send the request again without them.

---

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