---
title: "Look up a company in the public registry"
method: POST
path: "/businesses/lookup"
tags: ["Businesses"]
---

# Look up a company in the public registry

`POST /businesses/lookup`

## Request

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

Returns the public registry record of a company, read at request time (a copy up to 30 days
old may answer instead). Brazil only today: send `country: BR` and the CNPJ.

Three answers, and never a guess:
- `result: found` with the record. `stale: true` means the registry could not be read and a
  copy older than 30 days answered.
- `result: not_found`: every registry source said the number does not exist. It is a real
  answer and is charged.
- `503 REGISTRY_UNAVAILABLE`: the registry could not be read. The charge is refunded; retry.

A check-digit error is `400 INVALID_REGISTRATION_NUMBER` and another country is `422
FEATURE_NOT_AVAILABLE`; the charge for either is refunded. Nothing about the lookup is stored.

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

Requires the `businesses:read` scope. API keys created before this scope existed do not gain
it: create a new key with the scopes you need.

## Headers

- `Idempotency-Key` string

## Request body

- BusinessLookupRequest
  - `country` string, required: ISO 3166-1 alpha-2. `BR` only today.
  - `registration_number` string, required: The CNPJ, with or without punctuation. Check digits are verified.

## Response `200`

The registry answer.

- BusinessRegistryLookup
  - `object` 'business_registry_lookup', required
  - `country` string, required
  - `registration_number` string, required: The CNPJ
  - `result` 'found' | 'not_found', required
  - `stale` boolean: Present when `result` is `found`. `true` = the registry could not be read and a copy older than 30 days answered.
  - `checked_at` string, date-time, required
  - `business` object, nullable, required: The record. `null` when `result` is `not_found`.
    - `legal_name` string
    - `trade_name` string, nullable
    - `registration_status` 'active' | 'closed' | 'suspended' | 'unfit' | 'void' | 'unknown': The registry status. `unknown` = the registry gave none, or a word outside this list.
    - `legal_nature` string, nullable
    - `incorporation_date` string, date, nullable
    - `share_capital` object, nullable
      - `amount` string: Decimal string
      - `currency` 'BRL'
    - `primary_activity` object, nullable
      - `code` string: CNAE subclass
      - `description` string, nullable
    - `address` object
      - `city` string, nullable
      - `state` string, nullable
      - `postal_code` string, nullable
    - `partners` object[]
      - `name` string
      - `role` string, nullable
      - `since` string, date, nullable

## 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).
- `422`: The request is well-formed but cannot be fulfilled - e.g. a `rail` that is not served (see `Rail.x-ozav-rail-availability`).
- `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)
