Where a quote sits
Price it
qut_… id,
the destination amount, the rate and every fee line.Commit to it
qut_ id and a recipient, and turns the price into an intent.Send it
The four shapes
What a quote is follows entirely from the two currencies. Endl treatsUSDC,
USDT and RLUSD as stablecoins and everything else as fiat.
USDC → USDC may therefore be refused on USDC → EUR.
That is the gate working, not a bug.What every quote must carry
Five things are mandatory on every request, whatever the shape.RLUSD is a special case on the source leg. It counts as a stablecoin when
the shape is classified, and an RLUSD source is rewritten internally to USDC
on arbitrum for routing, with an RLUSD conversion fee applied to the result.
The deposit-rail rule above, though, only names USDC and USDT. Confirm
RLUSD-as-source with Endl before building on it; RLUSD as a destination is
unambiguous.Forward and reverse quotes
Sendsource.amount to price forwards — “I am sending 1,000, what lands?”. Send
destination.amount to price backwards — “the recipient must receive exactly
50,000, what do I send?”. Send one, never both, never neither.
Rounding
Amounts are normalised to two decimal places before anything is priced (half-up), and rounded again on the way out — deliberately in your favour, never Endl’s:source.amountis rounded up, because it is money coming in.destination.amountis rounded down, because it is money going out.
Chains, and why they are spelled differently here
Whenever a leg is a stablecoin, the chain matters. On a one-sided shape the chain on the crypto leg is required; on stablecoin → stablecoin it is optional per leg, but validated whenever supplied.Base and base both work
here. There is no quote chain for AvalancheC.
basesepolia, ethereumsepolia, arbitrumsepolia,
optimismsepolia, polygonamoy, tronnile or xrpledgertestnet, and each is
judged as the mainnet it mirrors. Production rejects all of them.Not every currency is on every chain
Two rules, applied to each leg independently:- USDT is only issued on
ethereumandtron. USDT on Base prices nowhere. - TRON carries only USDT. USDC on
tronis refused.
400 USDT is not supported on the base network (source.chain) — the
message names the leg, so you know which half to fix.
Amount limits
Four separate ceilings can reject the same amount. They are checked in this order, and the first one to fire is the one you are told about — the order is deliberate, so you get the limit you can actually act on rather than an internal one.Account-type limits
SWIFT's own floor
Structural ceilings
999,999.9999 or a
destination amount above 99,999,999.99. These are not commercial limits and
nothing is exempt from them.The payout rail's published maximum
- Stablecoin → stablecoin lifts the minimum but keeps the maximum. The 100 USD floor is a fiat-settlement rule and a crypto transfer can legitimately be smaller; the account ceiling still applies.
- AED is priced from its own fee configuration rather than the shared USD band, on either leg.
Routing you do not control
You choose rails; you do not choose providers. Endl picks the provider from the currencies and the rail, and the choice is invisible in the response — but it changes which KYC the customer needs. A quote routes to Paywho when either leg is a Paywho-supported currency on a Paywho payout rail, or when either leg isINR on BANK_TRANSFER, IMPS, UPI or
CRYPTO. Everything else routes to Bridge.
Paywho-routed and stablecoin-to-stablecoin quotes need Sumsub verification;
Bridge-routed quotes need the full Bridge check.
Pairs that are refused outright
Reading the quote back
feeCategoryTypeEnum — EXCHANGE_FEE, EXCHANGE_MARKUP_FEE,
OUTGOING_PROCESSING_FEE, GAS_FEE, PLATFORM_FEE, PARTNER_FEE, TDS_FEE and
others. Treat the set as open: display what you get, and do not switch
exhaustively on it.
source.amountToConvert and amountToConvertActual are display-only fields
describing what will actually be converted. Do not use them as inputs to anything.Fields that are quietly ignored
Three things you might send have no effect, and none of them is an error:partnerId— overwritten with whoever the API key authenticated as. You cannot file a quote under another partner.overrideGasFee— a merchant-portal feature, dropped on the v0 surface. You get the quote you asked for, priced with Endl’s own gas fee.- Any unknown property — ignored rather than rejected.
The ten minutes
expiresOn is set 600 seconds after generation, and expiry is enforced when you
use the quote, not when you read it.
Get quote reports expired as a boolean and will
happily return an expired quote — it is a record, not a reservation. Create
pre-transaction is where an expired quote is refused.
Do not generate quotes speculatively to warm a cache. Price when the customer is
ready to confirm, and re-price if they hesitate past the window.
Error handling
Quote failures are400 with the specific reason in errors[0], and the messages
are written to be shown to a user. The one quote-domain code is ERRQUO_1000
(404), for a quote that does not exist or belongs to another customer — the
same answer either way, so quote ids cannot be probed. Everything else uses the
shared envelope and the ERRCORE_ codes in
Errors and the response envelope.
Branch on errors[0].code, not on the message text.
Common pitfalls
400 'For USDC/USDT source currency, depositType must be CRYPTO_WALLET or CRYPTO_MANUAL_WALLET'
400 'For USDC/USDT source currency, depositType must be CRYPTO_WALLET or CRYPTO_MANUAL_WALLET'
depositRail: "CRYPTO". Those two rail names do not
exist in the v0 vocabulary.I passed my wallet's network and the quote rejected the chain
I passed my wallet's network and the quote rejected the chain
ArbitrumOne
is arbitrum to a quote; XrpLedger is xrp. See the mapping table above.400 'source.currency field is required' and I definitely sent it
400 'source.currency field is required' and I definitely sent it
source.amount × fxRate does not equal destination.amount
source.amount × fxRate does not equal destination.amount
fxRate is the pre-markup partner rate; the markup is a fee line, not
a rate adjustment. Reconcile with the amounts and feeList.My retry failed with 'Transaction already exists'
My retry failed with 'Transaction already exists'
Get quote returned 200 for a quote I know has expired
Get quote returned 200 for a quote I know has expired
expired: true. Expiry is enforced at
Create pre-transaction, not on read.A customer can quote USDC to USDC but gets 403 on USDC to EUR
A customer can quote USDC to USDC but gets 403 on USDC to EUR
SWIFT is refused on an account that definitely has it enabled
SWIFT is refused on an account that definitely has it enabled
accountType in the
body must say BUSINESS; having the rail enabled does not make an individual
eligible. It also has a 15,200 USD floor.A small stablecoin transfer was rejected for being under 100
A small stablecoin transfer was rejected for being under 100
I sent both source.amount and destination.amount
I sent both source.amount and destination.amount
Glossary
Api-Version: 2026-09.1. Rail availability, per-rail caps and
currency support are configuration and vary by customer and country — read them
from List rails and
Get recipient required fields
rather than hardcoding this page.