---
title: "List events"
method: GET
path: "/events"
tags: ["Webhooks"]
---

# List events

`GET /events`

## Request

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

Lists your account's events, newest first - the pull side of the event
log. Each item is the same canonical envelope a webhook delivers, so you
can reconcile or backfill a missed delivery without a webhook endpoint.
Client-scoped; keyset-paginated.

## Query parameters

- `limit` integer
- `starting_after` string

## Response `200`

A page of events.

- object: The uniform envelope every list endpoint returns.
  - `data` WebhookEvent[], required: The page of results. Each list endpoint documents its own item type.
    - `id` string, required: Opaque event identifier, prefixed `ozav_evt_`.
    - `object` 'event', required: Resource type discriminator; always the literal `event`.
    - `type` 'webhook.test' | 'customer.created' | 'customer.updated' | 'verification.created' | 'verification.updated' | 'screening.completed' | 'screening.monitoring_alert' | 'transfer.updated' | 'transfer.completed' | 'transfer.failed' | 'account.updated' | 'account.credited' | 'deposit.credited' | 'requirement.due' | 'document.reviewed' | 'transfer.approval_required' | 'limit_request.updated', required: Event types currently emitted by API v2 webhooks. The catalog lists only events that actually fire today: - `webhook.test` - queued by `POST /webhook-endpoints/{id}/test`. - `customer.created` / `customer.updated` - emitted by the v2 customers surface. - `verification.created` / `verification.updated` - emitted by the v2 verification surface. - `screening.completed` - emitted when a screening completes. - `screening.monitoring_alert` - a screening under monitoring (`POST /screenings/{id}/monitoring`) matches a sanctions or PEP entry that was added or changed after it was screened. Sent once per screening and entry. - `transfer.updated` / `transfer.completed` / `transfer.failed` - emitted by the v2 transfer surface. - `card.created` / `card.updated` - **card lifecycle only.** `card.created` fires on `POST /cards`; `card.updated` on a spending-controls change (`PATCH /cards/{id}`), on freeze / unfreeze / close, and when the card processor changes the card's status on its side: it confirms a `pending` card (now `active`), or freezes, closes (e.g. reported lost) or expires it. A processor change can arrive without any call of yours. Apart from confirming a `pending` card, it only ever restricts the card, never reopens it. - `card.fraud_confirmed` - emitted when OZAV reviews declines on one of your cards and confirms they were fraud. `data` is `{ card, confirmed_at }`, with `card` in the same shape as `card.updated`. **It is a notice, not an action:** the card is not frozen or closed by it. Deciding what to do with the card is yours - `POST /cards/{card_id}/freeze` or `/close`. It fires at most once per review, is never sent for a decline OZAV judged legitimate, and carries no internal identifiers. - `card.fraud_case_closed` - emitted when OZAV finishes reviewing declines on one of your cards, whatever the outcome. `data` is `{ card, outcome, fraud_category, decline_ids, closed_at }`, with `card` in the same shape as `card.updated` and `outcome` one of `fraud_confirmed`, `false_positive` (the declines were legitimate purchases: the cardholder can try again) or `inconclusive`. One review covers the declines of one fraud rule on the card: `decline_ids` lists them with the same `id`s that `GET /cards/{card_id}/declines` returns, oldest first, once per purchase even if the card processor retried it. If the review covered more than 500, these are the oldest 500; the rest are on `/declines`. A decline with no `card.declined` (for example, the card had no public id yet, or the card processor sent no authorization reference) is not listed, so the list can be empty. `fraud_category` is the kind of rule, with the same values as on `card.declined`, and `null` if that rule no longer exists. A `fraud_confirmed` review also sends `card.fraud_confirmed`. It fires at most once per review and carries no internal identifiers. - `card.authorized` - emitted when a purchase on one of your cards is **approved** and the amount is held. `data` is `{ card_id, transaction_id, amount_usd, merchant }`. It is sent after the decision has already been returned to the card processor, never inside the processor's authorisation window, so subscribing to it cannot slow down or decline a purchase. - `card.settled` - emitted when a purchase **clears**. `data` is `{ card_id, transaction_id, amount_usd, settled_at, merchant }`. `amount_usd` is the final amount, which can differ from the authorised one (tips, fuel adjustments) - use this one for your ledger, not the authorised figure. `transaction_id` (`ozav_ctxn_...`) is the `id` of the same purchase in `GET /cards/{card_id}/authorizations` and `GET /cards/{card_id}/transactions`, so the authorisation, its settlement and the row in your ledger reconcile on one key. A settlement of an approved hold carries the same value as its `card.authorized`, including a settlement that reaches OZAV before the hold was recorded. Only a second capture against the same authorisation (a split shipment) gets its own row and its own id. On `card.authorized` it is `null` when the hold row does not exist or could not be read when the event was sent (the authorisation was released first, or recording the hold failed): read `/authorizations`, or wait for its `card.settled`. `card.settled` can arrive before `card.authorized` for the same purchase. When both carry the same `transaction_id`, upsert on it and never move a settled row back to authorised. Both carry the card's public id, never an internal identifier, and are de-duplicated on the processor's own reference: a redelivered authorisation or settlement does not notify you twice. A card that has never been seen through the v2 API has no public id, so no event is sent for it. - `card.declined` - emitted when a purchase on one of your cards is **refused**. `data` is `{ card_id, amount_usd, decline_code, fraud_category, merchant }`. A purchase OZAV refuses leaves no hold and no row in `/authorizations`; it is listed on `GET /cards/{card_id}/declines`, the same refusal this event announced. `fraud_category` says which kind of fraud rule refused the purchase, and is `null` for every `decline_code` other than `fraud_rule`. It is one of `velocity` (too many purchases or too much spend in a short window), `transaction_amount` (one purchase above a limit), `period_spend` (daily or monthly spend above a limit), `merchant_category`, `merchant_country`, `time_of_day`, or `other`. The rule's own name and text are never sent. `decline_code` is one of: `insufficient_balance`, `card_frozen`, `card_inactive`, `account_inactive`, `spending_limit`, `mcc_blocked`, `country_blocked`, `presence_blocked`, `fraud_rule`, `issuer_declined`, or `temporarily_unavailable`. `issuer_declined` is a refusal by the card issuer under its own rules; a merchant category the issuer does not allow comes as `mcc_blocked`. When the refusal is on our side rather than the cardholder's, you get `temporarily_unavailable` instead of our internal reason. It means the purchase can be retried and the cardholder did nothing wrong - which is the only part you can act on. We never send the operator-facing text behind a decision. - `card.refunded` - emitted when a merchant **returns** money to one of your cards. `data` is `{ card_id, transaction_id, amount_usd, refunded_at, merchant }`, with `transaction_id` the refund's own row in `GET /cards/{card_id}/transactions`. `amount_usd` is **positive**: the direction travels in the event type, not in the sign, for the same reason `/transactions` reports `direction: credit` rather than a negative number. A refund is not an authorisation - the merchant returns and the value credits, with nobody asked - so it never appears in `/authorizations`. - `card.reversed` - emitted when a held authorisation is released **without settling**: the merchant cancelled it, it expired, or the card issuer declined it after OZAV approved. The held amount and its surcharge return to the balance. `data` is `{ card_id, transaction_id, amount_usd, client_surcharge_usd, reason, reversed_at }`, with `transaction_id` the same as in the `card.authorized` it closes (null if the hold was never recorded) and `reason` one of `reversed`, `expired` or `declined`. In the sandbox, trigger it with `POST /cards/{card_id}/simulate/reversal`. - `card.three_ds_code` - emitted when the card issuer asks for a 3-D Secure one-time code on an online purchase and sends the code to OZAV instead of to the cardholder. Show it to your cardholder so they can finish the purchase. `data` is `{ card_id, code, amount, currency, expires_at }`, with `expires_at` an ISO 8601 UTC timestamp; `amount`, `currency` and `expires_at` are null when the issuer does not report them. A code that has already expired is not sent. The code works for that one purchase only: do not log or store it. Webhook redelivery sends the same code again until it expires. - `card.dispute_created` - emitted when you open a dispute with `POST /cards/{card_id}/disputes`. `data` is the dispute (`CardDispute`). - `card.dispute_updated` - emitted each time a dispute changes status (`sent_to_issuer`, `won`, `lost`, `withdrawn`). `data` is the dispute (`CardDispute`). One event per status. - `card.deletion_scheduled` - emitted when a card has been frozen long enough that the card issuer will delete it. `data` is `{ card_id, frozen_days, deletes_at }`, with `deletes_at` an ISO 8601 UTC timestamp counted from when the card was frozen (null if that is unknown). Unfreeze the card before then to keep it; a deleted card cannot be restored. You get one notice per freeze and day count, so a card frozen again is warned again. - `card_float.top_up_blocked` - emitted when your automatic card float top-up cannot run because your own master wallet balance is short. Nothing was moved. `data` is `{ object: "card_float", reason: "integrator_balance_short", float: { available_amount, currency, floor_amount, target_amount }, top_up: { needed_amount, currency }, integrator_balance: { available_amount, currency } }`, all amounts decimal strings in USD (`integrator_balance. available_amount` is null when it could not be read). Fund your master wallet and the next check tops the float up. You get at most one per day while it stays blocked. See `PUT /card-float-settings`. In the sandbox you create one with `POST /cards/{card_id}/simulate/purchase` and `is_refund: true` in the body. - `account.updated` - emitted when a named account's public status changes, so an integrator learns their customer can receive money without polling GET /accounts. - `account.credited` - emitted when money arrives in a customer's BRL named account. `data` is `{ account_id, movement }`, with `movement` in the same shape `GET /accounts/{account_id}/movements` lists it, and the same `id`. The credit is read from the account's statement every 10 minutes, so the event can arrive up to that long after the payer sent the Pix. It fires once per credit. In the sandbox you create one with `POST /accounts/{account_id}/simulate/credit`. - `deposit.credited` - emitted when crypto sent to one of your wallets has been credited and is spendable. Receiving crypto needs no call from you: the arrival is observed on chain and credited on its own, and it already shows up in `GET /transactions` and in `GET /wallets/{wallet_id}/balances`. This event is the push, so you do not have to poll for it. There is no `deposit.pending`: before a deposit is credited it can still reorganize on chain, and telling you early would invite you to act on value that may vanish. - `requirement.due` - emitted when compliance review determines which documents are still missing for a customer, so you learn what to collect without polling. Body: `{customer_id, object: requirement_request, due_at, reason, reason_label, documents: [{key, label, destination?}], purpose?, missing_fields?}`. With `reason: destination_unidentified` (`purpose: travel_rule`) each entry of `documents` carries `destination: {chain, address}`: say who holds that wallet with `POST /customers/{customer_id}/destination-identifications`. When the ask is for registration data rather than a file (`reason: registration_incomplete` or `data_inconsistent`), `missing_fields` lists what to send, in the same tokens as `requirements.missing_fields` (e.g. `phone`, `residential_address`, `expected_monthly_volume_usd`): send them with `PATCH /customers/{customer_id}` (or, for `owners`, `POST /customers/{customer_id}/associated-persons`). Absent when nothing asked for is a field. `tax_id` / `registration` there cannot be corrected once the customer is approved or frozen (`409 TAX_ID_IMMUTABLE`): create a new customer instead. Emitted once per ask, like a document ask. There is no `requirement.satisfied`: the customer's own `GET /customers/{id}` shrinks `requirements.documents_due` when a file lands, and announcing a second event would need a second emitter to avoid becoming an empty promise. - `document.reviewed` - emitted when OZAV's own document review accepts or rejects a supporting document (proof of address, bank statement, proof of income, company document): `{customer_id, document_id, key, label, status: accepted|rejected, reason, reason_label, reviewed_at}`. A rejection is followed by a fresh `requirement.due` for the same document, with a new deadline and the `reason`. Not emitted while a document is waiting on a human reviewer. - `wallet_policy.updated` - emitted when a change to a managed wallet's policy is decided: `{wallet_id, change: {id, kind: destination.add|limits.update, status, ...}}`. For a destination, `status: applied` means it is already in `destinations.allowed`. For limits, `status: approved` means accepted and not yet in force - the signed record is issued minutes later; confirm with `GET /wallets/{wallet_id}/policy` (`limits.valid_until`). `rejected` carries a `reason` when one was given. Not emitted when you PATCH: requesting is not changing. - `transfer.approval_required` - not emitted: no send waits on approver keys. - `limit_request.updated` - emitted when the public status of a limit increase request (`POST /customers/{customer_id}/limit-requests`) changes: `awaiting_documents`, `in_review`, `approved` (with `approved_limit`) or `declined`. `data` is the `LimitRequest` itself. Once per status. A decline carries no reason. - `agent_action.updated` - emitted when the public status of an agent action (`POST /agents/{agent_id}/payments`, `/payouts`, `/fundings`, `/payees`, `/consumptions`, `/yield-applications` or `/accounts`, and `POST /agent-actions/{action_id}/simulate` in the sandbox) changes, to both the agent platform and the company whose money it is. `data` is the `AgentAction` itself. Once per status. `authorized` after `awaiting_approval` means a person at the company approved: a payout then runs on its own, and for a payment you repeat the request with the same `idempotency_key` to receive the signature. `settled` means the token contract shows the authorization used. `returned` means a payout's transfer ended without delivering the money (`cancelled` or `expired`): the mandate's budget is released. A payout becomes `settled` when its transfer completes. A transfer that needs review (including a Pix return) or that failed stays `executed` until OZAV reviews it; the event is still sent when the receipt's `transfer_status` changes. A funding (`POST /agents/{agent_id}/fundings`) is announced `awaiting_payment` when its Pix charge exists, then `settled` when the company paid and the stablecoin arrived, or `returned` if the charge expired unpaid. A refusal carries the mandate's reasons only; compliance refusals carry no reason. Events are not guaranteed to arrive in order, and a status that changes twice before OZAV reads it (for example `authorized` then `signed` in one request) is announced once, as the status it has then: `GET /agent-actions/{action_id}` is the source of truth. An action waiting for approval that nobody decides ends as `expired`. When an exception case (dispute) on the action is opened or changes status (`open`, `under_review`, `resolved_refunded`, `resolved_denied`), the same event goes out with the case in `data.exception`, once per case status. - `agent_wallet_withdrawal.updated` - emitted to the account holder's organization when a withdrawal from an agent's wallet (`POST /agents/{agent_id}/wallet/withdraw`, or the one OZAV starts when the agent or its grant is revoked) is created (`processing`, or `empty` when a revoked agent's wallet held nothing) and when it ends: `settled`, `refused` (`settled_amount` says what moved before the refusal) or `in_review`. `data` is the `AgentWalletWithdrawal` itself. Once per status; a withdrawal that pauses and carries on later is not announced again until it ends. A status that changes before OZAV reads it is announced once, as the status it has then, and events may arrive out of order. Repeating the request with the same `idempotency_key` returns the withdrawal as it is now: that answer is the source of truth. - `vault.activated` / `vault.failed` - emitted when a vault leaves `pending` (`POST /vaults`). `data` is the `Vault` as `GET /vaults/{vault_id}` answers it at that moment: with `lineage`, `addresses` and the enclave's `receipt` when active (verify the receipt before you deposit), with `failure_code` when failed. - `vault_transaction.signed` / `vault_transaction.rejected` / `vault_transaction.expired` - emitted when a vault transaction leaves `awaiting_approvals`. `data` is the `VaultTransaction` as `GET /vaults/{vault_id}/transactions/{transaction_id}` answers it: `signed` carries `signed_transaction` (or `signature` and `digest` for a typed message), which you broadcast yourself (OZAV never does); `rejected` carries `failure_code`; `expired` means `approve_until` passed before the threshold closed. The vault events are written in the same database transaction that concludes the vault or the transaction, so an event exists exactly when the state does. Once per status. Events are delivered in the key's environment. - `vault_root.created` - emitted when a root is registered (`POST /vault-roots`). `data` is the `VaultRoot` as `GET /vault-roots/{root_id}` answers it. - `vault_grant.created` / `vault_grant.revoked` - emitted when a grant is accepted (`POST /vault-roots/{root_id}/grants`) and when it is revoked (`POST .../grants/{grant_id}/revoke`). `data` is the `VaultGrant` as the list answers it, `GET /vault-roots/{root_id}/grants`: how many signatures and how many approvers. It does not carry `policy`, which only the single read `GET /vault-roots/{root_id}/grants/{grant_id}` returns. Revocation is announced once, on the stamping. - `vault_destination_grant.created` - emitted when destinations are authorised in a vault (`POST /vaults/{vault_id}/destination-grants`). `data` is the `VaultDestinationGrant` as `GET /vaults/{vault_id}/destination-grants/{destination_grant_id}` answers it. - `vault_transaction.requested` - emitted when a signature is requested (`POST /vaults/{vault_id}/transactions`). `data` is the `VaultTransaction` at that instant, so `approvals.received` is `0` and `signing` is `false` even when the request already carried `approvals[]`: the approvals are stored after the transaction row. Read `vault_transaction.approval_received`, or `GET`, for the quorum as it stands. - The six events above announce steps, and they exist only for facts that happened after they were switched on: a root, grant, destination grant or transaction created before that has no `created` or `requested` event, and a grant already revoked then has no `vault_grant.revoked`. There is no backfill. Reconcile the objects you had before with `GET`; from then on the events are complete. - `vault_transaction.approval_received` - emitted once per approval stored (`POST /vaults/{vault_id}/transactions/{transaction_id}/approvals`, or an `approvals[]` entry of the request). `data` is the `VaultTransactionApproval` as `GET /vaults/{vault_id}/transactions/{transaction_id}/approvals` lists it. A repeated approval from the same approver is announced once: the first one stands.
    - `livemode` boolean: `true` for an event about live resources, `false` for one from the sandbox. An endpoint created in the dashboard before it asked for an environment receives both, so branch on this field rather than on how the endpoint was registered. `GET /events` always returns it. A webhook delivery carries it on every event emitted since the field was introduced; a retry of an older event can arrive without it. When it is missing, look the event up with `GET /events/{event_id}`: each key finds only the events of its own environment. An endpoint registered with an API key only ever receives events of that key's environment. Before this field was introduced, the `customer.updated` OZAV sends on its own after a review (a status change, a new limit or a request for information), `wallet_policy.updated` about a wallet with only a sandbox id, and `account.updated` about a sandbox account could be recorded as production; such an older event keeps `livemode: true`.
    - `created_at` string, date-time, required: When the event occurred (RFC 3339, UTC), not when this delivery attempt was made. It stays the same across retries - use it, not receipt time, to order events.
    - `data` object, required: The affected resource, in the same shape its `GET` returns. Events about transfers, customers, cards, verifications and screenings carry it under the resource's own name: `data.transfer`, `data.customer`, `data.card`, `data.verification`, `data.screening`. Other events document their own `data` in the event list. It is a snapshot at event time, and it can be stale by the time you read it. When the current state matters (a decision about money), re-fetch by id rather than acting on this body. Deliveries can also arrive out of order.
  - `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.

---

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