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 reason | Meaning |
|---|
not_found | Unknown id, or an order owned by another wallet (no existence leak) |
already_terminal | Already CANCELLED / FILLED / REJECTED / EXPIRED — idempotent |
lock_invariant | A per-order balance-lock invariant tripped; the rest of the batch is unaffected |
unknown | Transient 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.
| Field | Type | Effect |
|---|
marketId | string | Cancel only orders for this market symbol |
side | "buy" / "sell" (case-insensitive) | Cancel only buys or only sells |
outcome | integer ≥ 0 | Cancel 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
- Server calls
matcher.cancelOrder over gRPC. NOT_FOUND is
treated as idempotent success.
- 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.
- Double-cancel protection via the conditional WHERE.
Cancellation states
| Condition | Final status | Notes |
|---|
Order was OPEN or PARTIAL | CANCELLED | Residual refunded |
Order was FILLED | not_found from API | Terminal — nothing to cancel |
Order was already CANCELLED | not_found from API | Idempotent |
Market was RESOLVED / CANCELLED | CANCELLED_BY_RESOLVE (set by admin-api) | Your DELETE returns not_found |