For the complete documentation index, see llms.txt. This page is also available as Markdown.

Cancel-on-Disconnect

Cancel-on-Disconnect (COD) is an account-scoped dead-man's switch built on cancelAllAfter. You arm a countdown; if you don't refresh it before the countdown elapses, the matching engine cancels every open order on the account. It's the safety net for "my client died — don't leave my orders sitting on the book."

It is exposed as POST /v2/cancelAllAfter over REST and as the cancelAllAfter message type over WebSocket Order Entry.

What it does

You arm the switch by sending a signed cancelAllAfter with a timeoutMs. While armed, the matching engine holds a countdown for your account. If you send another cancelAllAfter before it elapses, the countdown is reset (last-write-wins). If you don't, the countdown fires and the matching engine cancels every open order on the account — all markets, spot and perp — the same scope as cancelAll with no symbol filter, with one deliberate exception: protective stops are kept (see Protective stops).

The cancellations surface as CANCELLED order changes on the orderChanges channel, the same as any other cancel.

timeoutMs and bounds

timeoutMs

Effect

0

Disarm. Cancels the armed countdown; nothing will auto-cancel.

5000–60000

Arm / refresh. Sets the countdown to this many milliseconds (5–60 seconds, inclusive).

Any non-zero value outside [5000, 60000] is rejected with INPUT_VALIDATION_ERROR. Nothing is armed and any existing countdown is left unchanged.

Arm, refresh, disarm

  • Arm — send cancelAllAfter with a timeoutMs in range. The countdown starts.

  • Refresh — send cancelAllAfter again before the countdown elapses. Each call replaces the deadline (last-write-wins); re-sending the same timeoutMs is a valid refresh.

  • Disarm — send cancelAllAfter with timeoutMs = 0. The countdown is cleared.

Account scope

Countdown control has an account allowance separate from create/modify and cancellation requests. A rate-limited refresh leaves the previous countdown unchanged; track the last acknowledged triggerAt. Automatic cancellation on expiry is not blocked by an exhausted bulk-cancel allowance. Connection and IP limits still apply: the REST trading bucket includes countdown refreshes. See Rate Limits for the agreed levels and disarm behavior.

The timer is per account, not per connection or per order. One armed countdown covers every open order on the account across all markets (spot and perp).

Protective stops

The dead-man's switch preserves both armed triggers and fired protective children. Armed triggers remain outside the book; fired GTC/GTT children can remain on the book and execute. User cancelAll cancels both within its scope.

Trigger firing is live. Do not assume a disconnect removes protective orders or prevents them from trading; see Trigger Orders (SL/TP).

Transport-agnostic

REST and WebSocket Order Entry arm the same account-level matching-engine timer — there is one canonical deadline per account. You can arm over REST and refresh over WebSocket (or vice versa); the last cancelAllAfter to arrive, on whichever transport, wins.

Request fields

All fields are required:

Field
Type
Description

accountId

integer

The account whose orders are governed by the switch.

timeoutMs

integer

0 to disarm, otherwise 5000–60000 (inclusive).

signature

string

EIP-712 signature over the request (see below).

nonce

string

Signature nonce — see Signatures & Nonces.

signerWallet

string

The wallet that produced the signature.

deadline

integer

EIP-712 signature-validity window, in unix seconds. This is not the trigger time.

deadline is only the signature-validity window — how long the signed request is accepted for. The time the switch will fire is triggerAt in the response, derived from timeoutMs on the exchange clock. The two are unrelated.

Signing

The EIP-712 envelope signs the full CancelAllAfter struct — verifyingChainId, deadline, accountId, timeoutMs, and nonce (verifyingChainId binds the signature to the target chain). Use a fresh nonce per request. See Signatures & Nonces for the signing model and the Python SDK helpers.

Response

A successful arm/refresh echoes timeoutMs and returns triggerAt — the time the switch will fire, on the exchange clock.

A successful disarm (timeoutMs = 0) returns no triggerAt.

Arm a short timeout and refresh it on your own heartbeat — comfortably faster than the timeout you chose. As long as your client is healthy it keeps refreshing the countdown and nothing happens; the moment it stops (process crash, network partition, hang), the countdown elapses and the matching engine cancels your orders for you.

Because the timer is refreshed only by cancelAllAfter — not by your order flow or by protocol pings — your refresh loop has to send cancelAllAfter explicitly. This is independent of the WebSocket heartbeat, which keeps the connection alive but never touches the COD timer.

Errors

Error code
When

INPUT_VALIDATION_ERROR

A missing or malformed field, or a non-zero timeoutMs outside [5000, 60000].

CANCEL_ALL_AFTER_OTHER_ERROR

A matching-engine-side failure with no more specific code.

SERVICE_UNAVAILABLE_ERROR

Not accepted: back off and re-sign with a fresh nonce.

ORDER_OUTCOME_UNKNOWN_ERROR

API 3.6.2: the timer may have been armed, refreshed, or disarmed. Open orders cannot establish timer state; another refresh changes the deadline. See Handling ambiguous outcomes.

NUMERIC_OVERFLOW_ERROR

A numeric field overflows its expected range.

UNAUTHORIZED_SIGNATURE_ERROR

The signature doesn't validate against the request contents and signerWallet.

Last updated