---
title: "Where does the exchange rate get fixed in an on-ramp and off-ramp? A step-by-step map"
seoTitle: "Where the rate gets fixed in an on-ramp and off-ramp"
description: "A USD on-ramp and a USDC to BRL Pix off-ramp, step by step: who does what, where the exchange rate is locked, where fees apply, and what expiry changes."
date: "2026-09-22"
updated: "2026-09-22"
category: "payments"
author: "BlindPay Team"
howto:
  name: "How to lock the exchange rate on a USDC to BRL off-ramp over Pix"
  steps:
    - name: "Add and approve the recipient's Pix bank account"
      text: "Register the recipient's Pix details as a bank account for a customer who has completed KYC. A payout quote can only be created against an approved bank account."
    - name: "Create a payout quote"
      text: "Call the quote endpoint with the bank account, the amount in minor units, the side the amount is in, who pays the fee, and the network and token you send from. This is the moment the rate and fees are fixed."
    - name: "Show the receiver amount with a countdown"
      text: "Display receiver_amount in BRL and count down to expires_at, which is in epoch milliseconds. Ask the user to confirm inside that window."
    - name: "Authorize the stablecoin amount"
      text: "From your own EVM wallet, approve the contract.amount returned by the quote for BlindPay's contract address. The approval moves no money and does not change the rate."
    - name: "Execute the payout before the quote expires"
      text: "Create the payout with the quote_id and your sender wallet address. BlindPay collects the quoted stablecoin amount and pays BRL over Pix at the locked rate."
    - name: "Track settlement through webhooks"
      text: "Listen for payout.new when the stablecoins are captured and payout.complete when Pix confirms the deposit at the recipient's bank."
faq:
  - q: "Where is the exchange rate set in a stablecoin off-ramp?"
    a: "At quote creation. The payout quote locks the exchange rate and every fee before any stablecoins move. Authorizing the tokens, executing the payout, the on-chain transfer, and the local rail all happen at that locked rate, as long as you execute before the quote's expires_at. After expiry the quote is rejected and you need a new one."
  - q: "Does a US virtual account deposit have an exchange rate?"
    a: "No currency changes hands. A dollar deposit into a US virtual account becomes USDC or USDT, both dollar stablecoins, so there is no FX conversion to price. The cost is a fee. At BlindPay, the fee on deposits below $100 accrues to your monthly invoice, and on deposits of $100 or more it is deducted when the deposit converts."
  - q: "What happens if my quote expires before I execute the payout?"
    a: "The payout is rejected and nothing moves. Request a new quote, show the user the new receiver amount, and execute against the new quote ID. On EVM chains, check that your token approval still covers the new quote's amount, because the amount can change when the rate does."
  - q: "Do I need to interact with the blockchain to off-ramp USDC?"
    a: "Less than you think. Funding from your own EVM wallet takes one signed approval per payout, and the quote returns the exact payload to sign. BlindPay handles the conversion, the liquidity, and the delivery over Pix. With a BlindPay managed wallet, which is in beta and custodial, payouts skip the approval step entirely."
  - q: "How fast does a USDC to BRL payout over Pix arrive?"
    a: "Pix itself is instant and runs 24 hours a day, every day. BlindPay's corridor table lists Pix payouts as instant with up to 5 minutes to confirm. Most of the end-to-end time is the on-chain transfer of the stablecoins, which depends on the network you send from."
---

In a quote-based on-ramp or off-ramp, the exchange rate is fixed once: when the quote is created. A payout quote locks the rate and fees before any stablecoins move, and a payin quote locks them before the payer sends money. Every later step, from the on-chain transfer to the local rail, moves money at that locked rate.

One flow has no exchange rate at all. A dollar deposit into a US virtual account becomes USDC or USDT without a quote, because no currency changes hands. The cost there is a fee, not a rate. This guide maps both flows step by step, using the terms defined in the [on/off-ramp liquidity glossary](/resources/more/onramp-offramp-liquidity-glossary).

**Key takeaways**

- The rate is set at quote creation, not at execution and not at settlement.
- Authorizing tokens, the on-chain transfer, and the local rail never change the rate inside the window.
- A USD to USDC on-ramp has no FX conversion. Its cost is a fee line.
- An expired quote is rejected, not repriced. Nothing moves until you create a new one.
- On a USDC to BRL Pix payout, most of the elapsed time is the on-chain leg. Pix itself is instant.

## Where does the exchange rate get fixed?

At quote creation. Before the quote exists, any rate you see is indicative. Once it exists, the rate, the fees, the sender amount, and the receiver amount are locked until `expires_at`.

BlindPay's docs group the flows as Receive (payins, the on-ramp side) and Send (payouts, the off-ramp side), with the stablecoins held in a wallet in between. On the Send side, the [payout quote](/docs/payout-quotes) locks the exchange rate and the fee split, and a payout can only execute against a valid, unexpired quote. On the Receive side, the [payin quote](/docs/payin-quotes) locks the amount, method, fees, and destination before the payer sends anything.

At BlindPay, payout and payin quotes expire 5 minutes after creation by default. OTC payin quotes expire in 10 seconds. Read `expires_at` (epoch milliseconds) from each response rather than hardcoding the window.

## How does a USD on-ramp through a virtual account work?

A [US virtual account](/docs/virtual-accounts) is a bank account in your customer's name with its own routing and account number. Each deposit creates a payin, and BlindPay sends USDC or USDT to the wallet linked to the account.

