---
title: "Retrieve a quote"
method: GET
path: "/quotes/{quote_id}"
tags: ["Quotes"]
---

# Retrieve a quote

`GET /quotes/{quote_id}`

## Request

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

Retrieves a quote by its `ozav_q_` id, including its `rate`,
`exchange_conditions`, and `expires_at`. Every quote today is
`rate_type: floating` with `expires_at: null`: the rate is indicative
and re-derives at execution (see `Quote.rate_type`).

## Path parameters

- `quote_id` string, required

## Response `200`

The quote.

- Quote: An FX quote - opaque, indicative (`floating`), and carrying the applied exchange conditions. Execute it by creating a transfer that references its `id`.
  - `id` string, required: Opaque quote identifier, prefixed `ozav_q_`.
  - `object` 'quote', required: Resource type discriminator; always the literal `quote`.
  - `customer_id` string: Opaque customer id (prefixed `ozav_cus_`) the quote was created for.
  - `rate_type` 'floating' | 'locked', required: How the `rate` behaves until execution. - `floating` - indicative; the rate re-derives at execution time. **Every quote returns this today**, and it comes with `expires_at: null`: a firm price holds for only a few seconds, so OZAV re-quotes at execution instead of expiring your quote. Treat `rate` as indicative: what executes can differ from the quote. - `locked` - held fixed until `expires_at`. **Not served on this API yet.** When it ships it will require the price-lock facility enabled on your account, and will carry a real `expires_at`. Ask OZAV if you need a firm price today. Do not branch on `expires_at` being non-null: on `floating` it is always `null`, in sandbox and in production alike.
  - `rate` string, required: The FX rate as a decimal string - target-currency units per one source unit, all-in except for any `amount_deducted` (it already carries any `spread_bps` you added). Indicative on a `floating` quote, which is every quote today: it re-derives at execution. On the quote, `source.amount x rate` gives `target.amount` (with `amount_deducted` present, `(source.amount - amount_deducted) x rate`).
  - `source` MonetaryAmount, required: 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, required: 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.
  - `exchange_conditions` ExchangeConditions, required: 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.
  - `expires_at` string, date-time, nullable, required: When the quote expires (RFC 3339 / ISO 8601, UTC). **`null` on a `floating` quote is the normal answer, not a missing value, and it does not mean the quote is valid forever.** A floating rate is indicative: it re-derives at execution, so there is no window to "accept" it within. Quote as close to execution as you can, and read the rate you are given at execution as the one that applies. A timestamp appears only when the rate is held: a `locked` quote, which expires at this instant and is refused after it.
  - `created_at` string, date-time, required: When the quote was created (RFC 3339 / ISO 8601, UTC).
  - `facility` FacilityPreview: **The FX rate lock conditions (`trava de câmbio`), previewed alongside the quote.** Returned only when the request set `facility_preview: true` and your organization has the facility contracted and operable. Its `terms_version` is the value `POST /transfers` accepts as `facility_terms_version`, and this is the only place it comes from. **Absence is the answer, not an error.** A client without the facility gets a normal `201` quote with this block missing and no explanation - the preview never fails a quote, because the quote is the product. To learn why, attempt the transfer: it refuses loudly with `422 FACILITY_LOCK_UNAVAILABLE` and a machine-readable `details.reason`. **These numbers are contractual, not settings.** The penalty, the minimum charge, the reserve percentage, the client window and the cutoff come from the schedule (`Termo`) your organization signed. They are shown here because an acceptance over conditions the payer never saw does not support a charge later.
    - `terms_version` string: The version of the signed schedule these conditions come from. Echo it back as `facility_terms_version` on `POST /transfers`. It must match byte for byte.
    - `locked_rate` number: The rate implied by this quote, at this instant - indicative. The binding lock is priced at acceptance (`POST /transfers` with `facility_terms_accepted: true`) from a firm quote, and what the customer receives comes back as `facility.amount` on the `201`, net of your organization's exchange conditions. Do not present `locked_rate` as the final price. The rate that would be locked, at this instant, always expressed as **BRL per unit of the stablecoin** - the same base the acceptance records. On an off-ramp (stable to BRL) it is `expected_amount / source_amount`; on an on-ramp (BRL to stable) it is `source_amount / expected_amount`. Never a ratio in the other direction.
    - `source_amount` string: How much is being locked, in the source unit.
    - `source_asset` string: The source unit of `source_amount`.
    - `expected_amount_minor` string: What the customer receives at `locked_rate`, in the minor unit of `expected_amount_currency` - the quote's target currency: centavos (2 decimals) when the target is BRL, micro-units (6 decimals) when the target is a stablecoin such as USDC or USDT. A string, because the value can exceed what a JSON number holds exactly.
    - `expected_amount_currency` string: The currency of `expected_amount_minor`: the quote's target. `BRL` on an off-ramp preview, the stablecoin on an on-ramp preview. Read it before interpreting the amount - the minor unit follows it.
    - `client_deadline_at` string, date-time: By when the customer must deliver (pay the Pix, or hand over the asset). Missing it is what triggers the penalty below.
    - `settlement_deadline_at` string, date-time: OZAV's own settlement deadline.
    - `settlement_mode` 'T0' | 'T1': Settlement day for this lock.
    - `cutoff_time_brt` string: The daily cutoff, in BRT, from the schedule. **It and the client window cut each other short: the deadline is always whichever comes first.** With a 120-minute window and a 17:00 cutoff, locking at 16:00 gives 60 minutes, not 120, and locking at 16:30 gives 30. That is the schedule, not a defect - but it is the first thing that surprises an integrator, so branch on `client_deadline_at`, never on the window length.
    - `charge_rule` string: How the charge is composed if the customer does not deliver in time. Cumulative, and the penalty is the large part.
    - `penalty_pct` number: Penalty, as a fraction. `0.05` is 5%.
    - `minimum_charge_usd` number: The minimum charge per unsettled operation, in USD. **Not basis points** - an absolute amount.
    - `reserve_pct` number: The reserve (collateral) required, as a fraction of the open position - aggregate, including the new one, not per transaction. `0.02` is 2%. It must be constituted before acceptance and maintained while positions are open; if the ratio falls below the schedule's minimum, a top-up call is triggered.
    - `holiday_risk` boolean: `true` when the deadline crosses a business-day boundary the calculation cannot resolve against a holiday calendar. Treat the deadline as approximate and confirm.
    - `capacity_guaranteed` false: **Always `false`.** This preview prices the lock; it does not reserve room under the shared ceiling, which is resolved at acceptance. Reading headroom here and deciding later is exactly the non-atomic pre-check the pool exists to prevent - two clients would both see "there is room" and both proceed.
  - `facility_unavailable` FacilityUnavailable: **Why the rate-lock preview is not available for this account.** Returned instead of `facility` when the request set `facility_preview: true` and the facility cannot be offered. It never fails the quote - the quote is the product - it only explains the absence. **Read this instead of guessing at a missing `facility` block.** Branch on `reason`, which is the stable slug and the same vocabulary as `details.reason` on `422 FACILITY_LOCK_UNAVAILABLE`, so one mapping serves the preview and the refusal. **This preview runs the offer gates only** - the ones that decide whether a lock can be shown. It does not evaluate collateral or your contractual limits, because those are resolved at acceptance, inside the lock. So `collateral_insufficient` is **not** a value you will see here: it appears on `POST /transfers` instead. The table below is the complete set this endpoint can return. | `reason` | What it means | What to do | |---|---|---| | `client_not_exempt` | Your organization has not contracted the facility. | Talk to your account manager - no request will succeed until it is contracted. | | `no_client` | The key does not resolve to an organization the facility knows. | Contact support with the request id. | | `no_terms` / `terms_inactive` / `no_terms_version` / `invalid_terms` | The signed schedule (`Termo`) is missing, not active, or incomplete. | A contract step, not a parameter. Talk to your account manager. | | `past_client_cutoff` | Locking now is past today's cutoff. | Try earlier on the next business day. | | `client_window_too_short` | Today's cutoff is too close to leave a usable delivery window (under ten minutes). | Try earlier on the next business day. | | `client_window_exceeds_settlement` | Today's window does not fit settlement. | Ask for a shorter window, or try earlier. | | `unwind_not_ready` | The facility is not being offered right now. Not about your account. | Retry later; if it persists, contact support. | | `pool_config_missing` / `execution_margin_not_configured` / `settlement_mode_not_configured` | A platform setting the facility needs is missing. Not about your account. | Retry later; if it persists, contact support with the request id. | | `invalid_amounts` | The amounts on the quote cannot be used to price a lock. | Check `source.amount` and the target; if they look right, contact support. | | `previa_indisponivel` | The preview could not be computed. Not about your account, and not about your request. | The quote itself is valid and usable. If you need the preview, contact support with the `request_id` of this response. |
    - `reason` string, required: Stable, machine-readable slug. **Branch on this, never on `message`.**
    - `message` string, required: Human-readable explanation in English, safe to show an operator. It includes `(reason: <slug>)`. May change without notice.
  - `via` object: Present only on a quote requested with `via`: the stablecoin and the network the conversion passes through.
    - `currency` string, required
    - `chain` string, required
  - `legs` QuoteLeg[]: Present only on a quote requested with `via`. The two legs, in order: BRL → stablecoin, then stablecoin → target. The second leg is priced on the stablecoin the first delivers, rounded down to the cent; a remainder below 0.01 stays in your customer's wallet. The top-level `source` is the first leg's source and `target` the second leg's target.
    - `source` MonetaryAmount, required: 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, required: 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, required: Target units per one source unit, for this leg.
    - `exchange_conditions` ExchangeConditions, required: 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.

## Other responses

- `401`: Missing or invalid API key.
- `404`: The resource does not exist, is not visible to the caller, or the id was malformed for this position (see "Resource ids" under Errors). These share one status; `code` and `message` tell them apart.

---

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