A stablecoin API quote is the price a payments API returns before any money moves. An indicative rate shows where the market is today and commits to nothing. A binding quote fixes the rate and the fees under an ID until an expiry time, and the payment is refused after it. Most integrations need both, one after the other.
The category is defined in what a stablecoin API is, and a full integration is in the seven-step integration guide. A binding quote also leaves a record: the stored quote ID and its expiresAt tie the price a customer saw to the transaction that settled it. The names below are Lumx's, read from the Lumx docs and its API reference on November 1, 2026.
What is the difference between an indicative rate and a binding quote?
An indicative rate tells you where the price is. A binding quote tells you what you will get. The FX Global Code asks market participants to "make clear whether the prices they are providing are firm or merely indicative" (Principle 9, read on globalfxc.org on November 1, 2026). A payments API should draw the same line in its response, not in a footnote.
The two quote types side by side, with the Lumx request value and the amount inputs the docs allow for each.
Indicative rate | Binding quote | |
Provider commits to the price | No | Yes, for the lock window |
Pins the price for execution | No lock and no expiry, so the transaction prices at execution | Yes, the |
Has an expiry | No | Yes, an |
Lumx request value |
|
|
Amount you can send | Source amount only | Source amount or target amount |
Additional fee, per the docs | None | None for 30s, a fee for 1m and 5m |
Right place in the product | Amount field, calculators, rate sheets | Confirm screen |
On Lumx both live on one endpoint, POST /exchange-rates, and the type field picks the behavior. A floating rate is a real-time reference with no price lock, and the docs say the rate applied at execution can vary slightly with the market. A locked rate returns an exchangeRateId that you pass on the transaction request to settle at the quoted price.
Show the indicative rate while the user types, lock on confirm
The pattern that works: an estimate while the user types, a lock on the confirm screen, and an execute against the lock's ID on confirm.
Debounce the amount field by 300 to 500 milliseconds, then request a floating rate and label the result "about".
On the confirm screen, request a locked rate and render the amount the recipient will get, plus a countdown driven by the expiry timestamp.
On confirm, execute with the quote ID before the countdown ends.
If the lock lapses first, request a new one, show the new number, and say so only if it moved.
Creating a lock on every keystroke is the common mistake. The window starts running before the user has seen a number, and a busy amount field is a good way to meet 429 TOO_MANY_REQUESTS. The estimate and the lock will also differ. The market moves between the calls, and the fees can differ too. Per the partner fees docs, a locked rate fixes the partner fee at quote time, while a floating rate applies it when the transaction is created. A partner fee applies only when the request carries a partnerFeeId, since there is no default. Show the estimate as "before fees" unless you read the fees object in the response. The screen itself is in the cross-border payment quote screen.
On Lumx the preview is a floating request with customerId, rail, sourceCurrency, sourceAmount and targetCurrency, and the response carries the rate and the target amount. The rails the request accepts are PIX, SWIFT, SEPA, ACH, FEDWIRE, SPEI (Mexico's interbank transfer system, run by Banxico) and FPS. To show a rate to visitors who are not signed in, link the converter pages for USDC to BRL, USDC to MXN and USDC to COP.
I would put the lock behind the confirm button and nowhere else. The amount field gets the indicative rate, and I would label that number as an estimate in the interface, because a preview that quietly becomes the price is a support ticket waiting for a market move. I would also write the idempotency key to the payout row before the execute call goes out, and reuse it only when no response came back at all. A retry after the same key already got an answer replays that answer, and a retry with a fresh key after a lost response is how one payout becomes two.
What a Lumx locked quote contains and what it costs
A locked quote answers four questions with numbers: the market rate, the rate after fees, each fee, and how much arrives.
Fields of a locked exchange rate response on Lumx, from the API reference.
Field | What it holds |
| The quote ID, sent as |
| Expiration date and time in UTC |
| The amount and rate before fees |
| Each with a rate in basis points, a flat amount, a total amount and a currency |
| The amount and rate after fees, the numbers to show the user |
The docs' own sample responses make the arithmetic visible. They are examples at a 1m lock, not a rate card. A Pix (Brazil's instant payment system, run by the Central Bank) on-ramp of BRL 59,255.50 shows a base amount of 10,000 USDC at 5.9255 and a final amount of 9,978 USDC at 5.9386. The gap is 22 USDC, or 0.22% of the base amount, and 59,255.50 divided by 9,978 returns the final rate. The base rate is the raw mid, so the gap is fees only, with no FX margin inside it. The response splits it: fees.lumx is 11 USDC and fees.partner is 11 USDC, each 10 bps plus a flat 1 USDC in the docs sample.
For a quote by source amount, the fees come out of the target side: the sender pays the amount they typed and the recipient gets finalTargetAmount. For a quote by target amount, the partner fee comes on top of what the payer funds: on a BRL to USDC quote it raises the reais to send for the same USDC credited. Either way finalTargetAmount is all-in, with the Lumx fee and the partner fee inside it. Read the 0.22% and the 10 bps plus 1 USDC as the shape of the arithmetic in a docs sample, not as a Lumx price. Amounts are decimal strings, such as "59255.50" for BRL and "10000.000000" for USDC in the sample, so parse them with a decimal type and never a float.
The lock duration is a choice. The timelock field takes 30s, 1m or 5m, the default is 30 seconds, and the docs state that the 30-second lock has no fee while longer durations include a fee to cover volatility risk.
The three lock durations and where each one fits.
| Fee for the lock, per the docs | Where it fits |
| None | A confirm button the user presses within seconds |
| Included | A confirm screen with a review step |
| Included | A quote a person must read or forward before approving |
A locked request also accepts a targetAmount instead of a sourceAmount, for the case where the recipient must get an exact figure. A floating request does not, and COP is accepted too, including a target-amount lock for USDC to COP. A partnerFeeId sent with a locked request becomes part of the quote, so the number you show is the number that settles. How stablecoin payout pricing works explains how the spread and the fees add up around a quote.
How does a quote expire, and what do you do when it does?
A quote expires when the clock passes expiresAt. On Lumx that field is an absolute UTC timestamp, not a duration and not epoch milliseconds, and there is no separate TTL field: the window is the timelock you asked for. Build the countdown from expiresAt, not from a constant, and disable the button a second or two early.
On an on-ramp the same expiry also bounds the payment. The transactions docs say a locked on-ramp expires when its quote does, while a floating on-ramp over Pix or SPEI has one day and other rails seven. A payer who has to open a banking app needs more than 30 seconds, which is the case for the 5-minute option. A locked rate settles at the quoted price and an expired one is refused, so a re-lock after expiry is a new quote at the then-current price. Why windows are short is in how long a payment quote lasts.
Once expired, a quote is dead. Executing against it returns EXCHANGE_RATE_EXPIRED (422) and nothing moves, and a second use of a spent quote returns EXCHANGE_RATE_ALREADY_USED (422). Match on code, not on message. Give the expired-quote path its own state in the interface, because it is a normal event. Request a new locked rate, which always comes back with a new exchangeRateId, show the new amount, and treat it as a new quote. An unused locked quote has no side effect today, and a transaction held in an RFI (a compliance request for information) still settles at its quoted rate after the lock expires, because the fiat leg was already converted.
On Lumx, a deposit that arrives after a locked on-ramp expired is not processed and goes back to the sender on the same rail.
Execute once: a quote backs one payment
A locked rate backs one transaction. A second attempt returns EXCHANGE_RATE_ALREADY_USED, a second layer against double-sends. The execute calls are POST /transactions/on-ramp with customerId, exchangeRateId and purpose, and POST /transactions/off-ramp with destinationId, exchangeRateId and purpose. The amount, currency and fee come from the quote, so you do not send them again.
Both the quote call and the execute call accept an optional Idempotency-Key header with a UUID v4. One rule matters here: failed responses are cached too. If the execute call returned EXCHANGE_RATE_EXPIRED and you retry with the same key, you get the same error back. A re-quote, and any retry after an error response, takes a new key, because failed responses are cached. Reuse the key only when no response arrived. If you retry with the same key while the first call is still processing, you get IDEMPOTENCY_KEY_IN_FLIGHT (409), so wait and read GET /transactions instead of re-sending. The same key with a different body returns IDEMPOTENCY_KEY_CONFLICT (409).
Lumx answers an accepted execute with 202, and an off-ramp then runs TRANSFERRING_STABLECOIN, TRADING, TRANSFERRING_FIAT and SUCCESS, with webhooks on each change.
Retries and timeouts around a quote
Handle TOO_MANY_REQUESTS (429) with exponential backoff. The catalog marks TIMEOUT_ERROR, NETWORK_ERROR and SERVICE_UNAVAILABLE (all 503) as safe to retry, each with a new idempotency key once a response has come back. Size timeouts from your own measurements, because the budget that matters is the lock window. Illustrative example: with a 30-second lock, a 10-second timeout on the execute call and one retry, 20 seconds are gone, which leaves 10. If your measured tail latency does not fit, choose a longer lock for that screen and accept its fee.
When a locked quote is the wrong tool
A lock earns its place only when a person or a contract acts on the number.
Browsing and reporting. Calculators and rate sheets need the indicative rate, not a quote nobody executes.
Standing conversion. An autoconversion rule always converts at market and does not accept a locked quote. The
exchangeRateIdexists only on the single transaction.Price certainty for days. The longest lock is 5 minutes. A business that needs a rate for a week needs a forward contract from its bank or FX broker.
Unattended runs that execute late. A scheduled payout meets an expired lock with no human to retry. Fetch the lock right before the execute call, in the same job.
What a quote-then-execute payout looks like on Lumx
Take a marketplace that pays a seller in Brazil over Pix, with the exact reais shown before approval. 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.
The flow has four calls. The platform registers the seller's bank details as a destination once. At the confirm step, it sends POST /exchange-rates with type: LOCKED, rail: PIX, the USDC source amount, BRL as the target and a timelock of 1m, and it shows finalTargetAmount and a countdown. On approval, it sends POST /transactions/off-ramp with the destinationId, the exchangeRateId and a purpose, plus an Idempotency-Key it saved first. Then it waits for the webhooks and confirms the final state with GET /transactions/{id} before marking the payout paid. The marketplace pattern is covered end to end in marketplace payouts across Latin America, and the corridor mechanics in what a stablecoin corridor is.
Pricing around the quote is a monthly platform fee plus usage-based per-transaction fees, transparent FX on locked quotes, no pre-funding; no published plans. No pre-funding means no capital parked in settlement accounts before a payment, because conversion and settlement happen when the transaction starts. The pairs and rails you can quote are on the coverage page and in the global payments product overview.
Methodology and sources
Every Lumx field and error code above was read from the public docs on November 1, 2026: exchange rates, the get an exchange rate reference, the off-ramp reference, transactions, errors, idempotency and partner fees. The 0.22% figure is our arithmetic on the sample response, taken at a 1m lock. Principle 9 of the FX Global Code was read in the code's PDF on globalfxc.org on the same day.
Verified on November 1, 2026. Operational context, not legal, tax, or investment advice.
Cover photo: Lukas Tennie on Unsplash.
What are stablecoin API quotes?
Stablecoin API quotes are prices a payments API returns before a payment runs. An indicative quote shows the current rate without committing to it, and a binding quote fixes the rate, the fees and the amounts under an ID until it expires. On Lumx you request both from POST /exchange-rates: type FLOATING for the indicative rate and type LOCKED for the binding quote.
Can I quote the amount the recipient should receive?
Yes, on a binding quote. A locked rate on Lumx accepts either a source amount or a target amount, while a floating rate accepts only a source amount. Send the target amount when the recipient must get an exact figure, and show the finalTargetAmount from the response before the user confirms.
What error does an expired quote return?
An expired locked rate returns EXCHANGE_RATE_EXPIRED with HTTP status 422, and nothing moves. Request a new quote with a new idempotency key, show the new amount, and execute against the new ID. Lumx caches failed responses, so the same key would replay the same error. The expiry is the expiresAt timestamp in the quote response, in UTC.
Can a quote be used for more than one payment?
No. A locked rate backs one transaction, and a second attempt returns EXCHANGE_RATE_ALREADY_USED. Reuse the same Idempotency-Key header only when no response arrived, such as a client-side timeout, and Lumx then returns the cached response. After an error response, use a new key, because failed responses are cached too.
How do I choose between a 30-second and a 5-minute lock?
Choose the shortest lock that fits the screen, and use 5 minutes only when a person has to read or forward the quote before approving it. On Lumx the timelock field takes 30s, 1m or 5m, the default is 30 seconds, and the 30-second lock carries no additional fee while longer locks include a fee for volatility risk.





