Quotes & Pricing
How currency pairs are priced, and when you need a quote.
POST /v1/customers/{customerId}/quotes
This endpoint has one job, which is to price an FX pair. That price becomes the LP_EXECUTION leg of whatever consumes it.
What a quote becomes
A quote has no transaction type of its own. What you do with it decides that.
| Consumed by | Becomes |
|---|---|
POST /v1/customers/{customerId}/conversions | A CONVERSION. Both sides are the customer's own balances. |
POST /v1/customers/{customerId}/payouts/fiat | A PAY_OUT, with the FX running as a leg inside it. Value leaves the customer. |
| Nothing | A pay-in never consumes a quote. The deposit intent's indicativeRate is its price. |
There is no buy side or sell side. A quote is a directed pair with the amount fixed on one end.
Setting the amount
Set fromAmount to fix what's debited, or toAmount to fix what lands. Sending both is an error rather than a way to constrain both ends.
Use toAmount when the recipient has to receive an exact figure, such as settling an invoice. Use fromAmount when the debit is the fixed side, such as sweeping a balance.
The two conditional fields
chain and rail mirror each other. Each one is governed by one half of the pair.
| Pair shape | chain | rail |
|---|---|---|
| stablecoin to fiat, or fiat to stablecoin | required | required |
| fiat to fiat | omit | required |
| stablecoin to stablecoin | required | omit |
Supplying a field the pair shape forbids fails the request with pair_shape_violation rather than being ignored.
Why the rail is part of the price
(rail, currency) names the route being priced.
Rails into the same corridor don't cost the same. A quote that didn't know the rail could only return an average, and the difference would land in Alfred's margin instead of the customer's rate. (ACH, BOB) and (ACH, CLP) price separately. (CHATS, USD) means US dollars delivered in Hong Kong, which prices differently from US dollars delivered in the US.
So a quote is priced for one rail and can't be moved to another. A SPEI quote can't fund a PIX payout, and trying gets you rail_mismatch.
When you don't need one
Two cases come up regularly.
The first is a same-currency movement. When the funding balance and the destination share a currency there is no FX leg. A USD Corporate Account paying out to a USD account over ACH is a plain transfer, and so is an MXNa balance paying out to MXN over SPEI.
The second is par redemption. USDC or USDT going to USD is priced from the fee table at par. You may pass a quote to lock fees, but it is never required.
Alfred never introduces an artificial conversion to make a transaction fit a shape.
The route registry tells you which case applies. GET /v1/countries/{country} carries a quote object per route, broken down by pair shape.
Fees
Provider fees, network fees and Alfred's fee come back as separate itemized entries. They are never folded into the rate.
Rounding residuals post to the reconciliation account. They are never absorbed into a customer's conversion.
Your rates and fees are commercial terms that you agree with us separately. Read what a given movement costs from the quote instead of from a stored rate card. The quote is priced for the specific pair and rail you asked for.
Checking the numbers yourself
The quote response carries everything you need to verify the arithmetic rather than take it on trust.
| Field | What it is |
|---|---|
fromAmount | What gets debited |
toAmount | What lands |
rate | The FX rate applied |
fees[] | Each fee as its own entry, with a type, an amount and a currency |
rateLockedAt | When the rate was fixed |
expiresAt | Execute before this. Typically around two minutes |
The type on each fee is one of provider, network, alfred, conversion or payout. Nothing is aggregated, so you can attribute every charge.
The currency on a fee tells you which side it lands on. A fee denominated in fromCurrency comes off the source. A fee denominated in toCurrency comes off what arrives. Group the fees by currency and you can reconstruct both ends.
Store the whole quote rather than only the rate. When you reconcile against a statement later, rateLockedAt and the itemized fees are what let you explain a figure that someone is querying.
Confirm against a real sandbox quote whether a source-side fee is deducted before the rate is applied or after. The response gives you
fromAmount,toAmount,rateand every fee, so one live quote settles it for your own reconciliation.
Executing
GET /v1/quotes/{quoteId}
This re-reads the rate, the fees and the expiry. Use it to recover if you lost the create response.
Execution has to land inside expiresAt. An expired quote gets quote_expired, and the fix is to request a new quote. A quote that has already been used gets quote_consumed.
One more check happens before anything is locked. A pair whose route isn't LIVE fails with route_unsupported.
Updated 5 days ago

