Errors
Every error the API, the contract or the order flow can hand you, in one place, with what to do about it.
Error shape
API errors are JSON:
{ "error": "Order total is below the $1 minimum", "code": "BELOW_MIN_TOTAL" }The two refusals that ask you to wait — 429 RATE_LIMITED and 503 WARMING_UP — carry retryAfterSec and a Retry-After header instead of code. Order endpoints answer 400 { "error": "<message>" } without a code.
POST /api/quote
| HTTP | Code | Meaning | What to do |
|---|---|---|---|
| 400 | (no code) | The body is not valid JSON — rejected before validation, body { "error": "Unexpected token …" }. A missing body counts as {} and fails with BAD_FROM_CHAIN. | Send Content-Type: application/json and a JSON object. |
| 400 | BAD_FROM_CHAIN | chainId is not an enabled FROM chain, or the chain is paused. | Pick a chain with isTestnet: false, isEnabled: true and no isPaused in /api/chains. |
| 400 | BAD_TO_CHAIN | toChainId is not an enabled TO chain, or paused. | Same check on the TO side. |
| 400 | BAD_ADDRESS | fromToken, sender or toToken is empty or malformed for its chain. | EVM 0x…; Solana base58; TON any form. |
| 400 | BAD_FROM_TOKEN | fromToken is not listed for that FROM chain. | Use a token from /api/chains. |
| 400 | BAD_TO_TOKEN | toToken is not listed for that TO chain. | Use a token from /api/chains (0x0…0 = native). |
| 400 | BAD_ARRAYS / EMPTY / LENGTH_MISMATCH | recipients / toAmounts not arrays, empty, or different lengths. | One amount per recipient. |
| 400 | TOO_MANY | More than 10 000 recipients. | Split into several quotes. |
| 400 | BAD_RECIPIENT | A recipient fails the TO chain's validation. | Check the address family (EVM / Solana / TON / Bitcoin testnet). |
| 400 | BAD_TO_AMOUNT | An amount is not a positive base-unit integer string. | "5000000000000000000", not 5 or 5e18. |
| 400 | TO_TOO_SMALL | A TO amount is below the per-recipient minimum. | Raise that amount. |
| 400 | TO_TOO_PRECISE | A TO amount is off the token's precision grid (4 decimals; whole sats for BTC). | Round it. |
| 400 | FROM_ROUNDS_TO_ZERO | A TO amount is too small to price in the FROM token. | Raise it. |
| 400 | BELOW_SOL_RENT_MIN | Solana TO: below the 0.001 SOL rent floor. | Send at least 0.001 SOL per recipient. |
| 400 | BELOW_MIN_TOTAL | Order total under $1 (single) / $5 (multisend). | Raise the amounts. |
| 400 | NO_LIQUIDITY | Σ toAmounts exceeds what can be dispensed right now. | Check balance in /api/quotes; lower the amounts or wait. |
| 500 | NO_VAULT | The FROM chain has no pay target configured. | Pick another FROM chain; tell us. |
| 503 | NO_FROM_PRICE | The FROM token's price feed is missing or stale (400 with the same code if the feed reports a non-positive price). | Retry later or pay with another token. |
| 503 | NO_TO_PRICE | The TO token is unavailable (no rate or no liquidity data; 400 with the same code for a non-positive rate). | See available in /api/quotes; try later. |
| 503 | SIGNING_DISABLED | Quoting is paused by the operator. | Try later. |
Rate limits and availability
| HTTP | Body | Meaning | What to do |
|---|---|---|---|
| 429 | { "error": "RATE_LIMITED", "retryAfterSec": n } | Over 600 req/min per IP, or 10 quotes/min per IP, or 30 quotes/min per sender. | Sleep Retry-After seconds, then retry. Quote only when ready to pay. |
| 429 | (empty body) | Edge flood protection. | Back off harder. |
| 503 | { "error": "WARMING_UP", "retryAfterSec": 5, "waitingFor": [...] } | Backend just restarted; prices / balances still loading. | Retry after retryAfterSec. |
Details: Rate limits.
Other endpoints
| Endpoint | Error | Meaning |
|---|---|---|
GET /api/quotes | 400 Missing chainId or symbol | Pass both or neither. |
GET /api/quotes | 404 No quote found for symbol … | No such TO token on that chain. |
GET /api/prices | 400 Invalid address | Malformed token address. |
GET /api/prices/at | 400 chainId, address and ts are required · 400 Invalid ts · 404 No price recorded at or before ts | Fix the parameters; history starts when the token was listed. |
GET /api/order | 400 Provide order id or from_tx_hash | Pass one of the two. |
GET /api/order?id= | 200 null | Not indexed yet — keep polling. Ids are case-sensitive. |
GET /api/orders | 400 Incorrect address | The payer identity is malformed (EVM 0x…, Solana base58, TON). |
GET /api/orders | 400 status must be one of: … | Bad status filter. |
POST /api/orders/status | 400 ids must be an array and is required · 400 too many ids (max 500) | Fix the body. |
GET /api/orders/stream | 400 Incorrect address · 503 Too many subscribers | Fix the address · fall back to polling. |
EVM contract reverts
Decode a revert against these custom errors (the ABI is on the contract page):
| Error | Meaning | What to do |
|---|---|---|
Expired | Landed after deadline. | Request a fresh quote. |
MissingQuote | quoteId is zero. | Pass the quote's quoteId. |
AmountTooLow / ZeroAmount | Paid amount is zero or below the contract's per-token floor. | Pay fromTotal. |
TokenTransferMismatch | The token delivered less than amount (fee-on-transfer / rebasing). | Use a listed token. |
TokenNotAllowed | The token isn't accepted by the pay target. | Use a token from /api/chains. |
DirectPaymentNotAllowed | A plain transfer to the contract, not a pay call. | Call pay(...). |
EnforcedPause | The contract is paused. | Try later. |
Payments held for review
A payment that lands but does not match its quote — a different payer than sender, a different token, or an amount other than fromTotal — does not become an order and is not refunded automatically. It is flagged to the operator. If this happens to you, contact support on Telegram or Discord with the transaction hash. On EVM, passing the quote's fromTotal makes this impossible to hit by accident; on Solana / TON the transfer amount is yours to get right — see Payment rules.
The same applies to a second payment of a quote that was already honoured: it is held, and which of the two payments to return is decided by the operator. Never retry a payment whose transaction confirmed — track it instead.
Refund reasons
A payment that can't become an order gets a refund record (refund on GET /api/order, and a row in the payer's history). On TON look in the history (GET /api/orders?address=<UQ…>&view=batches): a refused payment did not consume its quote, so from_tx_hash=<quoteId> does not find it.
| Reason | Cause | Avoid it by |
|---|---|---|
expired | Paid after deadline (Solana / TON — on EVM the contract reverts). | Pay with plenty of the 180 s left; on TON set validUntil = deadline. |
unknown_quote | The memo / comment carried a quoteId the backend never issued. | Put the quoteId from the quote response verbatim in the memo / comment. |
unattributed | No readable quoteId at all in a transfer to the treasury (Solana / TON), above a small dust threshold. | Always send the memo / comment; never transfer to the treasury without a quote. |
ambiguous_memo | Several different quoteIds in one Solana transaction. | One quote per transaction. |
bad_signature | The stored quote's signature did not verify against a trusted signer key (operator-side). | Nothing to avoid; the payment comes back. |
failed_order | Some recipients of a batch failed. | Nothing to avoid; their slice comes back (for a partial batch, capped so the delivered part still cost at least $1). |
Refunds are sent by the operator; the record shows refund_pending until the return transaction is out, then refunded with refundTxHash.