---
title: "Risk signals for an email, a phone and an IP address"
method: POST
path: "/signals"
tags: ["Screenings"]
---

# Risk signals for an email, a phone and an IP address

`POST /signals`

## Request

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

Evaluates an email, a phone number and an IP address and returns explained risk signals, with
no customer and no stored record. Send at least one of the three.

- Email: syntax, disposable mailbox services and, for a `business`, the domain itself
  (exists, mail server, SPF/DMARC, registration age, related to the company `name`). The
  domain of an `individual` is never looked up on the network.
- Phone: valid number for the declared `country`, virtual, toll-free or premium-rate lines,
  and a calling code from another country.
- IP address: Tor exit nodes, cloud or hosting networks, and an address block registered to a
  country other than the declared one. The address is never returned, not even masked.

`checks` says what happened to each input: `evaluated`, `unavailable` (it could not run now)
or `not_provided`. A check that could not run never counts as "nothing found": it holds
`suggested_decision` at `review`. When none of the inputs sent could be evaluated, the answer
is `503 SIGNALS_UNAVAILABLE` and the charge is refunded; retry.

`suggested_decision` is `review` on any `high` signal or on any `unavailable` check, and
`clear` otherwise. No signal approves anything.

A private, loopback or reserved address is `400 INVALID_IP_ADDRESS`; the charge is refunded.

Send an `Idempotency-Key` to retry safely: the same key and body return the same answer and
are not charged again.

## Headers

- `Idempotency-Key` string

## Request body

- SignalsRequest
  - `type` 'individual' | 'business', required: Whether the contact belongs to a person or a company. Only a company's email domain is looked up on the network.
  - `name` string: The company name, compared with its email domain. Optional.
  - `country` string: Declared country (ISO 3166-1 alpha-2), for the phone and the IP address. Optional.
  - `email` string
  - `phone` string: International format (`+55 11 ...`), or national with `country`.
  - `ip_address` string: A public IPv4 or IPv6 address. Never returned.

## Response `200`

The signals.

- Signals
  - `object` 'signals', required
  - `type` 'individual' | 'business', required
  - `suggested_decision` 'clear' | 'review', required: `review` on any `high` signal or any `unavailable` check; `clear` otherwise.
  - `checks` object, required
    - `email` 'evaluated' | 'unavailable' | 'not_provided', required: `unavailable`: the check could not run now; it never means nothing was found.
    - `phone` 'evaluated' | 'unavailable' | 'not_provided', required: `unavailable`: the check could not run now; it never means nothing was found.
    - `ip_address` 'evaluated' | 'unavailable' | 'not_provided', required: `unavailable`: the check could not run now; it never means nothing was found.
  - `signals` object[], required
    - `code` string, required: Stable machine code. The `contact` codes are the ones of `ScreeningSignal`; the `ip` codes are: - `tor_exit_node_ip` - the address is a Tor exit node (`medium`). - `hosting_network_ip` - the address belongs to a cloud or hosting network (`low`). - `ip_country_mismatch` - the address block is registered to another country than `country` (`low`).
    - `category` 'contact' | 'ip', required
    - `severity` 'high' | 'medium' | 'low', required
    - `evidence` string, required
    - `why_it_matters` string, required
  - `rule_version` string, required: Version of the rules that produced this answer.

## Other responses

- `202`: Accepted - a concurrent request with the same `Idempotency-Key` is still executing; the body is `{"status": "processing"}`. Retry with the same key once the original completes: a completed request replays its stored response verbatim with the original status code and the `Idempotent-Replayed: true` header. This is not the resource - check the status code before deserializing. If this `202` repeats instead of resolving, the original request's outcome is unknown and no stored result will arrive - the key stays claimed when a request fails after the resource may already have been written. Stop retrying, reconcile with a `GET` to see whether the resource exists, and use a new key if it does not.
- `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.
- `409`: Conflict (e.g. IDEMPOTENCY_KEY_REUSED).
- `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)
