Exchange API (Beta)
POST /exchange carries every signed write: orders, cancels, leverage and margin changes, agent authorization, fund movements, and builder-code actions. action.type selects the operation; each one has its own page with a Try it panel that signs and sends it on testnet.
How to sign and wrap an action is covered once, in Signing. The TypeScript SDK does it for you.
Actions
Trading actions are signed by an agent key, authorized once with approveAgent. Fund movements and authorizations are signed by the account key itself.
| Action | Signed by | What it does |
|---|---|---|
| Account | ||
approveAgent | Account key | Authorize — or revoke — an agent key |
tokenSend | Account key | Move tokens between sub-accounts, to another user, or to a TxRelay peg |
withdraw3 | Account key | Withdraw USDC to Arbitrum via Bridge2 |
| Orders | ||
order | Agent key | Place up to 50 orders in one action |
cancel | Agent key | Cancel up to 200 orders by order id |
cancelByCloid | Agent key | Cancel up to 200 orders by client order id |
modify2 | Agent key | Amend a resting order's price and size in place, keeping its order id and queue position |
outcomeOperate | Agent key | Split collateral into YES/NO shares, or merge a complete set back |
| Margin (perpetuals) | ||
updatePositionMode | Agent key | Switch the account between one-way and hedge mode |
updateLeverage | Agent key | Set leverage for one instrument |
updateMarginMode | Agent key | Switch an instrument between cross and isolated margin |
updateIsolatedMargin | Agent key | Add or remove margin on an isolated position |
| Builder codes | ||
approveBuilderFee | Account key | Approve or update a builder's maximum fee rate |
revokeBuilderFee | Account key | Remove a builder-fee approval |
claimBuilderRewards | Agent key | Claim the builder's unclaimed rewards |
Builder-fee actions need protocol version 47 or later. TWAP, vault, and governance actions are not documented yet; cross-chain deposit addresses come from the TxRelay API, not /exchange.
Outcome markets
Outcome orders use the same order, cancel, cancelByCloid, and modify2 actions, with the market's instrument id as the asset. An Outcome order can be accepted asynchronously: an ok response means it was queued, not matched. A queued order that fails is pushed on outcomeOrderUpdate (successes are not); the matching lifecycle is on outcomeOrderUpdates.
Market lifecycle actions
Creating markets, pushing oracle prices, and settling markets go through outcomeDeploy. It is permissioned — only registered outcome deployers and platform hot keys can sign it, and oracle pushes must also come from the instrument's oracle_updater or the exchange's configured oracle addresses — so it is not part of this reference. Integrations trade existing markets and read them through outcomeMeta; the protocol model is in TIP 3.
Attach a builder to an order
Once the user has approved a builder with approveBuilderFee, an order action can carry one builder object:
{
"type": "order",
"grouping": "na",
"orders": [
{ "a": 1, "b": true, "p": "50000.0", "s": "0.01", "r": false, "t": { "limit": { "tif": "Gtc" } } }
],
"builder": { "b": "0x2e4D75df2eE2653EA7a113cC1Fa36D45E923061F", "f": 10 }
}| Field | Type | Description |
|---|---|---|
builder.b | address | Builder wallet address |
builder.f | uint32 | Fee in tenths of a basis point: rate = f / 100,000; f = 10 is 1 bp (0.01%) |
The order signature covers builder. The fee applies to perpetual and Outcome markets, on top of exchange fees, and may exceed neither the user's approval nor the market cap: f <= 100 (0.1%) for perpetuals, f <= 1000 (1%) for Outcome. If the builder is unapproved, the market is unsupported, or either cap is exceeded, the fee is dropped and the order still goes through.
Fees accrue only while the builder holds at least 100 USDC in its perpetual account; builderState reports that balance, eligibility, and accrued rewards. See Builder Code for the end-to-end flow.
Response
Every action returns its response type and a single status, however many orders or cancels it carries:
{
"status": "ok",
"code": 0,
"data": {
"type": "PlaceOrder",
"data": { "statuses": ["success"] }
}
}"success"means the transaction was accepted by consensus; it has not executed yet. Send"confirm": "executed"next toactionin the request body to wait for the execution result instead: the reply then carries"confirm": "executed", a failed execution comes back as{ "error": "<reason>" }instatuses, and a timeout returns HTTP 202{ "status": "pending", "txHash": … }.- The response never says whether an order rested, filled, or was cancelled. Read that from
orderUpdatesorfrontendOpenOrders. data.typeis the action's internal name and may differ from the request —orderreturnsPlaceOrder;cancelandcancelByCloidboth returnCancelOrder.- A rejected request returns
"status": "err"with a non-zerocodeand the reason inmessage— for example{ "status": "err", "code": 10000, "message": "Price precision does not match the tick size: 0.1" }. Some gateway rejections, such as rate limits, put the reason inresponseinstead. - An Outcome order can instead be acknowledged asynchronously with
{ "status": "ok", "traceId": "…" }— see Outcome markets.