order
Places one or more orders. Every order in the batch is signed together and applied in array order.
orderPlace one or more orders (limit, market, trigger, TP/SL).Agent-signed
How the batch is interpreted as a group.
"na""normalTpsl""positionTpsl""normalTrigger""reversePosition""closeAll"Order bodies; at most 50 per action.
Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. Long form: `asset`.
`true` to buy, `false` to sell. Long form: `isBuy`.
Limit price. The literal `"bbo"` prices at the current best bid/offer. Long form: `limitPx`.
Size in base units. Long form: `sz`.
Either a `limit` or a `trigger` body. Long form: `orderType`.
Client order id, used later by `cancelByCloid`. Long form: `cloid`.
Required when `tif` is `Gtd`. Unix milliseconds, and must be in the future.
Margin mode. Long form: `marginMode`. Optional: when omitted the node routes the order to the account's current margin mode for this market (its saved position profile, `cross` for an account that has never set one) — so the same body can land on a different mode after an `updateMarginMode`. API clients should send `m` explicitly. The margin mode is part of the position key, so it also selects which position the order opens into or reduces.
"cross""isolated"Reduce-only. Defaults to `false`. Long form: `reduceOnly`.
Must be `order`.
"order"Optional builder code. The order signature covers this object.
Builder wallet address.
Fee in tenths of a basis point: `rate = f / 100,000`, so `f = 10` is 1 bp (`0.01%`).
curl -X POST https://rpc.txflow-testnet.xyz/exchange \
-H "Content-Type: application/json" \
-H "x-api-version: 2026-08-16" \
-d '{
"action": {
"type": "order",
"grouping": "na",
"orders": [
{
"a": 1,
"b": true,
"p": "50000.0",
"s": "0.01",
"r": false,
"t": {
"limit": {
"tif": "Gtc"
}
}
}
]
},
"signature": {
"r": "0x<r>",
"s": "0x<s>",
"v": 27
},
"nonce": 1757992743994,
"vaultAddress": null,
"signatureChainId": null
}'`0` on success.
The action's internal name, which does not always match the request `type`.
"PlaceOrder"A rejected request returns `err` with a non-zero `code` and the reason in `message` (or `response`).
"ok"{
"status": "ok",
"code": 0,
"data": {
"type": "PlaceOrder",
"data": {
"statuses": [
"success"
]
}
}
}Outcome orders
For an Outcome market, a is its instrument_id. YES is the long leg and NO
the short leg; b and r together encode the leg and direction:
| Intent | b | r |
|---|---|---|
| Buy YES | true | false |
| Buy NO | false | false |
| Sell YES | false | true |
| Sell NO | true | true |
p is the price of the traded leg, between 0 and 1: buying NO at 0.30 sends
b: false, r: false, p: "0.30". Outcome orders use grouping: "na" and do not
support trigger/TP-SL order types. Time-in-force must be Gtc, Gtd, or Alo;
an Ioc order is rejected with Outcome limit orders only support GTC, GTD, and ALO orders, reported asynchronously on
outcomeOrderUpdate.
The initial HTTP response can be an asynchronous acceptance
({"status":"ok","traceId":…}) rather than a fill. If the queued order fails,
outcomeOrderUpdate pushes the reason under
that traceId; successes are not pushed. Follow the matching lifecycle on
outcomeOrderUpdates or read
outcomeFrontendOpenOrders.
Order bodies use abbreviated keys. The long forms noted above are accepted on input, but the abbreviated keys are what the signature covers. The optional builder object is documented under Attach a builder to an order.
Order body
Instrument's external id — the `index` value from `perpMeta`; Outcome markets use the `instrument_id`. Long form: `asset`.
`true` to buy, `false` to sell. Long form: `isBuy`.
Limit price. The literal `"bbo"` prices at the current best bid/offer. Long form: `limitPx`.
Size in base units. Long form: `sz`.
Either a `limit` or a `trigger` body. Long form: `orderType`.
Client order id, used later by `cancelByCloid`. Long form: `cloid`.
Required when `tif` is `Gtd`. Unix milliseconds, and must be in the future.
Margin mode. Long form: `marginMode`. Optional: when omitted the node routes the order to the account's current margin mode for this market (its saved position profile, `cross` for an account that has never set one) — so the same body can land on a different mode after an `updateMarginMode`. API clients should send `m` explicitly. The margin mode is part of the position key, so it also selects which position the order opens into or reduces.
"cross""isolated"Reduce-only. Defaults to `false`. Long form: `reduceOnly`.
Price, size, and client order id
| Rule | Rejected with |
|---|---|
p is a multiple of the instrument's priceTick from perpMeta, with no more decimals than the tick | Price precision does not match the tick size: <tick> / Tick size does not match |
s has at most szDecimals decimals and lies between the minimum size and maxLimitOrderSize / maxMarketOrderSize | Minimum order amount is <x> <BASE> / Maximum limit order amount is … |
| A limit price stays inside the oracle price band | Order buy price is not within the price limit (Minimum price: …; Maximum price: …) |
c is any string up to 64 bytes, unique among your open orders. A 0x prefix is reserved for liquidity-provider accounts, on every market | cloid length exceeds maximum / Duplicate client order ID <cloid> / Invalid cloid format |
Order type
A limit order carries a time-in-force:
{ "limit": { "tif": "Gtc" } }tif | Behavior |
|---|---|
Gtc | Good-til-canceled; rests on the book |
Ioc | Immediate-or-cancel; unfilled remainder is canceled |
Alo | Add-liquidity-only (post-only); rejected if it would take |
Gtd | Good-til-date; requires expiresAt |
FrontendMarket | Treated as a market order |
A trigger order carries a trigger price instead:
{
"trigger": {
"isMarket": true,
"triggerPx": "48000.0",
"tpsl": "sl"
}
}tpsl is tp (take-profit) or sl (stop-loss). Trigger orders are only accepted under a normalTpsl, positionTpsl, or normalTrigger grouping; sending one with grouping: "na" is rejected with Trigger orders are not supported for grouping na.
Triggered is not filled
A TP/SL that reaches its trigger price is fired, not guaranteed to execute. Two separate things happen:
- Trigger — the protection order's trigger state becomes
Triggered, and in a TP/SL pair the other side is canceled at the same moment (Canceledtrigger state). This is OCO: it happens on trigger, not on fill. - Execution — the fired order is then submitted to the matcher like any other order, and can be fully filled, partially filled, canceled, or rejected.
A fired order that does not fill does not revert to a pending protection order — it is in a terminal state and the position is left unprotected. Common reasons the fired leg does not fill:
| Outcome | Why |
|---|---|
| Canceled with no or partial fill | A fired market order carries the oracle price-protection band; the remainder past the band, or past the book's available liquidity, is canceled |
| Rejected | The limit price falls outside the price band or off the tick, or the instrument is halted / delisted, or the order is over the open-interest cap |
| Skipped entirely | The underlying position is already flat (closed, liquidated, or closed by the other side of the pair) when the trigger fires |
Because the sibling was already canceled in step 1, a failed fill leaves neither protection order live. Re-arm the protection yourself after confirming the outcome.
Confirm the result rather than assuming it: read the pair back with
tpslOrders (live) and
historicalTpslOrders (terminal, carrying
cancelSource / cancelReason), check the resulting order on
historicalOrders, and follow
orderUpdates for the live transitions.
Next steps
Read resting orders back with frontendOpenOrders. To reprice or resize a resting order, use modify2.