An off-ramp API for crypto exchanges is a service that converts a user's stablecoin balance into local currency and pays it to the user's own bank account over a local rail. The provider handles the conversion, the payout and the payment-leg compliance checks.
How does a withdrawal move from an exchange balance to a bank account?
Six steps, with the exchange's own ledger around them. The basics are in what a stablecoin off-ramp is.
The six steps of a fiat withdrawal, and who owns each
Step | What happens | Owner |
1. Onboard | The user becomes a verified customer of the provider | Provider, started by the exchange |
2. Register the account | The user's own bank account is stored as a destination | Exchange, checked by the provider |
3. Quote | The user sees the local amount before confirming, exact on a locked quote and labelled as an estimate otherwise | Provider, shown by the exchange |
4. Fund and execute | The exchange moves stablecoin to the user's wallet and submits the payout | Exchange |
5. Convert and pay | The balance is sold and the local payment leaves over the rail | Provider |
6. Close the ledger | The exchange marks the withdrawal done or failed from a webhook | Exchange |
On Lumx, the withdrawal is an off-ramp: it debits the stablecoin wallet of the customer who owns the destination, converts it, and pays the local rail. The exchange follows it through the offramp.transferring_stablecoin, offramp.trading, offramp.transferring_fiat and offramp.success events.
Onboarding the exchange and each withdrawing user
Two parties are verified: the exchange as a business, and each withdrawing user as an individual with a wallet and a destination of their own.
A VASP (virtual asset service provider, the Financial Action Task Force's term for exchanges and custodians) is allowed with disclosure, which triggers Enhanced Due Diligence and ongoing monitoring instead of a standard review (what a VASP is). A business that runs without the licence its jurisdiction requires is not served at all. Start the exchange's own KYB (know your business, the verification of a company and its owners) first (what KYB involves).
On Lumx, the exchange registers as a business customer, picks VASP as its business activity, and submits its regulated-activity information with the licence. A VASP goes through KYB Enhanced, which targets three business days.
The users are the second half. A user who lives in Brazil is created as an individual customer with a tax ID that is valid in Brazil, accepts the terms of service and submits identity data, a document and a liveness check, and the standard KYC (know your customer, the verification of an individual) review targets one business day. An exchange that already verified the user on Sumsub can reuse that work: with a data-sharing agreement arranged through compliance@lumx.io, the exchange passes a share token when it creates the customer. With reuse the customer does not resubmit documents or liveness, and it is open to individuals only.
I recommend starting a user's verification when they first deposit or open the withdraw screen, not at the first withdrawal, so a user who has just sold is not waiting on a review. I would also keep the exchange's ledger as the source of truth and treat the provider wallet as a staging balance, filled only for a withdrawal the user is about to confirm.
Which rails can a withdrawal land on?
The user's local rail, set by the destination they register: Pix (Brazil's instant payment system, run by the Central Bank of Brazil) in Brazil, SPEI (Mexico's interbank transfer system, run by Banxico) in Mexico, and Bre-B (Colombia's instant payment system) or ACH in Colombia.
The three withdrawal rails for BRL, MXN and COP, with what the destination needs and the per-payment ceiling
Currency | Rail | What the destination needs | Ceiling per payment | Settles |
BRL | Pix | The user's Pix key and tax ID. See USDC to BRL | The customer's off-ramp limit | Instant, every day at any hour, unless a review applies |
MXN | SPEI | A CLABE (the 18-digit Mexican bank account number), holder type, tax ID and name | The customer's off-ramp limit | Instant, every day at any hour, unless a review applies |
COP | Bre-B | The receiver's Bre-B key. See USDC to COP | 1,000 UVB, about COP 12.1 million | Instant, like Pix |
COP | ACH Colombia | Name, document type and number, email, bank, account and account type | No per-payment limit | No fixed time quoted |
Large amounts can trigger a risk review of up to two business hours, and a transaction held for an RFI (request for information, a compliance hold that asks for a document) waits until it is answered.
COP has two routes, and the user picks one when the destination is created, because the rail stays fixed on it. A Bre-B payout above the ceiling is refused, never split or moved to another rail. The ceiling comes from the Banco de la República, Circular Reglamentaria Externa DSP-465, Asunto 18, numeral 3.2 (March 31, 2026), and binds every provider. In 2026, 1,000 UVB is COP 12,110,000. A larger withdrawal needs a second destination on ACH Colombia. The full pair list is on the coverage page. For providers ranked by region, read the best stablecoin off-ramp APIs.
On Lumx, a destination for Pix or Bre-B or SPEI or ACH Colombia is created under a customer record with the holder relationship SELF when the user is paying their own account, and it goes through verification before it can be used, which the destinations.approved webhook reports.
Quote first, execute second: the withdrawal screen
The screen should show the number the user will receive before committing: a quote when the confirm step opens, then the payout when the user taps confirm. A payout without a quote lands at the market rate of that second. How long a price holds is in how long a payment quote lasts, and the layout in the cross-border payment quote screen.
The confirm screen needs the stablecoin leaving, the rate, the exchange's own fee, the local amount arriving and the time left on the price.
Use a floating rate, with no lock, for the estimate while the user types. On the confirm screen use a 30-second lock, which carries no fee. A 1-minute lock fits a 2FA step and a 5-minute lock a review step, and both carry a fee.
The payout debits the user's wallet and a lock lasts five minutes at most. A transfer between two onboarded customers settles in seconds with no FX, so the exchange can fund the wallet when the user confirms and still use the lock. Do not start the lock before the funding step is ready, or accept a floating rate and label the amount as an estimate.
For Pix, SPEI, Bre-B and ACH Colombia withdrawals on Lumx, POST /exchange-rates with type: LOCKED returns an id and an expiresAt, plus finalTargetAmount and finalExchangeRate, the figures to show on the confirm screen. The off-ramp then takes that id as exchangeRateId. An expired lock returns EXCHANGE_RATE_EXPIRED, and the fix is a new quote. The exchange's own withdrawal fee is a partner fee, in basis points, a flat dollar amount or both, applied by sending its partnerFeeId with the rate request and collected in a wallet the exchange names. A locked quote by target amount works for USDC to COP too.
How much does an off-ramp API cost an exchange?
Three prices stack on a withdrawal: the provider's fee, set by its agreement and returned on the quote, the exchange's own fee, shown as its own line on the confirm screen, and whatever the receiving bank charges.
Lumx prices on a monthly platform fee plus usage-based per-transaction fees, transparent FX on locked quotes, no pre-funding; no published plans. The exchange's own partner fee rides on the same quote. The Lumx fee is not deducted from the amount that arrives on Pix, SPEI or Bre-B: the local amount on the quote is what arrives, and only the receiving bank can charge the user. No pre-funding means no capital parked in settlement (nostro) accounts before a withdrawal, and the user's wallet balance is a separate matter.
Limits a withdrawal has to respect
A withdrawal over a limit is rejected before any money moves, so read the limit before you draw the screen. Limits follow the verification level and count per transaction type, so a deposit never uses up payout room.
Default limits by verification level, in USD, from the Lumx transaction-limits page read on November 1, 2026
Limit | KYC Standard | KYC Enhanced | KYB Standard | KYB Enhanced |
Per transaction | 7,500 | 25,000 and above | 50,000 | 50,000 and above |
Daily | 15,000 | 50,000 and above | 100,000 | 100,000 and above |
Monthly | 15,000 | 50,000 and above | 100,000 | 100,000 and above |
For an exchange of retail users, the first column bites: a 20,000 USD withdrawal needs Enhanced verification first, and small withdrawals can still hit the daily cap on the last one. The limits are the same in every country. Ceilings by rail are in payout limits and cut-offs by rail.
On Lumx, reading a customer with includeTransactionLimits=true returns the max, used and remaining amounts for the off-ramp window, so the screen can show "you can withdraw up to X today". A request over a limit returns TRANSACTION_LIMIT_EXCEEDED with a 422. Limits are per transaction type, so each move the exchange makes with the Transfer endpoint to fund a user wallet counts against its own transfer limit, in the KYB columns of the table: 50,000 per transaction and 100,000 per day at Standard. A limit-increase request, filed with a supporting document, targets a one-business-day review.
Travel Rule data on a withdrawal
FATF Recommendation 16, the Travel Rule (revised in June 2025), requires a VASP that sends a virtual asset transfer to pass originator and beneficiary information to the receiving VASP. That obligation sits with the exchange, and its compliance team owns the answer on which transfers carry the data, including a transfer to a user's wallet at a provider. Fields and the hold-or-return decision are in the Travel Rule guidelines.
On Lumx, every transaction is screened before it runs, in both directions, and Lumx is aligned with the Travel Rule. Destination wallet addresses will become destinations in the API, like the fiat accounts, with no date yet. Every withdrawal carries the holder details of the destination and a purpose code, which is what the compliance review reads. A user paying their own account sends PERSONAL_ACCOUNT, valid only with a SELF destination, and a holder tax ID that differs from the customer's returns HOLDER_TAX_ID_MISMATCH.
What the exchange ledger does at each off-ramp status
A withdrawal debits the user once and holds the amount as pending until a final status arrives.
Off-ramp statuses and the matching ledger action
Status | What it means | Ledger action |
| Stablecoin is leaving the user's wallet | Hold the amount as pending |
| The conversion is being executed | Keep holding |
| The local payment is on its way to the bank | Show "sent to your bank" |
| The payment left the banking partner | Release the hold and close the withdrawal |
| The off-ramp did not complete | Do not credit by timer; read the transaction and write to operations@lumx.io first |
Store the idempotency key with the withdrawal row before you send, and reuse it on every retry. Keys expire after 24 hours, and the same key with a different body returns a 409.
Lumx calls the off-ramp complete when the payment leaves its banking partner, which is offramp.success. An RFI left unanswered for 24 hours may end with the transaction refunded to the sender.
When an off-ramp API is the wrong tool for an exchange
It is the wrong tool when the exchange cannot name who is being paid, because a pooled payout account with no per-user onboarding is nesting. Nesting needs written approval from compliance (compliance@lumx.io); without it the transaction may be delayed, rejected or reversed. It is also the wrong tool without the licence the exchange's jurisdiction asks for, since Lumx does not onboard unlicensed virtual asset providers. Fit test: does each user have a Pix key, a Bre-B key, a CLABE (the Mexican bank account number) or the ACH Colombia bank details?
How this works on Lumx
Lumx is stablecoin payments infrastructure for businesses that move money between Latin America and the rest of the world: one API to collect, hold, convert, and pay out in BRL, MXN, COP, USD, EUR, and GBP or in USDC and USDT, over local rails such as PIX, SPEI, PSE, Bre-B, ACH, FEDWIRE, SEPA, and Faster Payments, with SWIFT and on-behalf-of payments and collections (POBO and COBO) in USD, EUR, and GBP, plus named virtual accounts, custodial wallets, and KYB/KYC built in.
For an exchange, the shape is this. The exchange is one business customer. A withdrawing user who lives in Brazil is an individual customer with a wallet on each supported network and a SELF destination on Pix. For users in Mexico or Colombia, tell Lumx where your users live when you scope the integration, so the onboarding path is settled before you build the screen. Withdrawals run in USDC on Ethereum, Polygon and Base, and USDT on Ethereum, Polygon and Tron. The exchange moves stablecoin to the user's wallet, either to the address or with the Transfer endpoint between two onboarded customers, and the off-ramp then debits that wallet.
Compliance sits inside the API: KYB for the exchange, KYC for each user through Sumsub, verification of each destination and monitoring of blockchain transactions through Chainalysis. The product is under global payments, the calls in how to integrate a stablecoin API, and the corridors on the operator map. An API integration takes 2 to 3 weeks to go-live.
The first sandbox run should cover the cases a happy path hides: a withdrawal followed to offramp.success, a lock left to expire so the exchange sees EXCHANGE_RATE_EXPIRED, an amount over the limit that returns TRANSACTION_LIMIT_EXCEEDED, a retry with the same idempotency key, an offramp.failed event that leaves the ledger pending, and a Bre-B payout above the ceiling.
Verified on November 1, 2026. Operational context, not legal, tax, or investment advice.
How does an off-ramp API for crypto exchanges work?
The exchange verifies each withdrawing user with the provider, registers the user's own bank account, requests a quote, moves stablecoin to the user's wallet and submits the payout. The provider converts and pays the local rail, and webhooks tell the exchange when to close the withdrawal. On Lumx, the user is a customer with a wallet, the bank account is a SELF destination, and the off-ramp debits that wallet.
How long should the withdrawal quote last?
Long enough to read the screen and confirm, since a longer lock costs more. On Lumx, a withdrawal rate locks for 30 seconds at no fee, or 1 or 5 minutes with a fee, and the off-ramp takes the quote's exchangeRateId. An expired lock returns EXCHANGE_RATE_EXPIRED, so request a new one.
How much does an off-ramp API cost an exchange?
The exchange pays the provider's fees and chooses whether to add its own. On Lumx, pricing is a monthly platform fee plus usage-based per-transaction fees, transparent FX on locked quotes, no pre-funding; no published plans, and the Lumx fee is not deducted from the amount that arrives on Pix, SPEI or Bre-B.
How long does a fiat withdrawal take?
On Pix and SPEI the payment is instant at any hour, every day. A large amount can add up to two business hours of risk review, and an RFI holds the transaction until it is answered. On Lumx, offramp.success means the payment has left the banking partner, and the receiving bank can still hold funds for its own review.
What should the exchange do when a withdrawal fails?
Keep the amount pending, never credit it by timer, and read the transaction first. On Lumx, offramp.failed is the event to watch. Its payload says "Your transaction has failed. Please contact support.", so the ledger step, a credit back or a retry, follows the exchange's own policy and a check of the transaction.





