---
title: "Screen a person, business, customer, or wallet address against designated lists"
method: POST
path: "/screenings"
tags: ["Screenings"]
---

# Screen a person, business, customer, or wallet address against designated lists

`POST /screenings`

## Request

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

Point-in-time screening against OZAV's in-house designated-persons
dataset (UN, OFAC, EU, UK and national sources, refreshed daily).
Subjects may be inline (person/business) or reference an existing
customer.

Send an `Idempotency-Key` to retry safely: the same key and body return the same
screening, with `Idempotent-Replayed: true`, and on OZAV Services the retry is not
charged again.

## Headers

- `PAYMENT-SIGNATURE` string
- `X-Ozav-Simulate-Payment` 'required'
- `Idempotency-Key` string

## Request body

- object
  - `subject` union, required: The screened subject. Provide exactly one of `customer_id`, `person`, `business`, or `wallet_address`. Inline subjects (`person` / `business`) enable standalone screening with no customer record.
    - object: Screen an existing customer by id.
      - `customer_id` string, required: The `ozav_cus_` id of an existing customer to screen.
    - object: Screen a person inline (no customer record required).
      - `person` object, required: The natural person to screen. Screened as given - no customer record is created and nothing is stored against a customer.
        - `name` string, required: Full name of the person to screen.
        - `date_of_birth` string, date: Date of birth (ISO 8601 date) - helps disambiguate matches.
        - `country` string: Country of the person as an ISO 3166-1 alpha-2 code.
        - `email` string: Optional contact email. Evaluated for contact signals (disposable mailbox; for businesses also non-existent domain, no mail server, domain age) when contact signals are enabled on the OZAV platform - a platform-wide setting, see `contact_evaluated`. Never stored or echoed back - only the resulting signal is kept, and for a person the email domain is quoted only when it is a public disposable-mail domain.
        - `phone` string: Optional contact phone (E.164 preferred; without `+`, send `country` so the number can be read - otherwise it is not evaluated). Evaluated for contact signals (invalid number, virtual line, country mismatch) when contact signals are enabled on the OZAV platform - a platform-wide setting. Never stored or echoed back.
    - object: Screen a business inline (no customer record required).
      - `business` object, required: The legal entity to screen. Screened as given - no customer record is created and nothing is stored against a customer.
        - `legal_name` string, required: Registered legal name of the business to screen.
        - `country` string: Country of registration as an ISO 3166-1 alpha-2 code.
        - `email` string: Optional contact email. Evaluated for contact signals (disposable mailbox; for businesses also non-existent domain, no mail server, domain age) when contact signals are enabled on the OZAV platform - a platform-wide setting, see `contact_evaluated`. Never stored or echoed back - only the resulting signal is kept, and for a person the email domain is quoted only when it is a public disposable-mail domain.
        - `phone` string: Optional contact phone (E.164 preferred; without `+`, send `country` so the number can be read - otherwise it is not evaluated). Evaluated for contact signals (invalid number, virtual line, country mismatch) when contact signals are enabled on the OZAV platform - a platform-wide setting. Never stored or echoed back.
    - object: Wallet-address screening is not available: this variant returns 422 FEATURE_NOT_AVAILABLE.
      - `wallet_address` object, required: The blockchain address to screen. Not available - sending this variant returns 422 `FEATURE_NOT_AVAILABLE`, it does not screen and return `clear`.
        - `address` string, required: The blockchain address to screen.
        - `network` 'ethereum' | 'base' | 'optimism' | 'polygon' | 'avalanche_c_chain' | 'worldchain' | 'unichain' | 'linea' | 'sonic' | 'sei' | 'celo' | 'zksync_era' | 'solana' | 'tron' | 'stellar', required: Public v2 blockchain network vocabulary for wallet balances and crypto transfer legs. This enum is the wire contract today and lists only networks accepted by public v2 fields now. The custody stack can have additional active networks before those networks are accepted by every public API flow; see `x-ozav-network-matrix` for that explicit split.
  - `lists` string[]: Which designated lists to screen against. Each item is one of: - `sanctions` - consolidated sanctions / designated-persons lists (UN, OFAC, EU, UK and national sources). Consulted for every subject. - `pep` - politically exposed persons. Consulted in-house for subjects whose `country` is `BR` (the Brazilian PEP register, refreshed monthly; `list_source` is `national`). For any other or missing country the list cannot be consulted and the result is `review` with an empty `matches` array. - `adverse_media` - negative-news screening. Not consultable yet: the result is `review` with an empty `matches` array. Defaults to `[ sanctions ]` when omitted.

## Response `201`

Screening result (synchronous when possible).

