Skip to main content

Cancel a single order

POST /api/orders/cancel
X-Api-Key: ps_live_<keyId>_<secret>   # needs `orders:write` scope
Content-Type: application/json

{ "orderId": "0x..." }
Response:
{ "orderId": "0x...", "status": "CANCELLED", "remainingQty": "42" }
If the order already reached a terminal state before your cancel arrived:
{ "orderId": "0x...", "status": "not_found" }
Field-name conventions across the orders API:
  • The cancel request and response use orderId.
  • List/read endpoints (GET /api/orders/open, /api/orders/history, /api/orders/{id}) return the same value under id.
  • The unfilled remainder is remainingQty everywhere it surfaces (cancel response, order list/read). Treat orderId and id as the same identifier for lookup; the variance is purely a request-vs-response convention.

Cancel a batch by ID

Cancel up to 100 orders by id in one request with POST /api/orders/cancel-batch. Built for an MM re-quote tick: it collapses N single cancels into one round-trip (one advisory-lock acquire, one COMMIT) instead of N.
POST /api/orders/cancel-batch
X-Api-Key: ps_live_<keyId>_<secret>   # needs `orders:write` scope
Content-Type: application/json

{ "orderIds": ["0x1f...", "0x2a...", "0x3c..."] }
Response splits every id into cancelled and notCancelled:
{
  "cancelled": ["0x1f...", "0x3c..."],
  "notCancelled": { "0x2a...": "already_terminal" }
}
notCancelled reasonMeaning
not_foundUnknown id, or an order owned by another wallet (no existence leak)
already_terminalAlready CANCELLED / FILLED / REJECTED / EXPIRED — idempotent
lock_invariantA per-order balance-lock invariant tripped; the rest of the batch is unaffected
unknownTransient failure — safe to retry
Duplicate ids are de-duplicated (cancelled once, reported once). Rate limited at 5 req/sec/wallet. No KYC / deposit-tier gating — cancellation is always permitted.

Cancel all your open orders

POST /api/orders/cancel-all
X-Api-Key: ps_live_<keyId>_<secret>   # needs `orders:write` scope
Content-Type: application/json

{ "marketId": "UAE-CUP-FINAL-20260425" }
Omit marketId to cancel every open order for the authenticated wallet (authenticated associatedWallet or the API-key’s associatedWallet). Response — cancelled is the array of cancelled order ids (breaking change 2026-06; previously a count):
{
  "cancelled": ["0x1f...", "0x2a...", "0x3c..."],
  "notCancelled": { "0x9e...": "already_terminal" },
  "marketId": "UAE-CUP-FINAL-20260425"
}
Breaking change (2026-06). cancelled changed from a number to an array of order ids — read cancelled.length for the count. Orders that could not be cancelled now appear in notCancelled (orderId → reason, same vocabulary as cancel-batch) instead of being dropped silently from the count. Cancel-all also operates on a snapshot of the orders open when the request arrives — orders placed while the cancel runs are not guaranteed to be included.

Scope filters — marketId, side, outcome

All three are optional and combine freely. Empty body = wallet-wide cancel.
FieldTypeEffect
marketIdstringCancel only orders for this market symbol
side"buy" / "sell" (case-insensitive)Cancel only buys or only sells
outcomeinteger ≥ 0Cancel only orders for this outcome index (0 / 1 / …)
POST /api/orders/cancel-all
{ "marketId": "UAE-CUP-FINAL-20260425", "side": "sell", "outcome": 0 }
Combines into one round-trip what previously required iterating /api/orders/open client-side and firing N cancel calls. Common MM use case: a price spike on outcome 0 means “pull all my asks on outcome 0 of this one market” without touching outcome-1 asks or any buys — single call.
Cancel-all is rate-limited at 1 req/sec/wallet. Use for operational interventions, not normal per-order cleanup.

Under the hood

  1. Server calls matcher.cancelOrder over gRPC. NOT_FOUND is treated as idempotent success.
  2. PG authoritative flip in a single tx:
    • UPDATE orders SET status='CANCELLED' WHERE id=$1 AND status IN ('PENDING','OPEN','PARTIAL')
    • refundResidualLocked() — sums remaining locked, refunds to available.
  3. Double-cancel protection via the conditional WHERE.

Cancellation states

ConditionFinal statusNotes
Order was OPEN or PARTIALCANCELLEDResidual refunded
Order was FILLEDnot_found from APITerminal — nothing to cancel
Order was already CANCELLEDnot_found from APIIdempotent
Market was RESOLVED / CANCELLEDCANCELLED_BY_RESOLVE (set by admin-api)Your DELETE returns not_found