Swapping currencies
End-to-end walkthrough of executing a swap from quote to webhook confirmation.
This guide takes you through every step of swapping currencies, from quoting to receiving the completion webhook.
Prerequisites
- An API key registered with a public Ed25519 key — see Authentication
- A webhook URL registered — see Webhook setup
- A non-zero balance in the
currency_inyou want to swap from
Flow
quote.get → (show user) → transaction.initiateSwap → webhook: transaction.updated (status COMPLETED)1. Fetch a quote
POST /quote.get
Content-Type: application/json
{
"currency_in": { "value": "100000000", "code": "USDC", "decimals": 6, "chain": "ethereum" },
"currency_out": { "value": "0", "code": "AUD", "decimals": 2 }
}(On a quote, currency_out.value is just a placeholder — "0" asks for the output amount.)
{
"data": {
"bid": { "value": "154700", "code": "AUD", "decimals": 2 },
"ask": { "value": "154700", "code": "AUD", "decimals": 2 },
"direction": "ask",
"fees": [
{ "type": "percentage", "value": { "value": "500", "code": "AUD", "decimals": 2 }, "label": "Service fee", "rate": 0.003 }
],
"stablecoin": null
}
}If you're showing the rate to a user, refresh it every 5–10 seconds while they're on the confirmation screen so the displayed estimate doesn't drift.
2. Execute the swap
POST /transaction.initiateSwap
Content-Type: application/json
{
"currency_in": { "value": "100000000", "code": "USDC", "decimals": 6, "chain": "ethereum" },
"currency_out": { "value": "154700", "code": "AUD", "decimals": 2 },
"external_accounts": { "from": null, "to": null }
}currency_in.valueis the input in the smallest unit (here,100 USDC=100000000atdecimals6).currency_out.valueis the output you expect — use theaskfrom the quote (154700here). Both sides must be non-zero; the engine validates the implied rate against the live market within a small tolerance and re-quotes at execution, so a slightly stale estimate is fine.
Response — the new transaction id:
{ "data": "08a4d98b-07b1-4948-a6e7-7c0ee556d564" }Fetch full details (status, amounts) via transaction.get with that id.
3. Wait for the webhook
When the swap settles, your webhook URL receives a delivery with the transaction in its terminal state. Verify the signature (Webhooks) and update your records.
4. Confirm
account.balance.getMany will now reflect the decreased USDC and increased AUD balance.
Error cases
Submission-time failures return { "error": { "code", "message" } } with code BAD_REQUEST:
- Unsupported currency —
currency=<CODE> is not a payable currency/is not a receivable currency. - Unsupported pair — direct native-crypto ↔ native-crypto swaps are rejected:
Direct <A> → <B> swaps are not supported. Bridge via USDC or USDT. - Below minimum — per-currency minimum trade sizes guard against dust trades:
Swaps from <CODE> must be at least <min> <CODE>. - Stale rate — if the implied rate in your request drifts too far from the live market, the quote validation rejects with the reason (e.g. asking you to refresh the quote).
- Invalid input — zero/negative amounts, identical
currency_in/currency_out, a missingchainon crypto, or unknown fields are all rejected by schema validation.
A swap that is accepted can still fail afterwards (e.g. insufficient balance at execution) — that surfaces as the transaction reaching status: "FAILED", not as a submission error.