- Screening: A point-in-time designated-list screening of a subject.
  - `id` string, required: Opaque screening identifier, prefixed `ozav_scr_`.
  - `object` 'screening', required: Resource type discriminator; always the literal `screening`.
  - `subject` union, required: The screened subject. Provide exactly one of `customer_id`, `person`, `business`, or `wallet_address`. Inline subjects (`person` / `business`) enable standalone screening with no customer record.
    - object: Screen an existing customer by id.
      - `customer_id` string, required: The `ozav_cus_` id of an existing customer to screen.
    - object: Screen a person inline (no customer record required).
      - `person` object, required: The natural person to screen. Screened as given - no customer record is created and nothing is stored against a customer.
        - `name` string, required: Full name of the person to screen.
        - `date_of_birth` string, date: Date of birth (ISO 8601 date) - helps disambiguate matches.
        - `country` string: Country of the person as an ISO 3166-1 alpha-2 code.
        - `email` string: Optional contact email. Evaluated for contact signals (disposable mailbox; for businesses also non-existent domain, no mail server, domain age) when contact signals are enabled on the OZAV platform - a platform-wide setting, see `contact_evaluated`. Never stored or echoed back - only the resulting signal is kept, and for a person the email domain is quoted only when it is a public disposable-mail domain.
        - `phone` string: Optional contact phone (E.164 preferred; without `+`, send `country` so the number can be read - otherwise it is not evaluated). Evaluated for contact signals (invalid number, virtual line, country mismatch) when contact signals are enabled on the OZAV platform - a platform-wide setting. Never stored or echoed back.
    - object: Screen a business inline (no customer record required).
      - `business` object, required: The legal entity to screen. Screened as given - no customer record is created and nothing is stored against a customer.
        - `legal_name` string, required: Registered legal name of the business to screen.
        - `country` string: Country of registration as an ISO 3166-1 alpha-2 code.
        - `email` string: Optional contact email. Evaluated for contact signals (disposable mailbox; for businesses also non-existent domain, no mail server, domain age) when contact signals are enabled on the OZAV platform - a platform-wide setting, see `contact_evaluated`. Never stored or echoed back - only the resulting signal is kept, and for a person the email domain is quoted only when it is a public disposable-mail domain.
        - `phone` string: Optional contact phone (E.164 preferred; without `+`, send `country` so the number can be read - otherwise it is not evaluated). Evaluated for contact signals (invalid number, virtual line, country mismatch) when contact signals are enabled on the OZAV platform - a platform-wide setting. Never stored or echoed back.
    - object: Wallet-address screening is not available: this variant returns 422 FEATURE_NOT_AVAILABLE.
      - `wallet_address` object, required: The blockchain address to screen. Not available - sending this variant returns 422 `FEATURE_NOT_AVAILABLE`, it does not screen and return `clear`.
        - `address` string, required: The blockchain address to screen.
        - `network` 'ethereum' | 'base' | 'optimism' | 'polygon' | 'avalanche_c_chain' | 'worldchain' | 'unichain' | 'linea' | 'sonic' | 'sei' | 'celo' | 'zksync_era' | 'solana' | 'tron' | 'stellar', required: Public v2 blockchain network vocabulary for wallet balances and crypto transfer legs. This enum is the wire contract today and lists only networks accepted by public v2 fields now. The custody stack can have additional active networks before those networks are accepted by every public API flow; see `x-ozav-network-matrix` for that explicit split.
  - `lists` string[]: The designated lists that were requested / screened against. Each item is one of: - `sanctions` - consolidated sanctions / designated-persons lists. - `pep` - politically exposed persons. - `adverse_media` - negative-news screening.
  - `status` 'pending' | 'completed', required: Processing state of the screening: - `pending` - the screening is still running. - `completed` - the screening finished; read the outcome from `result`.
  - `result` 'clear' | 'match_found' | 'review', required: Conservative, fail-closed outcome: - `clear` - every requested list was fully consulted with zero matches. - `match_found` - a strong (exact/alias) designated-list match. - `review` - a weaker match, or a requested list that could not be fully consulted; never silently cleared. Telling the two `review` causes apart, because they call for opposite actions and the answer is already in this response - read `matches`: - `review` with a non-empty `matches` - a weak (token-level) hit. A human decides. Retrying changes nothing; the same subject screens the same way. - `review` with an empty `matches` - every list you requested came back without a hit, and at least one of them could not be fully consulted. Nobody matched anything. This is transient on our side: retry later rather than queueing the subject for review. One exception, flagged in `signals`: `pep_exact_name_only` means the name is too common on the PEP register for a word-by-word comparison, so only exact name and aliases were compared. That is not transient - retrying returns the same; queue the subject for human review. The two are exhaustive for `review`, by construction: a weak hit is counted from the same array that `matches` is serialized from, so an empty `matches` leaves an unconsultable source as the only remaining cause. Which source degraded is not published. It is an operational fact about OZAV's ingestion, not about your subject, and the action it implies (retry) is the same whichever one it was.
  - `matches` ScreeningMatch[]: The matches found. Empty when the outcome is `clear` - and also empty on the `review` that means a list could not be consulted, which is why `result` and this field have to be read together. See `ScreeningMatch`.
    - `list_source` 'un' | 'ofac' | 'eu' | 'uk' | 'national', required: Which designated-list source produced the match: - `un` - United Nations Security Council consolidated list. - `ofac` - US OFAC (SDN and related lists). - `eu` - European Union consolidated sanctions list. - `uk` - UK OFSI consolidated list. - `national` - a national designation list.
    - `matched_name` string, required: The name on the list that matched the screened subject.
    - `strength` 'exact' | 'alias' | 'fuzzy', required: How strong the name match is: - `exact` - the names match exactly. - `alias` - the subject matched a known alias of a listed entity. - `fuzzy` - a weaker, token-level match (surfaced for review, never auto-cleared).
    - `list_entry_ref` string: The list's own reference id for the matched entry (e.g. an OFAC SDN number), for audit.
    - `id` string: The hit id (`hit_...`). It is the hit's fingerprint - list, entry, strength and listed name - so the same subject hitting the same entry again carries the same id. Use it with `PATCH /screenings/{screening_id}/hits/{hit_id}`.
    - `status` 'open' | 'false_positive' | 'confirmed': The decision on this hit. Reused by later screenings of the same subject when it is identifiable (`customer_id`, or a person with `date_of_birth`): - `open` - not decided. - `false_positive` - decided as not being the subject; no longer escalates. - `confirmed` - decided as the subject. A confirmed sanctions hit makes `suggested_decision` `decline`. A changed list entry (other strength or listed name) is a new hit and starts `open`.
    - `decided_by` 'you' | 'ozav' | 'null', nullable: Who decided: `you` (your organization, via the API) or `ozav` (OZAV compliance, shown until you decide; your decision prevails). `null` while `open`.
  - `suggested_decision` 'clear' | 'review' | 'decline': OZAV's suggested decision for this subject, computed and stored when the screening ran (reading it later returns that decision, not a recomputation). It only escalates: - `decline` - a strong (exact/alias) sanctions match corroborated by a second attribute: the `date_of_birth` you sent matches the date on the list entry. A name-only match - no date on either side, or a different date - is `review`, never `decline`, because a homonym must not be declined automatically. Consequences: a `business` subject cannot reach `decline` (businesses carry no date of birth), and a `customer_id` subject is screened by name only today (the customer's stored date of birth is not used) - both stop at `review`. - `review` - any list signal (every sanctions or PEP signal escalates), a `high` contact signal, or a `review` result (see `result`). - `clear` - no list match, every requested list consulted, and any contact signals below the escalation threshold (`medium`/`low` contact signals may be present). `result` keeps its list-only meaning; `suggested_decision` also weighs contact signals.
  - `signals` ScreeningSignal[]: Every finding behind `suggested_decision`, explained: what was seen and why it matters. List matches appear here too (with `category` `sanctions_list` or `pep_list`), next to contact signals (`contact`) when email/phone were sent and contact signals are enabled. An empty array is only "nothing found" together with `contact_evaluated` (see below).
    - `code` string, required: Stable machine code of the finding. A plain string, not a closed enum: new codes are added over time, so treat an unknown code by its `category` and `severity`. Known values: - `sanctions_match` / `sanctions_possible_match` - strong / partial sanctions-list name match. - `pep_match` / `pep_possible_match` - strong / partial PEP-register name match. - `disposable_email` - disposable (temporary) mailbox service. - `email_domain_not_found` - the email domain does not exist (businesses). - `email_domain_no_mx` - the email domain has no mail server (businesses). - `email_domain_dns_unresolved` - the email domain's DNS did not answer; not evaluated. - `invalid_email_syntax` - not a name@domain address. - `recently_registered_domain` - company domain registered less than 90 days ago. - `free_email_for_business` - a company using a free email provider. - `domain_without_spf_dmarc` - company domain publishes neither SPF nor DMARC. - `domain_unrelated_to_name` - company domain contains no word of the company name. - `invalid_phone` - not a valid number for the given country. - `non_personal_phone` - virtual, toll-free or premium-rate line. - `phone_country_mismatch` - calling code differs from the declared country. - `org_blocklist_match` - the subject is on your organization's block list. - `org_allowlist_match` - the subject is on your organization's allow list; contact signals were suppressed (block-list, sanctions and PEP matches never are). - `org_lists_unavailable` - your own lists could not be checked; screen again shortly. - `pep_exact_name_only` - the name is too common on the PEP register to be compared word by word, so it was compared by exact name and aliases only. `result` is `review`, and this is not transient: retrying returns the same. Have a person review the subject.
    - `category` 'sanctions_list' | 'pep_list' | 'contact' | 'org_list', required: Where the finding comes from: - `sanctions_list` - a sanctions / designated-persons list. - `pep_list` - a politically-exposed-persons register. - `contact` - the email or phone sent with the subject. - `org_list` - your organization's own block/allow lists (`/lists/entries`).
    - `severity` 'high' | 'medium' | 'low', required: How much the finding weighs. Every list signal (`sanctions_list`, `pep_list`) is `high`. - `high` - escalates the decision on its own. - `medium` - below the escalation threshold; a human weighs it with the rest. - `low` - informational.
    - `evidence` string, required: What was seen, in plain language.
    - `why_it_matters` string, required: Why this finding matters for due diligence.
  - `contact_evaluated` boolean: `true` when the email/phone you sent were fully evaluated. `false` when no email or phone was sent, contact signals are disabled on the OZAV platform (a platform-wide setting, not per account), the evaluation failed, or the email domain's DNS did not answer in time (then `email_domain_dns_unresolved` appears in `signals`). With `false`, the absence of contact signals says nothing about the contact data.
  - `created_at` string, date-time: When the screening was created (RFC 3339 / ISO 8601, UTC).

