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.

1

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

2

Error Format

Errors come back in one of two shapes. Business and authentication errors use an RFC 7807 problem object:

Problem object

A request with missing or malformed fields uses a validation envelope that names each field:

Validation error

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.


3

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.


4

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


5

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


6

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

7

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.


8

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


Share on
Did you find this page useful?

Join our Discord