Orders

All order actions are signed by your trading key — see Authentication & Signing for the domain and encoding rules.

Submit orders

POST /orders

The endpoint is a batch: 1–20 independently signed legs per request, all for the same account_id.

1{
2 "account_id": "0x<wallet 40 hex>000000000000000000000000",
3 "orders": [
4 {
5 "market": "BTC",
6 "side": "BUY",
7 "order_type": "MARKET",
8 "size": "0.001",
9 "limit_price": "65129",
10 "time_in_force": "IOC",
11 "reduce_only": false,
12 "leverage": 10,
13 "client_order_id": "my-order-1",
14 "nonce": "1787067314951",
15 "signature": "0x…"
16 }
17 ]
18}

Order types

order_typeBehavior
MARKETAlways IOC. limit_price is the client-computed slippage bound (mark × (1 ± tolerance)) — you can never be filled beyond it; the unfilled remainder cancels. This is the default when order_type is omitted.
LIMITRests on the book at limit_price with time_in_force: "GTC" (or IOC to take-or-cancel).
TRIGGERA TP/SL order. Set the trigger object; omit time_in_force (derived from trigger.is_market). limit_price is the fire price.

Trigger orders

1{
2 "market": "BTC",
3 "side": "SELL",
4 "order_type": "TRIGGER",
5 "size": "0.001",
6 "limit_price": "60000",
7 "trigger": { "trigger_price": "60500", "is_market": true, "tpsl": "SL" },
8 "reduce_only": true,
9 "client_order_id": "my-sl-1",
10 "nonce": "1787067314952",
11 "signature": "0x…"
12}
  • trigger_price — the raw mark-price threshold that arms the fire (no slippage applied).
  • is_markettrue fires an IOC order at limit_price. Only is_market: true is supported today; trigger-limit (false) is rejected.
  • tpslTP or SL. The server rejects a trigger already past the mark on its fire side.
  • size: "0" on a reduce-only trigger means close the full position at fire time.

Signing an order

Typed data — Order(bytes32 account,string market,string side,string orderType,string size,string limitPrice,string timeInForce,bool reduceOnly,uint256 leverage,uint256 nonce,string triggerPrice,bool isMarket,string tpsl):

1const signature = await agent.signTypedData({
2 domain: { name: "Hyperflow Perps", version: "2", chainId: 999 },
3 types: {
4 Order: [
5 { name: "account", type: "bytes32" },
6 { name: "market", type: "string" },
7 { name: "side", type: "string" },
8 { name: "orderType", type: "string" },
9 { name: "size", type: "string" },
10 { name: "limitPrice", type: "string" },
11 { name: "timeInForce", type: "string" },
12 { name: "reduceOnly", type: "bool" },
13 { name: "leverage", type: "uint256" },
14 { name: "nonce", type: "uint256" },
15 { name: "triggerPrice", type: "string" },
16 { name: "isMarket", type: "bool" },
17 { name: "tpsl", type: "string" },
18 ],
19 },
20 primaryType: "Order",
21 message: {
22 account: accountId, // bytes32
23 market: "BTC",
24 side: "BUY",
25 orderType: "MARKET",
26 size: "0.001",
27 limitPrice: "65129",
28 timeInForce: "IOC",
29 reduceOnly: false,
30 leverage: 10n,
31 nonce: 1787067314951n,
32 triggerPrice: "0", // non-trigger orders still sign the trigger tail
33 isMarket: false,
34 tpsl: "",
35 },
36});

Non-trigger orders must still sign the trigger tail with triggerPrice: "0", isMarket: false, tpsl: "". A market-fire trigger signs timeInForce: "IOC" even though the JSON body omits time_in_force. The signed strings must byte-match the body fields.

Response

data is an array index-aligned with your legs; top-level success is true only if every leg was SUBMITTED:

1{
2 "success": true,
3 "data": [
4 { "order_id": "", "client_order_id": "my-order-1", "status": "SUBMITTED" }
5 ],
6 "timestamp": 1787067314999
7}

Legs are independent — one REJECTED (with reason) never cancels the others. SUBMITTED means validated and queued; the order then advances asynchronously.

Order lifecycle

Poll GET /orders/{orderId}:

SUBMITTED → ACCEPTED → EXECUTING → FILLED
→ PARTIALLY_FILLED_CANCELLED
→ CANCELLED
→ REJECTED
→ FAILED (terminal: could not be routed to the venue)
PENDING_MODIFY (the NEW order created by a modify, held until the
modified order terminates)

A TRIGGER order stays SUBMITTED while armed and moves to ACCEPTED only when its trigger fires — long-lived SUBMITTED triggers are normal.

GET /orders/{orderId} can briefly return 404 right after submit — orders persist asynchronously. Retry with backoff.

Cancel an order

POST /orders/{orderId}/cancel

Signed as Cancel(address account,string orderId,string orderType,uint256 nonce) — note account here is your 20-byte wallet address, not the 32-byte account id:

1{
2 "account": "0xYourWallet",
3 "order_type": "LIMIT",
4 "nonce": "1787067314953",
5 "signature": "0x…"
6}

order_type is LIMIT (resting order) or TRIGGER (armed trigger); it is part of the signed payload. Returns status: SUBMITTED | REJECTED; 403 when the signature does not authorize the account.

Cancel in batch

POST /orders/cancel-batch
1{
2 "account": "0xYourWallet",
3 "cancels": [
4 { "order_id": "", "order_type": "LIMIT", "nonce": "1787067314954", "signature": "0x…" }
5 ]
6}

1–20 legs, each signing its own Cancel. Per-leg status is SUBMITTED or FAILED (with reason); legs are independent.

Modify an order

POST /orders/{orderId}/modify

Signed as Modify(bytes32 account,string orderId,string clientOrderId,string limitPrice,string size,string triggerPrice,uint256 nonce):

1{
2 "account_id": "0x<wallet 40 hex>000000000000000000000000",
3 "client_order_id": "my-order-1b",
4 "limit_price": "64900",
5 "size": "0.002",
6 "trigger_price": "0",
7 "nonce": "1787067314955",
8 "signature": "0x…"
9}
  • client_order_id must be new — reusing the modified order’s id is rejected.
  • Only price, size, and trigger price can change; market, side, time_in_force, reduce_only, leverage, and order_type are inherited.
  • For a limit order send trigger_price: "0"; for a trigger order limit_price is the fire price.
  • size: "0" (full-position close) is allowed only on TP/SL triggers.
  • Success returns the new order’s order_id. If a trigger modify races the trigger firing (or an open-order-cap check at apply time), the modify is dropped and the original order is untouched.

Next: Account Data