## Other responses

- `400`: The request is invalid.
- `401`: Missing or invalid API key.
- `402`: OZAV Services only (prepaid): your prepaid credit does not cover this call (code INSUFFICIENT_CREDIT). The call did not run and nothing was charged. `details` carries `balance_usd` and `price_usd`; top up in the portal (Billing) and retry. Organizations on the OZAV platform are invoiced monthly and never receive this. On `POST /decisions` and `POST /screenings`, where OZAV accepts x402 payment, the response also carries an x402 v2 challenge: the `PAYMENT-REQUIRED` header (base64 `PaymentRequired`) and the same object in `details.x402` - the price of this one call in native USDC, paid to OZAV. `accepts` lists one entry per network OZAV receives on, Polygon (`eip155:137`) first; where enabled, a second entry offers Base (`eip155:8453`). Pay on any one of them, signing for that network's USDC (the entry's `asset`); a payment whose `accepted.asset` is not the USDC of its `network` is refused with `network_or_asset_not_accepted`. Retry with the `PAYMENT-SIGNATURE` header to pay it. A payment OZAV does not accept answers `402 X402_PAYMENT_REJECTED` with `details.reason` (`amount_below_price`, `wrong_pay_to`, `network_or_asset_not_accepted`, `authorization_not_yet_valid`, `authorization_expires_too_soon`, `authorization_window_too_long`, `signature_invalid`, `authorization_already_used`, `insufficient_balance`, `settlement_refused`, `payment_malformed` or `payment_not_accepted`) and a new challenge; nothing was charged. If OZAV could not confirm the settlement, `503 X402_PAYMENT_UNAVAILABLE`: retry with the same header, it is never charged twice. A retried payment is credited only once OZAV reads, on-chain, the transaction that used the authorization moving the USDC to OZAV; when that cannot be established automatically, the `503` says the payment needs a manual check (contact support with the request id). Where OZAV also accepts MPP, the response carries an MPP challenge in `WWW-Authenticate: Payment` (method `evm`, intent `charge`, credential type `authorization`, valid for five minutes) and in `details.mpp.www_authenticate`. A credential OZAV does not accept answers `402 MPP_PAYMENT_REJECTED` with `details.reason` (the reasons above, plus `challenge_not_issued_by_ozav`, `challenge_expired` and `nonce_not_bound_to_challenge`); `503 MPP_PAYMENT_UNAVAILABLE` means retry with the same credential.
- `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.
- `429`: Too many requests. Read `code` first. `RATE_LIMIT_EXCEEDED` is the per-minute limit: wait the number of seconds in the `Retry-After` response header before retrying; retrying sooner just consumes budget without being served. A cap with its own code (for example `CREDENTIAL_ISSUANCE_LIMIT_REACHED`, which resets the next day) carries no `Retry-After`, and retrying sooner than its stated reset cannot succeed.
- `503`: A dependency OZAV needs (storage, a resource lookup) could not be reached, so OZAV fails closed rather than guess. Safe to retry with the same `Idempotency-Key` where the operation accepts one.

---

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