> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xo.market/llms.txt
> Use this file to discover all available pages before exploring further.

# Global trading pause

> Protocol-wide kill switch — what it does, how to detect it, how to handle it client-side.

XO has a protocol-wide pause flag on the on-chain `CTFExchange` contract. When pausing is active, the orderbook **rejects all new orders globally** until trading resumes. This is distinct from a single market being [`Paused`](/guides/market-lifecycle) — the global pause halts every market at once.

## What triggers it

The flag flips when the on-chain `TradingPaused` or `TradingUnpaused` event is observed on the CTF Exchange. The orderbook's indexer routes the event and the gateway updates its in-memory flag.

There are three reasons the orderbook will emit a `trading_state_changed` frame — see the `source` field below.

## REST: `GET /trading-state`

Out-of-band query for the current state. Always 200 with `Cache-Control: no-store`:

```json theme={null}
{"state": "active"}
```

or

```json theme={null}
{"state": "paused"}
```

Useful at startup to learn the initial state without waiting for the snapshot WS frame.

## Order placement while paused

`POST /order` and `POST /orders` return **503 Service Unavailable** with a typed error body and a `Retry-After: 60` header:

```http theme={null}
HTTP/1.1 503 Service Unavailable
Retry-After: 60
Content-Type: application/json

{
  "error": "trading_paused",
  "message": "Global trading is paused. New orders are not being accepted."
}
```

The stable `error: "trading_paused"` code lets clients branch deterministically:

```python theme={null}
if response.status_code == 503:
    body = response.json()
    if body.get("error") == "trading_paused":
        backoff_until_trading_active()
```

`Retry-After: 60` is an advisory floor, not a guarantee — the real unpause time is unknown.

## WebSocket: `trading_state_changed`

Emitted on **all three channels** (`/ws/market`, `/ws/user`, `/ws`) regardless of your current subscription. The event bypasses per-channel filters because it's protocol-wide.

```json theme={null}
{
  "event_type": "trading_state_changed",
  "state": "paused",
  "source": "on_chain",
  "timestamp": "1779355900000"
}
```

### Fields

| Field       | Values                                           | Meaning                                                 |
| ----------- | ------------------------------------------------ | ------------------------------------------------------- |
| `state`     | `paused`, `active`                               | The orderbook's view of the on-chain global pause flag. |
| `source`    | `on_chain`, `startup_reconciliation`, `snapshot` | Why this frame was sent. See below.                     |
| `timestamp` | digit string                                     | Millisecond Unix when the gateway emitted the frame.    |

### `source` values

| `source`                 | Meaning                                                                                                                                                                                                                                                                                                                                   |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `on_chain`               | A real `TradingPaused`/`TradingUnpaused` event was observed on-chain and routed via the indexer. **This is the live transition path.**                                                                                                                                                                                                    |
| `startup_reconciliation` | The orderbook booted with a stored value that disagreed with the on-chain truth at startup, and synthesised a transition to match the chain.                                                                                                                                                                                              |
| `snapshot`               | **Not a transition.** Emitted (a) immediately after every WebSocket handshake so new connections learn the current state without polling, and (b) after a broadcast lag where the dropped window might have contained a real transition. Snapshot frames are idempotent — if the snapshot matches your current view, treat it as a no-op. |

## Client-side handling

Recommended pattern:

1. **At startup**, either poll `GET /trading-state` once or wait for the initial `snapshot` frame on the WS handshake. Store the current `state`.
2. **Subscribe to any WS channel** to receive future transitions.
3. **On `state=paused`**:
   * Stop submitting new orders. They'll get 503-rejected anyway.
   * Existing resting orders are **retained** during a global pause — they don't get mass-cancelled the way they do on a per-market terminal transition. Matching is frozen until `state=active`.
   * Optionally cancel your own resting orders if you don't want exposure when trading resumes.
4. **On `state=active`**: resume order entry.
5. **Treat `snapshot` frames as idempotent** — only act on a state *change*.

```javascript theme={null}
let tradingState = 'active'; // seed from REST or initial snapshot

ws.on('message', (frame) => {
  if (frame.event_type !== 'trading_state_changed') return;
  if (frame.state === tradingState) return; // snapshot of unchanged state
  tradingState = frame.state;
  if (frame.state === 'paused') haltOrderEntry();
  else                          resumeOrderEntry();
});
```

## Difference from per-market `Paused`

|                | Global trading pause                    | Market `Paused` status                      |
| -------------- | --------------------------------------- | ------------------------------------------- |
| Scope          | All markets at once                     | Single market                               |
| Trigger        | On-chain `TradingPaused` event          | Admin action on one market                  |
| REST signal    | 503 `trading_paused`                    | 422 `market is not active (status: Paused)` |
| WS signal      | `trading_state_changed` on all channels | `market_status_changed` on `/ws`            |
| Resting orders | Retained, matching frozen               | Retained, matching frozen                   |

Both are non-terminal: orders stay on the book and the market can resume to normal trading. See [Market lifecycle](/guides/market-lifecycle) for the per-market state diagram.
