Trade API
Get a quote, place an order from the org's treasury wallets, and track it to a fill — with the same policy checks and approval chains as the in-app desk.
Requires a key or token scoped to Trade. Orders execute from your org's treasury wallets — there's no separate trading balance to fund, and no custodial pot to reconcile against.
The public API places MARKET orders
Limit and exchange orders exist in the trade desk's data model — you'll see them in reads and the event stream if a teammate places one from the in-app desk — but order creation through this API is MARKET-only today: execute now, at the best available route. Use the desk UI for limit and exchange execution.
Quotes & orders
Get a quote
const tokens = await client.listTradeTokens('ethereum'); // tradable tokens for the chain
const quote = await client.getTradeQuotes({
chain: 'ethereum',
from: 'ETH',
to: 'USDC',
amount: '2.5',
});
// -> { amountIn, amountInUsd?, bestQuote: { dex, amountOut, priceImpact, amountOutUsd?, ... } }Create and submit the order
Two-step, like every write on the platform: create a draft, then submit it into the policy/approval pipeline.
const order = await client.createTradeOrder({
walletId,
chain: 'ethereum',
tokenInSymbol: 'ETH',
tokenOutSymbol: 'USDC',
tokenIn: '0x…', // token addresses, from listTradeTokens()
tokenOut: '0x…',
amountIn: '2.5',
amountInRaw: '2500000000000000000',
slippageBps: 50,
});
await client.submitTradeOrder(order.id);Bitcoin routes via THORChain
BTC pairs have no EVM liquidity, so BTC-involving orders route through THORChain automatically — same API shape. A BTC sell produces a deposit transaction your org broadcasts from its own wallet; the platform never holds a Bitcoin key.
Track a fill
Simplest to build — ask the order for its own status.
const order = await client.getTradeOrder(orderId);
// order.status -> DRAFT | PENDING_APPROVAL | APPROVED | BROADCAST | CONFIRMED | FAILED | REJECTED
const { items } = await client.listTradeOrders({
status: 'PENDING_APPROVAL',
limit: 50,
});A durable, server-to-server record — the right default for reconciliation.
Subscribe to the shared order lifecycle on the trade domain:
Order.Submitted → Order.Approved (if policy required sign-off) → Order.Broadcast →
Order.Confirmed (or Order.Failed / Order.Rejected). Registration, payload shape, and
signature verification are in the webhooks guide.
LIMIT orders don't publish order.confirmed yet
order.confirmed fires for MARKET and EXCHANGE orders. A LIMIT order (routed through CoW
Protocol, placed from the in-app desk) won't — poll getTradeOrder/listTradeOrders for its
fill status instead if your org uses them.
The same events as a live stream — for a dashboard that wants push without a receiver.
Same subscription and scoping rules as webhooks — see the WebSockets guide for connecting and message shapes. Live-only: an event published while you're disconnected is missed, so pair this with the audit/order-history reads above rather than relying on it as your only record.
The full create → approve → sign → broadcast sequence (shared by every vertical) is in Approvals & signing.
Alerts & recurring orders
const alerts = await client.listTradeAlerts();
// -> [{ id, symbol, conditions, combinator: 'ALL' | 'ANY', enabled, wasMet, ... }]
// Recurring orders — the standard shape for DCA
await client.createRecurringTradeOrder({
walletId,
chain: 'ethereum',
tokenInSymbol: 'USDC',
tokenOutSymbol: 'ETH',
amountIn: '5000',
interval: 'WEEKLY',
});Price alerts are per-user and multi-condition (combinator: 'ALL' requires every condition,
'ANY' requires one) — the exact per-condition shape varies by type, so see the
API reference for POST /trade/alerts before building against it. Each
recurring order run re-enters the same policy/approval pipeline as a manual order, and
Trade.AlertTriggered fires over webhooks/WebSockets when an alert condition is met. Policy
rules for pairs, order types, and max slippage apply throughout — see the
permissions reference for the grants involved.