Error Handling Patterns
Build safe, predictable fallback paths when trades don’t go as expected.
Philosophy
Trade execution can fail — due to market changes, balance issues, or API constraints. The goal is not to avoid all errors, but to handle them gracefully, log them clearly, and fail in ways that protect both your user and your business.
Common Error Types
error case | status code | example message | what it means |
|---|---|---|---|
Quote expired | 422 | Quote <id> has expired | The quote was valid, but not used before its expires_at |
Invalid asset pair | 400 | Invalid asset pair | Use a supported pair (USDT, USDC, BTC, NGN); naira trades need NGN on one side |
NGN not enabled | 403 | feature-not-enabled | Naira trading is not switched on for your account |
Quote required (NGN) | 400 | Quote required | NGN orders must include a quote_id — the rate must be locked first |
Invalid or zero amount | 400 | Missing or invalid amount | Amount was not supplied or below threshold |
Insufficient balance | 422 | Insufficient balance | Wallet doesn’t have enough funds to complete trade |
Execution error | 500 | Trade execution failed | Internal error from liquidity routing or engine timeout |
Error Format
Errors come back in one of two shapes. Business and authentication errors use an RFC 7807 problem object:
A request with missing or malformed fields uses a validation envelope that names each field:
Branch on the HTTP status and code, not on the message text. Log the request_id (also sent as the x-correlation-id response header) and quote it when you contact support.
Retry Logic
Idempotency is your safety net. If your system doesn’t receive a response due to timeout or network drop:
On a crypto pair, look the order up first with GET /api/trading/orders/<your-reference>. If it is there, the first attempt landed — use its status. Resend only if it is not there, because a repeated reference is not rejected on crypto pairs
On an NGN pair, resend the request with the same reference. You get the original order back and nothing is executed twice
Never generate a new reference for a retry: a new reference is a new trade
Store the order id from every response; it is the only way to fetch an NGN order later.
Quote Expiry Handling
If you’re using quote_id, check its current state with Get Quote by ID:
You must execute the trade before the expires_at timestamp
If the quote is expired:
Return a soft error to the user:
"This price has expired. Tap refresh to get a new quote."
Immediately request a fresh quote from /api/trading/quotes
Monitoring Failures
To surface issues before they impact users — Get Orders lets you sweep for failures independently of webhook delivery:
Track all trade.failed webhook events
Alert on failure spikes (e.g., 5+ failures within 1 min)
Log the reason and context (wallet ID, amount, quote ID, chain, user ID)
Include the reference in all logs so issues can be traced across systems
Recovery Paths
Re-quote with Quotes, confirm a trade's real state with Get Order By ID before retrying, and use Cancel Order where an order is still cancellable.
failure case | suggested action |
|---|---|
quote expired | Show new quote immediately, don’t auto-retry without user confirmation |
insufficient balance | Retry later or trigger background top-up, then queue trade |
timeout or 503 on an order | Fetch the existing order by reference and use its status. Treat as success if confirmed, log conflict if amounts differ |
execution error | Retry with backoff, optionally alert ops for trades over threshold |
Best Logging Practices
Log every failed trade attempt with:
reference
quoteId (if used)
input amount, source/destination asset failure code or message
createdAt timestamp user or account context
Use this to power dashboards, postmortems, and support flows.
Protecting Users
In all failure cases:
Do not deduct balances until trade.completed is confirmed
Clearly communicate when a trade failed and why
Never “freeze” funds unless you’ve locked execution in your internal system
Offer retry or refresh options in the UI, not silent fails