---
title: "List transfers"
method: GET
path: "/transfers"
tags: ["Transfers"]
---

# List transfers

`GET /transfers`

## Request

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

Lists transfers, newest first, with the standard cursor pagination.
Filter by `customer_id` and/or `status` (a `TransferStatus` value).
Returns a `Page` of `Transfer`. BRL/Pix paths create real transfer
records; combinations whose settlement rails are not enabled still fail
closed before a transfer is persisted.

## Query parameters

- `limit` integer
- `starting_after` string
- `customer_id` string
- `status` 'awaiting_payment' | 'payment_confirmed' | 'converting' | 'payout_initiated' | 'completed' | 'failed' | 'cancelled' | 'expired' | 'needs_review' | 'awaiting_approval': Canonical transfer state machine. A successful FX transfer walks `awaiting_payment → payment_confirmed → converting → payout_initiated → completed`; the remaining states are terminal failures or attention states. | Value | Meaning | |---|---| | `awaiting_payment` | Transfer created; awaiting the customer's inbound payment. | | `payment_confirmed` | The inbound payment has been confirmed. | | `converting` | Funds are in flight and the FX conversion is underway (not yet confirmed). | | `payout_initiated` | The payout to the destination has been initiated. | | `completed` | Terminal success - funds delivered to the destination. | | `failed` | Terminal failure - no funds delivered. | | `cancelled` | Terminal - the transfer was cancelled. | | `expired` | Terminal - the transfer expired before completing. | | `needs_review` | Stuck and needs attention (e.g. underfunded, returned, in review); not terminal. | | `awaiting_approval` | Not returned: no transfer waits on approver keys. |

## Response `200`

Paginated transfers.