1. **You** create the virtual account for a customer who passed KYC and link a destination wallet. The account goes through a compliance review and a bank review.
2. **The payer** sends an ACH, wire, or SWIFT transfer to the account's routing and account number, like any other bank account.
3. **BlindPay** sees the deposit and creates a payin. Your endpoint receives a `payin.new` webhook.
4. **BlindPay** converts the deposit and sends USDC or USDT to the linked wallet on its network.
5. **Your endpoint** receives `payin.complete` when the stablecoins land.

Where is the rate set? Nowhere, because there is no FX. USDC is designed to be [redeemable 1:1 for US dollars](https://www.circle.com/usdc), so dollars in and dollar stablecoins out is not a currency exchange. What you pay is a fee: at BlindPay, deposits below $100 accrue the fee to your monthly invoice (`billing_fee_amount`), and deposits of $100 or more have it deducted when the deposit converts (`transaction_fee_amount`).

Timing depends on the rail the payer chose. BlindPay's [corridor table](/docs/overview) lists RTP as instant and ACH and wire deposits at up to 5 business days. [Stablecoin virtual accounts explained](/resources/more/stablecoin-virtual-accounts-explained) covers the account itself.

## How does a USDC to BRL off-ramp over Pix work?

This is the flow where the rate matters, because USDC becomes Brazilian reais.

1. **You** add the recipient's Pix details as a [bank account](/docs/bank-accounts). It must be approved before you can quote against it.
2. **You** create a payout quote with the bank account, `request_amount` in minor units, `currency_type`, `cover_fees`, `network`, and `token`. **The rate and fees are fixed here.**
3. **Your app** shows `receiver_amount` in BRL with a countdown to `expires_at`.
4. **The sender** authorizes the stablecoins. From your own EVM wallet, that means calling `approve` with the `contract` payload from the quote. See [payout with EVM](/docs/payout-evm).
5. **You** execute the payout with `quote_id` and `sender_wallet_address` before the quote expires. BlindPay collects the quoted stablecoin amount, and `payout.new` fires once the funds are captured.
6. **Pix** delivers the reais to the recipient's bank. `payout.complete` fires when the deposit is confirmed.

Pix is Brazil's instant payment system and runs 24 hours a day, every day, according to the [Banco Central do Brasil](https://www.bcb.gov.br/en/financialstability/pix_en). BlindPay's corridor table lists Pix payouts as instant, with up to 5 minutes to confirm.

## Which step sets the rate, and which steps charge fees?

Only one step sets the rate. Fees are also fixed at that step, then applied when money moves.

| Step | What happens | Who acts | Where the rate is set | Where fees apply |
| --- | --- | --- | --- | --- |
| 1. Bank account | Pix details registered and approved | You, BlindPay | Not yet | None |
| 2. Payout quote | Rate, fees, and both amounts locked | BlindPay | **Here** | Fees calculated and shown |
| 3. Confirmation screen | Receiver amount and countdown shown | Your app | Already fixed | None |
| 4. Authorization | Token approval signed | Sender's wallet | Already fixed | Network gas for the approval |
| 5. Execution | Stablecoins collected | BlindPay, blockchain | Already fixed | Quoted fees taken |
| 6. Delivery | BRL paid over Pix | Pix | Already fixed | None beyond the quote |

On the virtual account on-ramp, step 2 does not exist. The fee is applied at step 4 of that flow, or on your invoice for deposits under $100.

## What happens if the quote expires?

It is rejected, and nothing moves. An expired quote is never executed at a new rate.

Request a new quote, show the user the new `receiver_amount`, and ask them to confirm again. Do not resubmit the old quote ID in a retry loop. If you fund from an EVM wallet, check that your approval still covers the new quote's `contract.amount`, since the amount can shift when the rate does. If an approval transaction is slow to mine, quote again rather than executing against a quote that is about to lapse.

Expiry is a normal path, not an error. [Stablecoin FX slippage](/resources/more/stablecoin-fx-slippage-live-quotes) explains why a binding window is what keeps slippage at zero.

## What do you not have to touch?

The conversion, the liquidity, and the local rail. You do not choose a market maker, hold reais, open a Brazilian bank account, or connect to Pix.

You touch the blockchain once per payout when you fund from your own wallet: a single signed approval, with the payload handed to you in the quote. With a BlindPay managed wallet, which is in beta and custodial, payouts skip the approval step entirely. [What happens on-chain in a stablecoin payment](/resources/more/what-happens-on-chain-in-a-stablecoin-payment) shows the transactions behind the scenes.

## What do the numbers look like on one payout?

**Illustrative example.** A company off-ramps 10,000 USDC to a supplier's Pix account, with `cover_fees: false`. The rates and fee are made up for the arithmetic, not BlindPay pricing.

| Moment | Number | Arithmetic |
| --- | --- | --- |
| Quote created | Market rate 5.40, quoted rate 5.373 BRL per USDC | Margin (5.40 - 5.373) / 5.40 = 0.50% |
| Quote created | Receiver amount before flat fee: 53,730 BRL | 10,000 x 5.373 |
| Quote created | Flat fee 20 BRL, receiver amount 53,710 BRL | 53,730 - 20 |
| Approval and execution | 10,000 USDC collected | Sender amount from the quote |
| Pix delivery | 53,710 BRL credited | Same as the quote |

Now suppose the user waits too long and the quote expires. The new quote comes back at 5.360. The new receiver amount is 10,000 x 5.360 - 20 = 53,580 BRL, which is 130 BRL less than the first screen. The user sees that number before anything moves, and decides again.

For reference, Brazil's central bank publishes daily buy and sell [PTAX dollar rates](https://dadosabertos.bcb.gov.br/dataset/dolar-americano-usd-todos-os-boletins-diarios). They are a sanity check, not an executable price.

## What to do next

Create one payout quote on a development instance and map each field to the six steps above. The [payout quotes docs](/docs/payout-quotes) show the full request and response.
