Where does the exchange rate get fixed in an on-ramp and off-ramp? A step-by-step map

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.

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.

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 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 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 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, 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 lists RTP as instant and ACH and wire deposits at up to 5 business days. 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. 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.
  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. 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.

StepWhat happensWho actsWhere the rate is setWhere fees apply
1. Bank accountPix details registered and approvedYou, BlindPayNot yetNone
2. Payout quoteRate, fees, and both amounts lockedBlindPayHereFees calculated and shown
3. Confirmation screenReceiver amount and countdown shownYour appAlready fixedNone
4. AuthorizationToken approval signedSender's walletAlready fixedNetwork gas for the approval
5. ExecutionStablecoins collectedBlindPay, blockchainAlready fixedQuoted fees taken
6. DeliveryBRL paid over PixPixAlready fixedNone 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 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 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.

MomentNumberArithmetic
Quote createdMarket rate 5.40, quoted rate 5.373 BRL per USDCMargin (5.40 - 5.373) / 5.40 = 0.50%
Quote createdReceiver amount before flat fee: 53,730 BRL10,000 x 5.373
Quote createdFlat fee 20 BRL, receiver amount 53,710 BRL53,730 - 20
Approval and execution10,000 USDC collectedSender amount from the quote
Pix delivery53,710 BRL creditedSame 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. 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 show the full request and response.

FAQ