- object: The uniform envelope every list endpoint returns.
  - `data` Transfer[], required: The page of results. Each list endpoint documents its own item type.
    - `id` string, required: Opaque transfer identifier, prefixed `ozav_tr_`.
    - `object` 'transfer', required: Resource type discriminator; always the literal `transfer`.
    - `customer_id` string: The `ozav_cus_` id of the customer this transfer is for.
    - `quote_id` string: The `ozav_q_` id of the executed quote, when the transfer references one.
    - `status` 'awaiting_payment' | 'payment_confirmed' | 'converting' | 'payout_initiated' | 'completed' | 'failed' | 'cancelled' | 'expired' | 'needs_review' | 'awaiting_approval', required: Canonical transfer state machine. A successful FX transfer walks `awaiting_payment → payment_confirmed → converting → payout_initiated → completed`; the remaining states are terminal failures or attention states. | Value | Meaning | |---|---| | `awaiting_payment` | Transfer created; awaiting the customer's inbound payment. | | `payment_confirmed` | The inbound payment has been confirmed. | | `converting` | Funds are in flight and the FX conversion is underway (not yet confirmed). | | `payout_initiated` | The payout to the destination has been initiated. | | `completed` | Terminal success - funds delivered to the destination. | | `failed` | Terminal failure - no funds delivered. | | `cancelled` | Terminal - the transfer was cancelled. | | `expired` | Terminal - the transfer expired before completing. | | `needs_review` | Stuck and needs attention (e.g. underfunded, returned, in review); not terminal. | | `awaiting_approval` | Not returned: no transfer waits on approver keys. |
    - `approval_id` string: Not returned. No transfer waits on approver keys.
    - `status_detail` object: Optional detail explaining the current status. Present for a transfer that needs OZAV reconciliation, and for one that ended badly (`failed`, `cancelled`, `expired`) once the reason was recorded; `code` is a catalog error code you can branch on. Absent when no reason was recorded, which is the case for transfers created before this field existed; treat absence as "not stated", never as "no reason". It never contains internal names, native rail error payloads, or a compliance verdict.
      - `code` string: Machine-readable OZAV status detail code.
      - `message` string: Human-readable status detail.
      - `retryable` boolean: Whether the client should retry the same action without OZAV intervention.
    - `source` union, required: Where a transfer's funds come from. Discriminated by `type`; the value selects the flow together with the destination.
      - TransferSourceFiatPayment: A fiat payment the customer sends in (the FX / on-ramp source leg).
        - `type` 'fiat_payment', required: Discriminator for a fiat-payment source; always the literal `fiat_payment`.
        - `currency` string, required: Uppercase ISO 4217 code or stablecoin symbol (e.g. BRL, USD, EUR, MXN, USDC, USDT). Uppercase is the canonical form: values are accepted case-insensitively on input but are always uppercase in responses.
        - `rail` 'pix' | 'ach' | 'fedwire' | 'sepa' | 'spei' | 'swift' | 'faster_payments' | 'co_transfer' | 'uaefts' | 'eft' | 'cnaps' | 'zengin' | 'nip' | 'cce' | 'rtc': Canonical payment-rail vocabulary, shared by named accounts and payout destinations. The table below answers one question: can you open a named account on this rail (machine-readable in `x-ozav-rail-availability`)? Paying out is a different question, answered by the corridor table in `POST /payout-destinations`. The two differ: `fedwire`, for example, is live for payouts, while opening a USD account on it is sandbox only. | Rail | Corridor | Availability | |---|---|---| | `pix` | BRL - Brazil instant payments | available | | `fedwire` | USD - US wire | sandbox only | | `sepa` | EUR - SEPA credit transfer | live (behind enablement) | | `spei` | MXN - Mexico SPEI | sandbox only | | `swift` | Cross-border SWIFT | live (behind enablement) | | `faster_payments` | GBP - UK Faster Payments | live (behind enablement) | **`ach` and `co_transfer` are not offered.** Requests for accounts, payout destinations and quotes use `RequestRail`, which leaves both out, and a `fiat_payment` source on `ach` is refused with `422 RAIL_NOT_AVAILABLE`. They stay in this enum only because older records carry them. **`live (behind enablement)`** means the corridor is served in production, but not to everyone: opening an account still requires that OZAV has enabled the specific customer for foreign-currency named accounts. Until then a `live` request is refused with a named error that says which step is missing - not with `ACCOUNT_RAIL_NOT_AVAILABLE`, which would send you looking at the corridor instead of the customer. **`sandbox only` is a real, usable state, and not a promise of later.** The corridor is fully exercisable with a `test` key: you can open the account, read its receiving instructions and build against the exact response shape production will return. What it does not have is a settlement partner behind it, so a `live` key is refused with `ACCOUNT_RAIL_NOT_AVAILABLE` rather than opening something that cannot receive. Build against it in sandbox; the request you write does not change when the corridor goes live.
        - `virtual_account_id` string: Accepted and ignored: the payment is always received through the standard flow. Omit it.
        - `payment_instructions` PixPaymentInstructions: Pix payment instructions for a specific pay-in transaction.
          - `rail` 'pix', required: The payment rail; always `pix`.
          - `intent_id` string, required: OZAV's id for the Pix pay-in intent. **Opaque - store it and echo it back, never parse it.** Unlike the `ozav_*` resource ids, this one carries no prefix and is a UUID today; the format is not part of the contract and may change. Use it to correlate the Pix payment with this transfer, not to infer anything about the intent.
          - `br_code` string, nullable: Pix copy-and-paste BR Code for this specific transaction.
          - `qr_code_image` string, nullable: QR code image payload, when available.
          - `expires_at` string, date-time, nullable: When the Pix instructions expire.
      - TransferSourceWallet: A managed wallet as the source of funds (off-ramp / wallet transfer).
        - `type` 'wallet', required: Discriminator for a wallet source; always the literal `wallet`.
        - `wallet_id` string, required: The `ozav_wal_` id of the source wallet.
        - `currency` string: Uppercase ISO 4217 code or stablecoin symbol (e.g. BRL, USD, EUR, MXN, USDC, USDT). Uppercase is the canonical form: values are accepted case-insensitively on input but are always uppercase in responses.
        - `chain` string: The network the funds leave from, when the wallet serves more than one. Same rule as on a wallet destination: with the FX rate lock it is what the hedge follows, and an absent network refuses the lock instead of guessing.
      - TransferSourceCryptoPayment: An inbound crypto deposit as the source (crypto receive).
        - `type` 'crypto_payment', required: Discriminator for a crypto-payment source; always the literal `crypto_payment`.
        - `deposit_address_id` string, required: The id of the crypto deposit address the funds arrive at.
      - TransferSourceAccount: The customer's own named fiat account (`ozav_acc_`) as the source of an on-ramp: the customer already deposited into that account, and the order is funded from its balance. No payment instructions are returned, because there is nothing left to pay. The account must be active and known to its institution, and the amount must fit the balance that is not already committed to the customer's other open orders. In the sandbox, a test key can use a sandbox account after `POST /accounts/{account_id}/simulate/credit`. Choose `fiat_payment` instead when the customer has not deposited yet and needs payment instructions to send the money.
        - `type` 'account', required: Discriminator for an own-account source; always the literal `account`.
        - `account_id` string, required: The `ozav_acc_` id of the customer's own named fiat account.
    - `destination` union, required: Where a transfer's funds go. Discriminated by `type`; the value selects the flow together with the source.
      - TransferDestinationPayout: A registered payout destination as the target (FX / off-ramp destination leg).
        - `type` 'payout_destination', required: Discriminator for a payout-destination target; always the literal `payout_destination`.
        - `payout_destination_id` string, required: The `ozav_pd_` id of the registered payout destination.
      - TransferDestinationWallet: A managed wallet as the target (on-ramp / wallet transfer).
        - `type` 'wallet', required: Discriminator for a wallet target; always the literal `wallet`.
        - `wallet_id` string, required: The `ozav_wal_` id of the destination wallet.
        - `chain` string: The network the credit lands on, when the wallet serves more than one. A managed wallet serves several networks, so a BRL Pix on-ramp into one requires it: without it the transfer is refused with `DESTINATION_CHAIN_REQUIRED` before any charge is created. **Optional in general, but effectively required when you use the FX rate lock.** The hedge OZAV buys is withdrawn to treasury on the same network as the delivery - buying on one and delivering on another would leave a crossing for someone to resolve with the exposure already open. With no network supplied there is nothing to follow, so the lock is **refused** (`facility.reason = sem_destino_de_tesouraria`) rather than guessed. The transfer itself still proceeds; only the lock is lost. The refusal can also mean the pair itself has no registered treasury destination (`USDC:tron`, for instance, is not a pair OZAV serves). The message names the `asset:network` pair, so read it before assuming the field is at fault.
      - TransferDestinationCryptoAddress: External crypto destination; triggers wallet screening and travel-rule requirements by policy.
        - `type` 'crypto_address', required: Discriminator for an external crypto-address target; always the literal `crypto_address`.
        - `address` string, required: The external blockchain address to send to; must be valid on `network`.
        - `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.
        - `currency` string, required: Uppercase ISO 4217 code or stablecoin symbol (e.g. BRL, USD, EUR, MXN, USDC, USDT). Uppercase is the canonical form: values are accepted case-insensitively on input but are always uppercase in responses.
      - TransferDestinationAccount: The customer's own named fiat account (`ozav_acc_`) as the target of an off-ramp: the stablecoin is sold and the fiat is credited to that account, where it stays until the customer moves it. Available for USD, EUR and GBP accounts that are active and known to the account's institution; the quote's target currency must be the account's currency. Choose this when the customer wants to hold a fiat balance rather than pay a third party; choose `payout_destination` to pay out to a registered external account.
        - `type` 'account', required: Discriminator for an own-account target; always the literal `account`.
        - `account_id` string, required: The `ozav_acc_` id of the customer's own named fiat account.
    - `source_leg` SettlementLeg: The settlement state of one side of a transfer (source or destination). Read-only.
      - `status` 'pending' | 'processing' | 'needs_review' | 'completed' | 'failed' | 'cancelled': Canonical per-leg settlement status. A transfer carries an independent `source_leg` and `destination_leg`, each with one of these values. | Value | Meaning | |---|---| | `pending` | Accepted; awaiting the payer's funds. Non-terminal. | | `processing` | Funds in flight (received, or payout initiated). Non-terminal. | | `needs_review` | Stuck / needs attention (underfunded, in review, returned). Non-terminal. | | `completed` | Terminal success - the leg settled. | | `failed` | Terminal failure - the leg did not settle. | | `cancelled` | Terminal - the leg was cancelled. |
      - `settled_at` string, date-time: When this leg reached terminal completion (RFC 3339 / ISO 8601, UTC). Present only once the leg is `completed`.
    - `destination_leg` SettlementLeg: The settlement state of one side of a transfer (source or destination). Read-only.
      - `status` 'pending' | 'processing' | 'needs_review' | 'completed' | 'failed' | 'cancelled': Canonical per-leg settlement status. A transfer carries an independent `source_leg` and `destination_leg`, each with one of these values. | Value | Meaning | |---|---| | `pending` | Accepted; awaiting the payer's funds. Non-terminal. | | `processing` | Funds in flight (received, or payout initiated). Non-terminal. | | `needs_review` | Stuck / needs attention (underfunded, in review, returned). Non-terminal. | | `completed` | Terminal success - the leg settled. | | `failed` | Terminal failure - the leg did not settle. | | `cancelled` | Terminal - the leg was cancelled. |
      - `settled_at` string, date-time: When this leg reached terminal completion (RFC 3339 / ISO 8601, UTC). Present only once the leg is `completed`.
    - `amounts` object, required: The amounts and rate of this transfer. They start from the quote when the transfer is created (when the quote carried `exchange_conditions.amount_deducted`, `rate` is the all-in `target.amount / source.amount`, published at 8 decimal places, and differs from the quote's `rate` by the deducted cost), and on some paths they are updated at settlement: on the fiat-to-stablecoin path (`fiat_payment` to `wallet`) `source`, `target` and `rate` are rewritten from the settled amounts as the operation progresses, before the matching `transfer.*` webhook is sent. On the other paths they keep the quoted values, which are indicative on a `floating` quote. Read them again from `GET /transfers/{transfer_id}` or the webhook after the transfer settles; do not cache the values returned at creation.
      - `source` MonetaryAmount: An amount paired with its currency, at 6-fractional-digit accounting precision.
        - `amount` string, required: Decimal string at accounting precision - up to 6 fractional digits. Never a float. The value must be greater than zero, with no leading zeros in the integer part (a bare `0` integer part is allowed for sub-unit values such as "0.50") and at most 15 integer digits.
        - `currency` string, required: Uppercase ISO 4217 code or stablecoin symbol (e.g. BRL, USD, EUR, MXN, USDC, USDT). Uppercase is the canonical form: values are accepted case-insensitively on input but are always uppercase in responses.
      - `target` MonetaryAmount: An amount paired with its currency, at 6-fractional-digit accounting precision.
        - `amount` string, required: Decimal string at accounting precision - up to 6 fractional digits. Never a float. The value must be greater than zero, with no leading zeros in the integer part (a bare `0` integer part is allowed for sub-unit values such as "0.50") and at most 15 integer digits.
        - `currency` string, required: Uppercase ISO 4217 code or stablecoin symbol (e.g. BRL, USD, EUR, MXN, USDC, USDT). Uppercase is the canonical form: values are accepted case-insensitively on input but are always uppercase in responses.
      - `rate` string: The applied FX rate as a decimal string (target units per source unit). Absent for a same-currency move.
    - `exchange_conditions` ExchangeConditions: Public exchange conditions. `spread_bps` is the additional client-selected spread for the quote, in integer basis points. OZAV cost, floor, margin, split and cost analytics are internal and are not serialized in public API responses.
      - `spread_bps` integer, required: Additional client-selected spread, in integer basis points (100 bps = 1%).
      - `rate_without_spread` string: The same quote before the `spread_bps` you selected, in the same scale as `rate`. When you did not select a spread it equals `rate` exactly. Use it to tell the spread you added apart from the OZAV rate. If you are comparing our quote to a market reference, compare `rate_without_spread`: `rate` already carries whatever spread you asked for. **To read your spread back exactly, measure both against the quote, not against each other.** Both rates are derived from the same internal base, so dividing one by the other returns slightly more than `spread_bps` (with a 10 bps floor, a 40 bps spread reads as 40.04). The value that is exact is `spread_bps` itself. Absent when the quote does not allow deriving it. OZAV cost, floor and margin stay inside both rates and are never serialized.
      - `amount_deducted` string: The amount taken off `source.amount` before conversion, in the source currency. Present only when an execution cost (the delivery network cost, the local payment rail cost and, on purchases, the settlement safety margin) is charged as an amount instead of being carried by the rate. Today that is limited to stablecoin purchases (fiat to USDT/USDC) on accounts configured for it, so its presence depends on the account, not on the corridor alone. The network cost depends on `target.chain`: send it. Sandbox does not emit it yet. On a transfer created from such a quote, `amounts.rate` is the all-in rate (`target.amount / source.amount`, 8 decimal places), so the transfer reconciles on its own, to that precision, without this field. With it, `(source.amount - amount_deducted) * rate` equals `target.amount`. That is the reconciliation to run when both are present. Absent, the plain `source.amount * rate` holds, as before. **It is published at the source currency's accounting precision, rounded UP.** The reconciliation therefore closes to within one minor unit of the source, and never resolves to more than `target.amount` promises. Rounding it down would let the arithmetic overstate what you receive. **It is not an OZAV charge, and it is not our cost.** It is what was deducted from this quote. OZAV cost, floor and margin stay internal and are never serialized.
    - `travel_rule` TravelRule: Originator and beneficiary information for a crypto send, required by BCB Resolution 520/2025 when value leaves custody to an external address. **Read the "Required for an external crypto send" list below before building this object.** The fields marked required *there* are not the same as the ones marked required *here*: this schema is shared with paths where Travel Rule is supplementary, so it stays permissive. The mandatory set is enforced on `destination.type = crypto_address`, and a request that misses any of it is refused with `422 TRAVEL_RULE_REQUIRED` and a `details.missing_fields` array naming each one. ### Required for an external crypto send | field | rule | |---|---| | `originator.name` | non-blank | | `originator.identity_documents[].type` | at least one entry must be accepted for the customer's country - see the table below | | `originator.identity_documents[].number` | non-blank; for `cpf` and `cnpj`, the check digits must be valid (`cnpj` accepts the alphanumeric format) - otherwise `missing_fields` names `originator.identity_documents.number(digito_verificador_invalido)` | | `originator.identity_documents[].issuing_country` | required on **every** entry | | `originator.address.street_line_1` | non-blank (`street` and `line1` are accepted aliases) | | `originator.address.city` | non-blank | | `originator.address.country` | non-blank | | `beneficiary.name` | non-blank | | `beneficiary.wallet_type` | `external` or `self_custodied` (`hosted` is rejected on this path) | | `beneficiary.wallet_attested_ownership_at` | ISO-8601 timestamp of when the holder attested ownership; must not be in the future | | `beneficiary.is_self` | boolean; `true` when the destination belongs to your own customer | #### Accepted originator identity documents, by country The requirement follows the customer's country of residence as held on the customer record - **not** the country you send inside `travel_rule`, which is stored but never selects the regime. A country not listed below uses the default set. To be precise about where that country comes from: you declare it when you create the customer (`individual.tax_id.country`, or the address), and you can correct it later with `PATCH /customers/{id}`. Both paths refuse a jurisdiction OZAV does not serve. So the regime is not selectable *per request*, but it does follow what you declared for that customer - keep it accurate. | country | accepted `identity_documents[].type` | |---|---| | `BR` | `cpf`, `cnpj` | | `NG` | `bvn`, `nin`, `passport`, `national_id` | | *(default)* | `passport`, `national_id` | If your customer's country is not listed and the default set does not fit its national identifiers, tell us and it gets modelled - the list is data, not code. A destination that is an OZAV wallet id (`ozav_wal_...`) is exempt: the value stays in custody and both sides are already verified. Values inside `originator` and `beneficiary` must be plain strings - do not embed internal or third-party references.
      - `originator` TravelRuleParty: One side of a Travel Rule record. `required` here lists only what every path needs; see `TravelRule` for the stricter set an external crypto send enforces.
        - `name` string, required: Full legal name of the party, as it appears on their verified identity.
        - `account` string: The wallet address or account reference for this party.
        - `identity_documents` object[]: Identity documents for this party. On an external crypto send the originator must carry at least one entry whose type is accepted for the customer's country (see "Accepted originator identity documents, by country"). Accepted under the legacy name `identifying_information` as well; both normalize to the same record.
          - `type` string, required: Document kind, lowercase. Which kinds satisfy the originator requirement depends on the customer's country - see "Accepted originator identity documents, by country" above. Other kinds are accepted and stored but do not satisfy it on their own. A refusal names the kinds that do, in `details.missing_fields` as `originator.identity_documents[<kind>|<kind>]` and in the message. Note that the customer resource types are a different set: `GET /customers` returns `type: other` for a document whose check digits it cannot validate, and `ssn`, `ein`, `nif` and `company_number` for others. None of those satisfy the originator requirement, so for a customer outside the table send `passport` or `national_id` here rather than copying the customer resource's type. A customer whose country is missing, or is not a 2-letter code, is read as `BR`, the strictest row.
          - `number` string, required: The document number. Non-blank. For `cpf` and `cnpj` on an external crypto send, the check digits are validated; `cnpj` accepts the Receita Federal alphanumeric format (12 alphanumeric + 2 numeric check digits). Punctuation is ignored.
          - `issuing_country` string, required: ISO 3166-1 alpha-2 country that issued the document.
        - `address` object: Postal address. Required for the originator on an external crypto send. `street_line_1` is the canonical key; `street` and `line1` are accepted aliases.
          - `street_line_1` string
          - `street_line_2` string
          - `city` string
          - `state` string
          - `postal_code` string
          - `country` string: ISO 3166-1 alpha-2.
        - `wallet_type` 'hosted' | 'external' | 'self_custodied': How the party's wallet is held. On an external crypto send the beneficiary must be `external` or `self_custodied`; `hosted` is rejected there.
        - `wallet_attested_ownership_at` string, date-time: When the holder attested ownership of the wallet.
        - `is_self` boolean: True when this party is your own customer (a send to their own external wallet) rather than a third party.
      - `beneficiary` TravelRuleParty: One side of a Travel Rule record. `required` here lists only what every path needs; see `TravelRule` for the stricter set an external crypto send enforces.
        - `name` string, required: Full legal name of the party, as it appears on their verified identity.
        - `account` string: The wallet address or account reference for this party.
        - `identity_documents` object[]: Identity documents for this party. On an external crypto send the originator must carry at least one entry whose type is accepted for the customer's country (see "Accepted originator identity documents, by country"). Accepted under the legacy name `identifying_information` as well; both normalize to the same record.
          - `type` string, required: Document kind, lowercase. Which kinds satisfy the originator requirement depends on the customer's country - see "Accepted originator identity documents, by country" above. Other kinds are accepted and stored but do not satisfy it on their own. A refusal names the kinds that do, in `details.missing_fields` as `originator.identity_documents[<kind>|<kind>]` and in the message. Note that the customer resource types are a different set: `GET /customers` returns `type: other` for a document whose check digits it cannot validate, and `ssn`, `ein`, `nif` and `company_number` for others. None of those satisfy the originator requirement, so for a customer outside the table send `passport` or `national_id` here rather than copying the customer resource's type. A customer whose country is missing, or is not a 2-letter code, is read as `BR`, the strictest row.
          - `number` string, required: The document number. Non-blank. For `cpf` and `cnpj` on an external crypto send, the check digits are validated; `cnpj` accepts the Receita Federal alphanumeric format (12 alphanumeric + 2 numeric check digits). Punctuation is ignored.
          - `issuing_country` string, required: ISO 3166-1 alpha-2 country that issued the document.
        - `address` object: Postal address. Required for the originator on an external crypto send. `street_line_1` is the canonical key; `street` and `line1` are accepted aliases.
          - `street_line_1` string
          - `street_line_2` string
          - `city` string
          - `state` string
          - `postal_code` string
          - `country` string: ISO 3166-1 alpha-2.
        - `wallet_type` 'hosted' | 'external' | 'self_custodied': How the party's wallet is held. On an external crypto send the beneficiary must be `external` or `self_custodied`; `hosted` is rejected there.
        - `wallet_attested_ownership_at` string, date-time: When the holder attested ownership of the wallet.
        - `is_self` boolean: True when this party is your own customer (a send to their own external wallet) rather than a third party.
    - `created_at` string, date-time: When the transfer was created (RFC 3339 / ISO 8601, UTC).
    - `updated_at` string, date-time: When the transfer was last updated (RFC 3339 / ISO 8601, UTC).
    - `completed_at` string, date-time: When the transfer reached a terminal state (RFC 3339 / ISO 8601, UTC). Present once `status` is terminal.
    - `metadata` Metadata: Free-form string map (max 50 keys).
    - `facility` FacilityOutcome: **What happened to the FX rate lock on this transfer.** Present on the `201` of a create that sent `facility_terms_accepted: true`, **on BRL/Pix flows**. A transfer created without the lock has no `facility` key at all, and its body is byte-for-byte what it has always been. **Two limits worth knowing before you build on it.** **It is a create-time outcome, not transfer state.** `GET /transfers/{id}` does not return it, and neither do the `transfer.*` webhook events after the first one. If you need it later, store it when you receive the `201`. **The lock applies to BRL/Pix on-ramp and off-ramp only.** The fields are accepted on every combination, but on a `crypto_address` destination, a wallet-to-wallet move, or the BRS redemption corridor they have no effect and no `facility` block comes back. Read the absence of the block on those flows as "no lock", not as "locked". **Read `locked` before you present a price to the payer.** A lock can be requested and still not happen, and those cases return `201`: the Pix charge is valid and the customer will pay it, so refusing would lose the operation for a condition that only costs the lock. The consequence is that this block is the only thing standing between "the price is fixed" and "the price is whatever the market does before you pay". | `state` | `locked` | What it means | What to do | |---|---|---|---| | `locked` | `true` | The rate is fixed until `deadline_at`. | Deliver before the deadline. Missing it triggers the schedule's penalty. | | `not_locked` | `false` | The lock did not happen and there is nothing to correct - typically the shared ceiling had no room, or a withdrawal was covered by the balance already delivered. The transfer is valid. | Nothing, but the conversion uses the rate at execution time. Tell whoever is paying. | | `refused` | `false` | The lock was refused by account state - conditions changed between the quote and the acceptance. | Read `reason`, fix it, and accept again. | | `undetermined` | `false` | The hedge may have executed; nothing was recorded as locked. | Do not retry and do not treat as failed. Reconcile with `GET /transfers/{id}`. |
      - `locked` boolean, required: Whether the rate is actually fixed. The one field a payer's decision hangs on.
      - `state` 'locked' | 'not_locked' | 'refused' | 'undetermined', required: The outcome. `not_locked` and `refused` are not synonyms and must not be collapsed: `refused` means the caller has something to correct, `not_locked` means there is nothing to do and the operation proceeds unlocked.
      - `reason` string: Machine-readable slug for why the lock did not happen. Absent when `locked` is `true`. **This is the stable discriminator - branch on it, never on `message`.** Same vocabulary as `details.reason` on `422 FACILITY_LOCK_UNAVAILABLE`, so one mapping serves both the refusal and the degraded path.
      - `message` string: Human-readable explanation in English. May change without notice. Never parse it.
      - `deadline_at` string, date-time: When the locked rate expires. Present only when `locked` is `true`. This is the deadline as recorded, not one recalculated client-side - a second, locally computed deadline is not the one a charge would be based on.
      - `amount` string: **What the customer receives** at the locked conditions, net of your organization's exchange conditions - the same number the floating flow would credit for this order, fixed at acceptance and delivered exactly. Present only when `locked` is `true`.
      - `currency` string: The currency of `amount` and `counterparty_amount`. Present only when `locked` is `true`.
      - `counterparty_amount` string: The gross amount OZAV bought to cover this lock (the exposure). Informational, for reconciliation. It is not what the customer receives - `amount` is. Present only when `locked` is `true`.
    - `settlement_references` TransferSettlementReferences: **The references the payment rail produced for this transfer**, the ones a bank or an auditor asks for to trace the payment. Returned by `GET /transfers/{transfer_id}` and `GET /transfers` for off-ramp transfers (a payout to a `payout_destination` or to your customer's own account) and for wallet sends (a transfer to a `wallet` or a `crypto_address`). **Only the keys that exist are present.** A reference appears once the payout step recorded it, never earlier and never made up; a key that does not apply to the rail is simply absent, and the whole object is absent while nothing has been recorded. Read the absence as "not available (yet)", never as "failed". The `transfer.*` webhook events and the `201` of a create do not carry it: read it again with `GET /transfers/{transfer_id}` after the transfer settles. | Key | Rail | What it is | |---|---|---| | `end_to_end_id` | Pix | The Pix end-to-end id (`E...`). | | `imad` | US wire | Input Message Accountability Data. | | `omad` | US wire | Output Message Accountability Data, when the wire reports it. | | `uetr` | SWIFT | The end-to-end transaction reference (UUID). | | `trace_number` | ACH | The ACH trace number. | | `ach_reference` | ACH | The ACH reference. | | `sepa_reference` | SEPA | The SEPA payment reference. | | `spei_reference` | SPEI | The SPEI tracking key. | | `tracking_number` | Other bank rails | The rail's tracking number. | | `tx_hash` | On-chain | The transaction hash, present only once the send is confirmed on-chain. | | `network` | On-chain | The network the transaction settled on (`ethereum`, `polygon`, `solana`, `tron`, `stellar`). Present with `tx_hash`. |
      - `end_to_end_id` string: Pix end-to-end id.
      - `imad` string: US wire Input Message Accountability Data.
      - `omad` string: US wire Output Message Accountability Data.
      - `uetr` string: SWIFT end-to-end transaction reference.
      - `trace_number` string: ACH trace number.
      - `ach_reference` string: ACH reference.
      - `sepa_reference` string: SEPA payment reference.
      - `spei_reference` string: SPEI tracking key.
      - `tracking_number` string: Tracking number of the bank rail.
      - `tx_hash` string: On-chain transaction hash.
      - `network` string: Network the on-chain transaction settled on.
    - `beneficiary_bank` TransferBeneficiaryBank: **The receiving bank of a fiat payout, masked.** Returned by `GET /transfers/{transfer_id}` and `GET /transfers` for off-ramp transfers sent to a bank account. Account numbers are never returned whole: `account_masked` is `****` plus the last four characters, and `iban_masked` keeps the country and check digits and the last four. Only the keys that are known are present; the object is absent when the destination is not a bank account (a Pix key, for instance) or the bank details were not recorded.
      - `bank_name` string: Name of the receiving bank.
      - `country` string: Country of the receiving bank.
      - `routing_number` string: US ABA routing number, a public bank identifier.
      - `account_masked` string: Account number, last four characters only (`****1234`).
      - `iban_masked` string: IBAN with the middle masked (`DE89**************3000`).
      - `bic` string: SWIFT/BIC code of the receiving bank.
  - `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)
