> For the complete documentation index, see [llms.txt](https://docs.reya.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.reya.xyz/developers/devnet/api-reference/websocket-api-reference.md).

# WebSocket Info API Reference

{% hint style="info" %}
**Download the current devnet1 contract:** [Info AsyncAPI spec](https://api-devnet.reya-cronos.network/v2/asyncapi-spec.yaml). This endpoint serves the deployed API contract and may advance beyond the version described in this guide. For the exact API **3.5.2** contract, use the immutable [canonical spec source](https://github.com/Reya-Labs/reya-api-specs/tree/4fafae7d19076e8d31811930000a78d842758d9f).
{% endhint %}

## Overview

The Reya DEX Trading WebSocket API v2 provides real-time streaming data for decentralized exchange operations on the Reya Network. This version offers user-friendly data structures with human-readable formats, removing blockchain-specific details while maintaining comprehensive trading functionality.

For placing and cancelling orders over WebSocket, see [WebSocket Order Entry API Reference](/developers/devnet/api-reference/ws-exec-api-reference.md). The recommended integration runs both connections in parallel: that surface for order entry, this surface for market and account updates.

## Server Endpoints

Perpetual order-book trading is currently available only on **devnet1**. Other environments will be added here as the order book becomes available on them.

### Devnet1 (perpOB testnet)

* **URL**: `wss://websocket-devnet.reya-cronos.network/`
* **Protocol**: WSS
* **Description**: The environment to integrate against today; currently the only environment with perpetual order-book support.

## Channel Architecture

The API uses a hierarchical channel structure with clear separation between different data types:

{% stepper %}
{% step %}

### Market Data Channels

* `/v2/perpMarkets/summary` - Perp market summaries
* `/v2/perpMarket/{symbol}/summary` - Individual perp market summary
* `/v2/spotMarkets/summary` - Spot market summaries
* `/v2/spotMarket/{symbol}/summary` - Individual spot market summary
* `/v2/market/{symbol}/perpExecutions` - Market-specific perpetual executions
* `/v2/market/{symbol}/depth` - L2 order book depth snapshots for a specific market
* `/v2/market/{symbol}/spotExecutions` - Market-specific spot executions
* `/v2/market/{symbol}/executionBusts` - Market-specific execution busts (failed fills — spot + perp)
* `/v2/assetOraclePrices` - Asset Stork oracle prices
  {% endstep %}

{% step %}

### Wallet Data Channels

* `/v2/wallet/{address}/positions` - Position updates
* `/v2/wallet/{address}/orderChanges` - Order change updates
* `/v2/wallet/{address}/perpExecutions` - Wallet-specific perpetual executions
* `/v2/wallet/{address}/spotExecutions` - Wallet-specific spot executions
* `/v2/wallet/{address}/executionBusts` - Wallet-specific execution busts (failed fills — spot + perp)
* `/v2/wallet/{address}/accountBalances` - Account balance updates
* `/v2/wallet/{address}/transfers` - Transfer history (account ledger) entries: deposits, withdrawals, transfers, stakes, spot legs, perp fees and rebates, liquidation penalties
* `/v2/wallet/{address}/accounts` - Account creation / ownership-change notifications for the wallet
  {% endstep %}
  {% endstepper %}

### Parameter Validation

#### Symbol Parameter

* **Pattern**: `^[A-Za-z0-9]+$`
* **Examples**: `BTCRUSDPERP`, `WETHRUSD`, `kBONKRUSDPERP`, `AI16ZRUSDPERP`
* **Description**: Trading symbol supporting alphanumeric characters

#### Address Parameter

* **Pattern**: `^0x[a-fA-F0-9]{40}$`
* **Example**: `0x6c51275fd01d5dbd2da194e92f920f8598306df2`
* **Description**: Ethereum wallet address (40 hexadecimal characters)

## Message Structure

The Info surface uses one envelope shape for streamed channel data, plus a small set of control envelopes for subscription management. All envelopes share a `type` discriminator at the top level; the rest of the body is type-specific.

### Channel Data Envelope (Server → Client)

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/perpMarket/BTCRUSDPERP/summary",
  "data": { /* channel-specific data */ }
}
```

* **type**: Always `"channel_data"` for data updates
* **timestamp**: Server timestamp in milliseconds
* **channel**: Specific channel identifier
* **data**: Channel-specific payload (object or array)

### Subscribe Envelope (Client → Server)

```json
{
  "type": "subscribe",
  "channel": "/v2/perpMarkets/summary",
  "id": "req123"
}
```

The `id` is an optional client-chosen correlation marker. The server does not echo it back in the confirmation and does not enforce uniqueness across in-flight subscribes; it is purely for client-side bookkeeping.

### Subscribed Confirmation (Server → Client)

```json
{
  "type": "subscribed",
  "channel": "/v2/perpMarkets/summary",
  "contents": { /* optional initial data */ }
}
```

The `contents` field carries an initial snapshot for channels that provide one (e.g. `/v2/market/{symbol}/depth`); otherwise it is omitted. For `/v2/wallet/{address}/orderChanges`, `contents` carries the current open orders plus a `snapshotSequenceNumber` cursor. For `/v2/wallet/{address}/transfers`, `contents.data` carries the wallet's most recent ledger entries, newest first.

### Unsubscribe Envelope (Client → Server)

```json
{
  "type": "unsubscribe",
  "channel": "/v2/perpMarkets/summary",
  "id": "req123"
}
```

### Unsubscribed Confirmation (Server → Client)

```json
{
  "type": "unsubscribed",
  "channel": "/v2/perpMarkets/summary"
}
```

### Error Envelope (Server → Client)

```json
{
  "type": "error",
  "message": "Invalid channel",
  "channel": "/v2/invalid/channel"
}
```

The shape and the full set of possible `message` values are documented in [Error Catalog](#error-catalog) below.

### Heartbeats

The heartbeat / connection-liveness mechanism is documented in detail on its own page — see [Heartbeats](/developers/devnet/connectivity/heartbeats.md). Short version: protocol-level pings handle liveness automatically, no application-level code is required on the client.

## Reconnecting & resyncing

Delivery depends on the channel class:

* **Stateful feeds** — depth and `orderChanges` — stay ordered and lossless while the connection is healthy. If your client cannot keep up, the connection closes with code `1013` (`slow consumer — resubscribe for fresh snapshot`). Reconnect and rebuild from a fresh snapshot.
* **Latest-value feeds** — including `assetOraclePrices` — may skip intermediate updates and deliver the latest value per asset when your client falls behind. Do not use these channels as an every-tick ledger. If the client continues to fall behind, the connection closes with `1013`.

A service restart or required feed resynchronization closes the connection with code `1012`. Treat the reason string as advisory: recovery for both `1012` and `1013` is the same:

1. Reconnect and re-send `subscribe` for each channel.
2. The `subscribed` confirmation's **`contents`** carries a fresh initial snapshot for channels that provide one (e.g. `/v2/market/{symbol}/depth` returns a full `SNAPSHOT`). Rebuild from it and resume applying `UPDATE`s.
3. For `orderChanges`, rebuild from the subscribed confirmation's `contents.data` open-order snapshot and `contents.snapshotSequenceNumber` cursor; subsequent rows have `sequenceNumber` greater than that cursor. For other per-wallet channels (`positions`, `accountBalances`), reconcile current state from REST, then resume the stream.

Updates from the disconnected window are not replayed. A reconnect/resubscribe returns a new top-100 `SNAPSHOT`; it does not inject a mid-stream snapshot into the old connection. An ordered per-market depth reset can instead be delivered in-band as an ordinary `UPDATE` that removes old levels and adds the replacement bounded view. Apply it using the same absolute-quantity rules as any update.

{% hint style="warning" %}
`FEED_RESET` remains in the API 3.5.2 enum, but current devnet1 resynchronization uses close code `1012` instead. If you receive `FEED_RESET`, rebuild your local order view; it does not mean your orders were cancelled.
{% endhint %}

## Channels Reference

### 1. Market Data Channels

#### `/v2/perpMarkets/summary`

**Purpose**: Real-time updates for all perp market summaries.

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/perpMarkets/summary"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/perpMarkets/summary",
  "data": [
    {
      "symbol": "BTCRUSDPERP",
      "updatedAt": 1747927089946,
      "oiQty": "154.741",
      "fundingRate": "-0.000509373441021089",
      "longFundingValue": "412142.26",
      "shortFundingValue": "412142.26",
      "volume24h": "917833.49891",
      "pxChange24h": "92.6272285500004",
      "markPrice": "2666.48162040777",
      "oraclePrice": "2666.11940512338",
      "throttledMidPrice": "2666.48166680625",
      "pricesUpdatedAt": 1747927089597
    }
  ]
}
```

<details>

<summary><strong>Data Type - MarketSummary</strong></summary>

* `symbol` (string): Trading symbol
* `updatedAt` (integer): Last calculation timestamp (milliseconds)
* `oiQty` (string): Total open interest quantity
* `fundingRate` (string): Current hourly funding rate
* `longFundingValue` (string): Current long funding value
* `shortFundingValue` (string): Current short funding value
* `volume24h` (string): 24-hour trading volume
* `pxChange24h` (string, optional): 24-hour price change
* `markPrice` (string, optional): Mark price at summary update — the matching engine's mark
* `oraclePrice` (string, optional): Stork's **CEX-mark** feed for the market's underlying (`<ASSET>USDMARK`), in the market's quote currency. **Not the index price** — it is `price2`, one of the three inputs to `markPrice` (see [Mark Price](/developers/devnet/pricing-and-funding/mark-price.md)); the index is a separate Stork feed (`<ASSET>USD`). Do not use it as the funding or premium reference. Also distinct from `SpotMarketSummary.oraclePrice`, which is the spot index. Omitted whenever no fresh value is available (most commonly a tick older than 60 seconds) rather than republishing a stale one — absent means "no fresh value", not zero.
* `throttledMidPrice` (string, optional): Throttled order-book mid price at summary update. Absent on an empty or one-sided book
* `pricesUpdatedAt` (integer, optional): Last price update timestamp

</details>

#### `/v2/perpMarket/{symbol}/summary`

**Purpose**: Real-time updates for a specific perp market's summary.

**Parameters**:

* `symbol`: Trading symbol (e.g., `BTCRUSDPERP`, `kBONKRUSDPERP`)

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/perpMarket/BTCRUSDPERP/summary"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/perpMarket/BTCRUSDPERP/summary",
  "data": {
    "symbol": "BTCRUSDPERP",
    "updatedAt": 1747927089946,
    "oiQty": "154.741",
    "fundingRate": "-0.000509373441021089",
    "longFundingValue": "412142.26",
    "shortFundingValue": "412142.26",
    "volume24h": "917833.49891",
    "pxChange24h": "92.6272285500004",
    "markPrice": "2666.48162040777",
    "oraclePrice": "2666.11940512338",
    "throttledMidPrice": "2666.48166680625",
    "pricesUpdatedAt": 1747927089597
  }
}
```

<details>

<summary><strong>Data Type - MarketSummary</strong></summary>

Same as above - see `/v2/perpMarkets/summary` channel for complete field definitions.

</details>

#### `/v2/spotMarkets/summary`

**Purpose**: Real-time updates for all spot market summaries

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/spotMarkets/summary"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/spotMarkets/summary",
  "data": [
    {
      "symbol": "WETHRUSD",
      "updatedAt": 1747927089946,
      "volume24h": "917833.49891",
      "pxChange24h": "92.6272285500004",
      "oraclePrice": "2666.48162040777",
      "throttledMidPrice": "2666.48166680625"
    }
  ]
}
```

<details>

<summary><strong>Data Type - SpotMarketSummary</strong></summary>

* `symbol` (string): Trading symbol
* `updatedAt` (integer): Last calculation timestamp (milliseconds)
* `volume24h` (string): 24-hour trading volume in USD
* `pxChange24h` (string, optional): Absolute 24-hour price change
* `oraclePrice` (string, optional): Stork price for the market's base token — the spot **index** feed (`<ASSET>USD`) — quoted in USDC (also called rUSD on Reya Network). Note this is a different feed from `MarketSummary.oraclePrice`, which carries the CEX mark.
* `throttledMidPrice` (string, optional): Throttled order-book mid price at summary update. Omitted on an empty or one-sided book, where no mid exists — absent means "no mid", not zero.

</details>

#### `/v2/spotMarket/{symbol}/summary`

**Purpose**: Real-time updates for a specific spot market's summary

**Parameters**:

* `symbol`: Trading symbol (e.g., `WETHRUSD`)

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/spotMarket/WETHRUSD/summary"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/spotMarket/WETHRUSD/summary",
  "data": {
    "symbol": "WETHRUSD",
    "updatedAt": 1747927089946,
    "volume24h": "917833.49891",
    "pxChange24h": "92.6272285500004",
    "oraclePrice": "2666.48162040777",
    "throttledMidPrice": "2666.48166680625"
  }
}
```

<details>

<summary><strong>Data Type - SpotMarketSummary</strong></summary>

Same as above - see `/v2/spotMarkets/summary` channel for complete field definitions.

</details>

#### `/v2/market/{symbol}/perpExecutions`

**Purpose**: Real-time perpetual executions for a specific market

**Parameters**:

* `symbol`: Trading symbol (e.g., `BTCRUSDPERP`, `AI16ZRUSDPERP`)

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/market/BTCRUSDPERP/perpExecutions"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/market/BTCRUSDPERP/perpExecutions",
  "data": [
    {
      "exchangeId": 1,
      "symbol": "BTCRUSDPERP",
      "takerAccountId": 12345,
      "makerAccountId": 67890,
      "takerOrderId": "63552420354981888",
      "makerOrderId": "63552420037263360",
      "qty": "1.0",
      "side": "B",
      "price": "43000.00",
      "takerFee": "12.90",
      "protocolFeeCredit": "7.74",
      "referrerFeeCredit": "1.29",
      "takerRebateCredit": "2.58",
      "poolFeeCredit": "1.29",
      "type": "ORDER_MATCH",
      "timestamp": 1747927089946,
      "sequenceNumber": 152954,
      "fillId": "7205759403792794624"
    }
  ]
}
```

<details>

<summary><strong>Data Type - PerpExecution</strong></summary>

Under the order book, a perpetual execution is a single match record carrying **both sides**. On the wallet-scoped channel you receive the record whether you were the taker or the maker — determine your side from `takerAccountId` vs `makerAccountId`.

* `exchangeId` (integer): Exchange identifier
* `symbol` (string): Trading symbol
* `takerAccountId` (integer): Account identifier of the taker side of the match
* `makerAccountId` (integer, optional): Maker account ID (counterparty providing liquidity)
* `takerOrderId` (string, optional): Taker-side order ID. Absent when the taker order was filled and removed in the same matching round.
* `makerOrderId` (string, optional): Maker-side order ID. Absent when the maker order was fully filled in this execution.
* `qty` (string): Execution quantity
* `side` (Side): Taker side (B=Buy, A=Sell). The maker is always the opposite side.
* `price` (string): Execution price
* `takerFee` (string): Gross fee debited from the taker in rUSD (signed; positive = the taker paid). On a Fee v3 fill it is exactly `protocolFeeCredit + referrerFeeCredit + takerRebateCredit + poolFeeCredit`.
* `protocolFeeCredit` (string, optional): Fee v3 component of `takerFee` credited to the protocol fee collector, in rUSD. Present on Fee v3 fills only; absent on pre–Fee v3 executions and never synthesized.
* `referrerFeeCredit` (string, optional): Fee v3 component of `takerFee` credited to the taker's referrer, in rUSD (`0` with no referrer). Present on Fee v3 fills only.
* `takerRebateCredit` (string, optional): Fee v3 component of `takerFee` routed to the taker-rebate settlement bucket, in rUSD. Part of the gross debit — not necessarily an immediate net credit back to the taker. Present on Fee v3 fills only.
* `poolFeeCredit` (string, optional): Fee v3 component of `takerFee` credited to the passive pool, in rUSD. Present on Fee v3 fills only.
* `makerFee` (string, optional): Signed net maker fee in rUSD from the legacy maker debit/credit legs (negative = rebate). Present only on pre–Fee v3 executions that recorded a maker debit or credit; omitted on every Fee v3 fill (no maker fee or rebate leg) and on `ADL` / `MARKET_CLOSE`.
* `takerOpeningFee` (string, optional): Opening-fee portion of the taker fee in rUSD. Absent for position-extending executions.
* `makerOpeningFee` (string, optional): Opening-fee portion of the maker fee in rUSD. Absent for position-extending executions and whenever `makerFee` is absent.
* `type` (ExecutionType): Execution type (`ORDER_MATCH`, `LIQUIDATION`, `ADL`, `MARKET_CLOSE`)
* `timestamp` (integer): Execution timestamp (milliseconds)
* `sequenceNumber` (integer): Global sequence number
* `fillId` (string): Stable fill identifier for correlating the execution with order updates.
* `takerRealizedPnl` (string, optional): Taker realized PnL from this execution in rUSD (priceVariationPnl + fundingPnl). Absent for position-extending executions.
* `makerRealizedPnl` (string, optional): Maker realized PnL from this execution in rUSD. Absent for position-extending executions.
* `takerPriceVariationPnl` (string, optional): Taker PnL component from price movement in rUSD.
* `makerPriceVariationPnl` (string, optional): Maker PnL component from price movement in rUSD.
* `takerFundingPnl` (string, optional): Taker PnL component from funding payments in rUSD.
* `makerFundingPnl` (string, optional): Maker PnL component from funding payments in rUSD.

</details>

#### `/v2/assetOraclePrices`

**Purpose**: Real-time asset Stork oracle prices. This is the replacement feed for asset-oracle consumers of the `/v2/prices` channel removed from the published API contract, and deliberately omits AMM-era `poolPrice`.

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/assetOraclePrices"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/assetOraclePrices",
  "data": [
    {
      "asset": "rUSD",
      "oraclePrice": "1.0000",
      "updatedAt": 1747927089946
    },
    {
      "asset": "ETH",
      "oraclePrice": "2500.00",
      "updatedAt": 1747927089946
    }
  ]
}
```

<details>

<summary><strong>Data Type - AssetOraclePrice</strong></summary>

* `asset` (string): Asset symbol
* `oraclePrice` (string): Asset Stork oracle price
* `updatedAt` (integer): Last update timestamp (milliseconds)

</details>

#### `/v2/market/{symbol}/depth`

**Purpose**: Real-time L2 order book depth for a specific market — an initial **top-100 snapshot per side** followed by incremental **updates** to that bounded view

**Parameters**:

* `symbol`: Trading symbol (e.g., `BTCRUSDPERP`, `DOGERUSDPERP`)

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/market/BTCRUSDPERP/depth"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/market/BTCRUSDPERP/depth",
  "data": {
    "symbol": "BTCRUSDPERP",
    "type": "SNAPSHOT",
    "bids": [
      { "px": "42999.50", "qty": "1.5" },
      { "px": "42998.00", "qty": "2.0" }
    ],
    "asks": [
      { "px": "43000.50", "qty": "1.0" },
      { "px": "43001.00", "qty": "3.0" }
    ],
    "updatedAt": 1747927089946
  }
}
```

{% hint style="warning" %}
**Snapshots vs updates — you must merge, not replace.** The **first** message after you subscribe is a `"type": "SNAPSHOT"` of at most **100 best bid and 100 best ask levels**. **Every subsequent message is `"type": "UPDATE"` and carries only the price levels that changed** — not the whole book. Maintain your own book and apply each UPDATE:

* For each level in `bids` / `asks`, **upsert** the level at that `px` to the new `qty`.
* A level with **`qty: "0"` means that price level was removed** — delete it from your book.
* Levels not present in an UPDATE are unchanged — leave them.

Treating an UPDATE as a full snapshot will wipe your book on every tick and leave stale levels behind.

**The boundary is part of the feed.** A level leaving the top 100 is removed with `qty: "0"` even if orders still rest there on the exchange. A level entering the top 100 is added with its full current quantity. Changes entirely below the top-100 boundary produce no public depth update. For example, removing the best bid can promote the previous 101st bid; the update removes the old best and adds the promoted level. Apply the entire update before using the new view. Zero means "remove from my published view", not necessarily "no liquidity exists at this price".

There is no WS depth-size parameter. Do not combine a deeper REST snapshot with this stream to maintain a full book: levels outside the WS view are not kept current. REST `GET /v2/market/{symbol}/depth?limit=N` defaults to 100 levels per side and caps at 1,000; see [REST OpenAPI contract](https://api-devnet.reya-cronos.network/v2/openapi-spec.yaml).
{% endhint %}

**Update message** (only changed levels; `qty: "0"` removes a level):

```json
{
  "type": "channel_data",
  "timestamp": 1747927090120,
  "channel": "/v2/market/BTCRUSDPERP/depth",
  "data": {
    "symbol": "BTCRUSDPERP",
    "type": "UPDATE",
    "bids": [
      { "px": "42999.50", "qty": "2.25" },
      { "px": "42998.00", "qty": "0" }
    ],
    "asks": [],
    "updatedAt": 1747927090120
  }
}
```

<details>

<summary><strong>Data Type - Depth</strong></summary>

* `symbol` (string): Trading symbol
* `type` (DepthType): `SNAPSHOT` (the bounded view — the first message after subscribe) or `UPDATE` (only the changed levels — every subsequent message)
* `bids` (array): Bid side levels, sorted descending by price. On a `SNAPSHOT`, at most the best 100 levels on that side; on an `UPDATE`, only changes to that bounded view.
  * `px` (string): Price level
  * `qty` (string): Aggregated quantity at this price level — **`0` means the level was removed** (on an `UPDATE`)
* `asks` (array): Ask side levels, sorted ascending by price. On a `SNAPSHOT`, at most the best 100 levels on that side; on an `UPDATE`, only changes to that bounded view.
  * `px` (string): Price level
  * `qty` (string): Aggregated quantity at this price level — **`0` means the level was removed** (on an `UPDATE`)
* `updatedAt` (integer): Exchange timestamp (milliseconds) of the latest depth change for this market. It is comparable across connections. An unchanged value can mean a quiet market; use heartbeats to monitor connection liveness.

</details>

#### `/v2/market/{symbol}/spotExecutions`

**Purpose**: Real-time spot executions for a specific market

**Parameters**:

* `symbol`: Trading symbol (e.g., `WETHRUSD`, `BTCRUSD`)

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/market/WETHRUSD/spotExecutions"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/market/WETHRUSD/spotExecutions",
  "data": [
    {
      "exchangeId": 1,
      "symbol": "WETHRUSD",
      "takerAccountId": 12345,
      "makerAccountId": 67890,
      "takerOrderId": "63552420354981888",
      "makerOrderId": "63552420037263360",
      "qty": "1.0",
      "side": "B",
      "price": "2500.00",
      "takerFee": "0.0",
      "type": "ORDER_MATCH",
      "timestamp": 1747927089946,
      "sequenceNumber": 152954,
      "fillId": "7205759403792794624"
    }
  ]
}
```

<details>

<summary><strong>Data Type - SpotExecution</strong></summary>

* `exchangeId` (integer, optional): Exchange identifier
* `symbol` (string): Trading symbol
* `takerAccountId` (integer): Account identifier of the taker side of the trade
* `makerAccountId` (integer): Maker account ID (counterparty providing liquidity)
* `takerOrderId` (string, optional): Taker-side order ID. Absent when the taker order was filled and removed in the same matching round.
* `makerOrderId` (string, optional): Maker-side order ID. Absent when the maker order was fully filled in this execution.
* `qty` (string): Execution quantity in base asset units
* `side` (Side): Taker side (B=Buy, A=Sell). The maker is always the opposite side.
* `price` (string): Execution price in quote-per-base units
* `takerFee` (string): Fee charged to the taker, in the market's fee asset
* `type` (ExecutionType): Execution type — for spot, `ORDER_MATCH` or `LIQUIDATION` (`ADL` / `MARKET_CLOSE` are perp-only)
* `timestamp` (integer): Execution timestamp (milliseconds since epoch)
* `sequenceNumber` (integer): Monotonic per-execution sequence number across the spot matching engine; increases by 1 for every spot execution on Reya. Use this to dedup and gap-detect on the consumer side after a reconnect.
* `fillId` (string): Stable fill identifier for correlating the execution with order updates.

</details>

#### `/v2/market/{symbol}/executionBusts`

**Purpose**: Real-time execution busts (failed fills) for a specific market — covers both spot and perp

**Parameters**:

* `symbol`: Trading symbol (e.g., `WETHRUSD`, `BTCRUSDPERP`)

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/market/BTCRUSDPERP/executionBusts"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/market/BTCRUSDPERP/executionBusts",
  "data": [
    {
      "symbol": "BTCRUSDPERP",
      "takerAccountId": 12345,
      "exchangeId": 1,
      "makerAccountId": 67890,
      "takerOrderId": "63552420354981888",
      "makerOrderId": "63552420037263360",
      "qty": "1.0",
      "side": "B",
      "price": "43000.00",
      "reason": {
        "reasonName": "AccountBelowIM",
        "accountId": 1234,
        "delta": "-321",
        "shortfall": "321"
      },
      "timestamp": 1747927089946,
      "sequenceNumber": 152954,
      "fillId": "7205759403792794624"
    }
  ]
}
```

<details>

<summary><strong>Data Type - ExecutionBust</strong></summary>

A bust is emitted when the matching engine matched two orders but the on-chain settlement attempt reverted (e.g. insufficient balance, signature staleness, market paused). The match is rolled back; both orders are released back to their owners' state. This single channel covers **both spot and perp** — tell them apart by the symbol suffix (`…RUSD` = spot, `…RUSDPERP` = perp). See [Trade Busts](/developers/devnet/executions-and-settlement/trade-busts.md) for the full trade lifecycle, when busts happen, and how clients should handle them.

* `symbol` (string): Trading symbol (spot ends in `RUSD`, perp ends in `RUSDPERP`)
* `takerAccountId` (integer): Account identifier of the taker side of the failed trade
* `exchangeId` (integer): Exchange identifier
* `makerAccountId` (integer): Maker account ID (counterparty)
* `takerOrderId` (string): Taker-side order ID
* `makerOrderId` (string): Maker-side order ID
* `qty` (string): Failed base quantity in base asset units
* `side` (Side): Taker side (B=Buy, A=Sell)
* `price` (string): Price at which the failed match was attempted
* `reason` (ExecutionBustReason): Machine-readable decoded revert reason object. Branch on `reason.reasonName`. Known contract errors have typed fields (for example `AccountBelowIM` includes `accountId`, `delta`, and `shortfall`); decoded but unmodeled contract errors use `{ reasonName: "<ErrorName>", args: { ... } }`; legacy decoded strings use `{ reasonName: "DecodedReason", message: "..." }`; undecodable bytes use `{ reasonName: "UnknownReason", message: "Unknown reason, reach out for support" }`. You do not need to ABI-decode anything yourself.
* `timestamp` (integer): Block timestamp of the failed settlement (milliseconds since epoch). This is the chain-side timestamp, not the original off-chain match timestamp.
* `sequenceNumber` (integer): Monotonic execution-bust sequence number; increases by 1 per bust. Use it to dedup and gap-detect after a reconnect.
* `fillId` (string): Stable fill identifier for correlating the bust with order updates.

</details>

### 2. Wallet Data Channels

#### `/v2/wallet/{address}/positions`

**Purpose**: Real-time position updates for a wallet

**Parameters**:

* `address`: Ethereum wallet address (e.g., `0x6c51275fd01d5dbd2da194e92f920f8598306df2`)

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/positions"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/positions",
  "data": [
    {
      "exchangeId": 1,
      "symbol": "BTCRUSDPERP",
      "accountId": 12345,
      "qty": "1.5",
      "side": "B",
      "avgEntryPrice": "43000.00",
      "avgEntryFundingValue": "100.25",
      "lastTradeSequenceNumber": 152954
    }
  ]
}
```

<details>

<summary><strong>Data Type - Position</strong></summary>

* `exchangeId` (integer): Exchange identifier
* `symbol` (string): Trading symbol
* `accountId` (integer): Account identifier
* `qty` (string): Position quantity
* `side` (Side): Position side (B=Buy, A=Sell)
* `avgEntryPrice` (string): Average entry price
* `avgEntryFundingValue` (string): Average entry funding value
* `lastTradeSequenceNumber` (integer): Last execution sequence number

</details>

#### `/v2/wallet/{address}/orderChanges`

**Purpose**: Real-time order change updates for wallet order-state tracking.

**Parameters**:

* `address`: Ethereum wallet address

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/orderChanges"
}
```

**Subscribed Confirmation**:

```json
{
  "type": "subscribed",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/orderChanges",
  "contents": {
    "data": [
      {
        "exchangeId": 1,
        "symbol": "BTCRUSDPERP",
        "accountId": 12345,
        "orderId": "490346525705109504",
        "clientOrderId": "1778601294274124",
        "qty": "1.0",
        "execQty": "0",
        "cumQty": "0",
        "side": "B",
        "limitPx": "43000.00",
        "orderType": "LIMIT",
        "timeInForce": "GTC",
        "reduceOnly": false,
        "status": "OPEN",
        "createdAt": 1747927089946,
        "lastUpdateAt": 1747927089946
      }
    ],
    "snapshotSequenceNumber": 152953
  }
}
```

The subscribed confirmation returns the current open-orders snapshot in `contents.data` and the snapshot cursor in `contents.snapshotSequenceNumber`. Snapshot orders do **not** include `sequenceNumber`; the cursor represents the snapshot boundary. Subsequent `channel_data` order rows include `sequenceNumber` greater than `snapshotSequenceNumber`.

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/orderChanges",
  "data": [
    {
      "exchangeId": 1,
      "symbol": "BTCRUSDPERP",
      "accountId": 12345,
      "orderId": "490346525705109504",
      "clientOrderId": "1778601294274124",
      "qty": "1.0",
      "execQty": "0.5",
      "cumQty": "0.5",
      "firstFillId": "7205759403792794624",
      "fillCount": 1,
      "side": "B",
      "limitPx": "43000.00",
      "orderType": "LIMIT",
      "timeInForce": "GTC",
      "reduceOnly": false,
      "status": "OPEN",
      "createdAt": 1747927089946,
      "lastUpdateAt": 1747927089946,
      "sequenceNumber": 152954
    }
  ]
}
```

<details>

<summary><strong>Data Type - Order</strong></summary>

This channel carries **resting matching-engine orders** and their state changes. Resting orders include `LIMIT` orders with a `GTC` or `GTT` time-in-force; a `LIMIT` IOC never rests. Armed `STOP_LOSS` / `TAKE_PROFIT` triggers appear here as `OPEN` orders (with their `triggerPx`, and `qty` omitted — whole-position) through their arm → re-price → fire/cancel lifecycle, including across matching-engine restarts; while armed they add no executable book liquidity. They now fire into the chosen IOC/GTC/GTT child; a fired GTC/GTT remainder can rest in the book and is cancel-only. The channel reports the order's **actual `timeInForce`** and `orderType`.

`Order.sequenceNumber` is present on live `orderChanges` event rows and on `orderHistory` REST rows. It is absent on open-order/resting snapshots, including `openOrders` REST responses and the `contents.data` snapshot in the `orderChanges` subscribed confirmation.

* `exchangeId` (integer): Exchange identifier
* `symbol` (string): Trading symbol
* `accountId` (integer): Account identifier
* `sequenceNumber` (integer, optional): Monotonic matching-engine order-event sequence. Present on live `orderChanges` event rows and `orderHistory` REST rows; omitted on open-order/resting snapshots. Use it to splice `orderHistory` with the live stream and to deduplicate inclusive `orderHistory` page-boundary overlap.
* `side` (Side): Order side (B=Buy, A=Sell)
* `limitPx` (string): Limit price
* `orderType` (OrderType): `LIMIT`, `STOP_LOSS`, or `TAKE_PROFIT`.
* `status` (OrderStatus): Order status (`OPEN`, `FILLED`, `CANCELLED`). A partially filled order with a resting remainder stays `OPEN`; use `cumQty` to identify partial fills. Request rejections are returned as errors instead of order-status updates.
* `createdAt` (integer): Creation timestamp (milliseconds)
* `lastUpdateAt` (integer): Last-update timestamp (milliseconds). It reflects the order's most recent change and equals `createdAt` until the order changes.
* `orderId` (string): Order identifier
* `clientOrderId` (string, optional): Client-provided order ID, present when the order has a non-zero client id
* `triggered` (boolean, optional): `false` for an armed trigger, `true` for a fired protective child, never `true` for LIMIT. Present on current `openOrders` and `orderChanges`; always omitted on `orderHistory`, where absence means unknown. See [Armed and fired trigger orders](/developers/devnet/order-entry/trigger-orders.md#reading-armed-and-fired-orders).
* `qty` (string, optional): Order quantity; omitted while a trigger is armed. A fired child's remaining quantity is an upper bound: each fill is clamped to the position still reducible
* `execQty` (string, optional): Executed quantity in the current order update
* `cumQty` (string, optional): Cumulative executed quantity across all fills
* `firstFillId` (string, optional): Identifier of the first fill this update represents. Together with `fillCount`, identifies a contiguous fill range. Present only on fill updates.
* `fillCount` (integer, optional): Number of fills this update represents. Present only with `firstFillId`.
* `timeInForce` (TimeInForce, optional): The order's actual time in force. Book orders rest with GTC/GTT; armed triggers can carry IOC/GTC/GTT for their future child.
* `reduceOnly` (boolean, optional): Reduce-only flag, reflecting the value signed on the order
* `postOnly` (boolean, optional): Post-only (maker-only) flag, reflecting the value signed on the order
* `expiresAfter` (integer, optional): For GTT, the signed expiry timestamp (Unix seconds); absent for GTC/IOC. A protective trigger and child share this expiry and stop early to allow time for settlement
* `cancelReason` (CancelReason, optional): Present when a `CANCELLED` order includes a machine-readable reason — e.g. `USER_CANCEL`, `GTT_EXPIRED`, `SELF_TRADE_PREVENTION`, or `RISK_CANCELLED`. For `FEED_RESET` and close code `1012`, see [Reconnecting & resyncing](#reconnecting--resyncing).
* `cancelReasonMessage` (string, optional): Human-readable explanation for `cancelReason`

</details>

#### `/v2/wallet/{address}/perpExecutions`

**Purpose**: Real-time perpetual execution updates for a wallet

**Parameters**:

* `address`: Ethereum wallet address

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/perpExecutions"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/perpExecutions",
  "data": [
    {
      "exchangeId": 1,
      "symbol": "BTCRUSDPERP",
      "takerAccountId": 12345,
      "makerAccountId": 67890,
      "takerOrderId": "63552420354981888",
      "makerOrderId": "63552420037263360",
      "qty": "1.0",
      "side": "B",
      "price": "43000.00",
      "takerFee": "12.90",
      "protocolFeeCredit": "7.74",
      "referrerFeeCredit": "1.29",
      "takerRebateCredit": "2.58",
      "poolFeeCredit": "1.29",
      "type": "ORDER_MATCH",
      "timestamp": 1747927089946,
      "sequenceNumber": 152954,
      "fillId": "7205759403792794624"
    }
  ]
}
```

<details>

<summary><strong>Data Type - PerpExecution</strong></summary>

Same as above - see `/v2/market/{symbol}/perpExecutions` channel for complete field definitions.

</details>

#### `/v2/wallet/{address}/spotExecutions`

**Purpose**: Real-time spot execution updates for a wallet

**Parameters**:

* `address`: Ethereum wallet address

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/spotExecutions"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/spotExecutions",
  "data": [
    {
      "exchangeId": 1,
      "symbol": "WETHRUSD",
      "takerAccountId": 12345,
      "makerAccountId": 67890,
      "takerOrderId": "63552420354981888",
      "makerOrderId": "63552420037263360",
      "qty": "1.0",
      "side": "B",
      "price": "2500.00",
      "takerFee": "0.0",
      "type": "ORDER_MATCH",
      "timestamp": 1747927089946,
      "sequenceNumber": 152954,
      "fillId": "7205759403792794624"
    }
  ]
}
```

<details>

<summary><strong>Data Type - SpotExecution</strong></summary>

Same as above - see `/v2/market/{symbol}/spotExecutions` channel for complete field definitions.

</details>

#### `/v2/wallet/{address}/executionBusts`

**Purpose**: Real-time execution bust updates for a wallet — covers both spot and perp

**Parameters**:

* `address`: Ethereum wallet address

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/executionBusts"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/executionBusts",
  "data": [
    {
      "symbol": "WETHRUSD",
      "takerAccountId": 12345,
      "exchangeId": 1,
      "makerAccountId": 67890,
      "takerOrderId": "63552420354981888",
      "makerOrderId": "63552420037263360",
      "qty": "1.0",
      "side": "B",
      "price": "2500.00",
      "reason": {
        "reasonName": "AccountBelowIM",
        "accountId": 1234,
        "delta": "-321",
        "shortfall": "321"
      },
      "timestamp": 1747927089946,
      "sequenceNumber": 152954,
      "fillId": "7205759403792794624"
    }
  ]
}
```

<details>

<summary><strong>Data Type - ExecutionBust</strong></summary>

Same as above - see `/v2/market/{symbol}/executionBusts` channel for complete field definitions.

</details>

#### `/v2/wallet/{address}/accountBalances`

**Purpose**: Real-time account balance updates for a wallet

**Parameters**:

* `address`: Ethereum wallet address

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/accountBalances"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/accountBalances",
  "data": [
    {
      "accountId": 12345,
      "asset": "WETH",
      "realBalance": "1.25",
      "balanceDEPRECATED": "1.25"
    }
  ]
}
```

<details>

<summary><strong>Data Type - AccountBalance</strong></summary>

* `accountId` (integer): Account identifier
* `asset` (string): Asset symbol (e.g., WETH, RUSD)
* `realBalance` (string): Sum of account net deposits and realized PnL from closed positions
* `balanceDEPRECATED` (string): Sum of account net deposits only (deprecated, will be removed)

</details>

#### `/v2/wallet/{address}/transfers`

**Purpose**: Transfer history (account ledger) entries for a wallet — the same entries `GET /v2/wallet/{address}/transfers` returns, one per account side of every on-chain transfer leg, signed from the account's view. Net deposits only: realized PnL and funding never appear. See [Transfer History (Account Ledger)](/developers/devnet/executions-and-settlement/transfer-history.md).

**Parameters**:

* `address`: Ethereum wallet address

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/transfers"
}
```

The `subscribed` acknowledgement carries the wallet's most recent entries in `contents.data`, newest first (at most 30 for the whole wallet; zero-amount entries are not pushed). Every later frame carries one new entry.

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/transfers",
  "data": [
    {
      "sequenceNumber": 273183852,
      "accountId": 12345,
      "asset": "RUSD",
      "amount": "-1.00",
      "netDepositsAfter": "999.00",
      "type": "PERP_TAKER_FEE",
      "counterpartyAccountId": 1,
      "symbol": "BTCRUSDPERP",
      "fillId": "42",
      "timestamp": 1747927089946,
      "transactionHash": "0x1dfdfed33589363842c4d8e5cfa1b3a9782ec07cb9a5ab5236ad1e1c224b64ea"
    }
  ]
}
```

<details>

<summary><strong>Data Type - Transfer</strong></summary>

* `sequenceNumber` (integer): Unique id of the entry, increasing in chain order and never reused
* `accountId` (integer): The account this entry belongs to
* `asset` (string): Asset symbol (e.g., RUSD, SRUSD)
* `amount` (string): Signed from this account's view; negative = collateral left the account
* `netDepositsAfter` (string): Net deposits of the account in `asset` right after this entry (same basis as `AccountBalance.balanceDEPRECATED`; not `realBalance`)
* `type` (string): Entry type — `DEPOSIT`, `WITHDRAWAL`, `TRANSFER`, `POOL_STAKE`, `POOL_UNSTAKE`, `PERP_TAKER_FEE`, `PERP_REFERRER_REBATE`, `PERP_TAKER_REBATE`, `PERP_POOL_REBATE`, `PERP_FEE`, `SPOT_EXECUTION`, `AUTO_EXCHANGE`, `AUTO_EXCHANGE_INSURANCE_FEE`, `LIQUIDATION_*`, `INSURANCE_FUND_COVERAGE`, `POOL_MERGE`, `OTHER`; treat an unfamiliar value as `OTHER`
* `counterpartyAccountId` (integer, optional): The other side of the leg; absent for deposits, withdrawals, stakes and unstakes
* `symbol` (string, optional): Market of the execution that caused the entry (perp fee entries, spot entries)
* `spotExecutionSequenceNumber` (integer, optional): `SpotExecution.sequenceNumber` for spot entries
* `fillId` (string, optional): Fill identifier linking the entry to `PerpExecution.fillId` / `SpotExecution.fillId`; historical perp fee entries may omit it
* `timestamp` (integer): Block timestamp in milliseconds
* `transactionHash` (string): The settling transaction

</details>

#### `/v2/wallet/{address}/accounts`

**Purpose**: Notifies when an account owned by the wallet is **created**, has its **main/spot binding** change, or (defensively) changes **ownership**. Lets you detect new accounts without polling.

**Subscription**:

```json
{
  "type": "subscribe",
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/accounts"
}
```

**Message Structure**:

```json
{
  "type": "channel_data",
  "timestamp": 1747927089946,
  "channel": "/v2/wallet/0x6c51275fd01d5dbd2da194e92f920f8598306df2/accounts",
  "data": [
    {
      "accountId": "12345",
      "owner": "0x6c51275fd01d5dbd2da194e92f920f8598306df2",
      "mainAccountId": "12345",
      "spotAccountId": null,
      "isMainPerpAccount": true,
      "isSpotAccount": false
    }
  ]
}
```

<details>

<summary><strong>Data Type - AccountUpdate</strong></summary>

* `accountId` (decimal string): The account that was created or changed.
* `owner` (string): The owner wallet address.
* `mainAccountId` (decimal string or `null`): The owner's main perp account id, or `null` when none is bound.
* `spotAccountId` (decimal string or `null`): The owner's spot account id, or `null` when none is bound.
* `isMainPerpAccount` (boolean): Whether `accountId` is the owner's main perp account.
* `isSpotAccount` (boolean): Whether `accountId` is the owner's spot account.
* `removed` (boolean, optional): Present and `true` only on the *previous* owner's channel when an account's ownership changes — drop the account from that owner's list.

</details>

## Error Catalog

The server emits an `error` envelope when it cannot process a frame. The connection stays open; only the offending operation is rejected. Every error envelope shares this shape:

```json
{
  "type": "error",
  "message": "<human-readable description>",
  "channel": "<channel path, present when applicable>"
}
```

The `channel` field is included when the error relates to a specific channel (e.g. an invalid subscribe target). It is omitted for frame-level errors that aren't tied to a particular channel.

The full set of `message` strings emitted by the server:

| Message                                        | When emitted                                                                                                                              | Client action                                                                                                                             |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `Invalid JSON`                                 | The frame body could not be parsed as JSON.                                                                                               | Fix the client serializer.                                                                                                                |
| `Invalid type`                                 | The frame's `type` field is not one of `subscribe`, `unsubscribe`, `ping`. (`pong` is server-only — clients don't send JSON pong frames.) | Verify the request `type`.                                                                                                                |
| `Invalid channel name`                         | The subscribe / unsubscribe target does not match a known channel path or has malformed parameters (e.g. an invalid symbol or address).   | Check the channel name against the [Channels Reference](#channels-reference) and the [Parameter Validation](#parameter-validation) rules. |
| `Error while fetching snapshot from {channel}` | The initial snapshot could not be provided, so the subscription did not succeed.                                                          | Retry the subscribe after a short backoff. If the problem persists, contact support with the channel name and timestamp.                  |

## Data Types & Schemas

### Enumeration Types

<details>

<summary><strong>Side</strong> - Order/position side indicator</summary>

* `B`: Buy/Bid
* `A`: Ask/Sell

</details>

<details>

<summary><strong>ExecutionType</strong> - Type of execution that occurred</summary>

* `ORDER_MATCH`: Regular order matching
* `LIQUIDATION`: Liquidation execution
* `ADL`: Auto-deleveraging execution
* `MARKET_CLOSE`: Perp-only terminal execution used to close a residual position when a market is force-closed

</details>

<details>

<summary><strong>OrderStatus</strong> - Current status of an order</summary>

* `OPEN`: Order is active and can be filled
* `FILLED`: Order has been completely filled
* `CANCELLED`: Order has been cancelled

</details>

<details>

<summary><strong>CancelReason</strong> - Why a `CANCELLED` order terminated</summary>

* `NO_LIQUIDITY`: IOC executed zero quantity because no fillable liquidity was available at its limit
* `IOC_REMAINDER`: IOC partially filled and cancelled the unfilled remainder
* `SELF_TRADE_PREVENTION`: Order would have crossed the same account
* `GTT_EXPIRED`: GTT order expired
* `USER_CANCEL`: Explicit user cancel
* `MASS_CANCEL`: Explicit cancel-all
* `CANCEL_ALL_AFTER`: Cancel-on-disconnect timer fired
* `RISK_CANCELLED`: Pre-trade risk cancelled an already-resting maker as it was about to match. `cancelReasonMessage` is fixed per reason and does not identify the binding check; matching continues through the remaining book
* `OCO_SIBLING_FIRED`: A protective child was created, consuming the paired armed stop even if the child filled nothing
* `PROTECTIVE_SELF_TRADE_SWEEP`: A resting maker was swept so the account's protective stop could execute
* `POSITION_CLOSED`: Nothing remains reducible. Before a child exists, both armed legs are cancelled; for a resting child, a zero reducible-size clamp cancels its remainder
* `RISK_REJECTED`: Pre-trade risk refused a protective fire before a child existed; both legs are cancelled. The reason message is fixed, not the specific failed check
* `FEED_RESET`: Resynchronization signal, not an order cancellation. Current devnet1 uses close code `1012` instead; reconnect and re-subscribe for fresh snapshots

</details>

<details>

<summary><strong>OrderType</strong> - Type of order placed</summary>

* `LIMIT`: Limit order
* `STOP_LOSS`: Stop-loss (trigger) order
* `TAKE_PROFIT`: Take-profit (trigger) order

</details>

<details>

<summary><strong>TimeInForce</strong> - Order duration specification</summary>

* `IOC`: Immediate or Cancel
* `GTC`: Good Till Cancel
* `GTT`: Good Till Time (auto-expires at the order's signed `expiresAfter`)

</details>

<details>

<summary><strong>DepthType</strong> - Order book depth message type</summary>

* `SNAPSHOT`: Snapshot of the public top-100 view per side
* `UPDATE`: Diff containing one or more absolute changed levels across bids and asks; `qty: "0"` removes a level

</details>

<details>

<summary><strong>AccountType</strong> - Account type classification</summary>

*Used by the REST account model. WebSocket messages do not carry an `AccountType` field — the `accounts` channel conveys account classification via the `isMainPerpAccount` / `isSpotAccount` booleans instead.*

* `MAINPERP`: Main perpetual trading account
* `SUBPERP`: Sub perpetual trading account
* `SPOT`: Spot trading only account

</details>

## Connection Management

### Reconnection Pattern

Reconnect with the usual exponential-backoff-with-jitter pattern. The Reya-specific steps — re-subscribe to every channel and reconcile missed events from REST (e.g. `GET /v2/wallet/{address}/openOrders`, `GET /v2/wallet/{address}/perpExecutions`) — are covered in [Reconnecting & resyncing](#reconnecting--resyncing) above.

For the meaning of WS close codes you'll see on `onclose` (`1000`, `1001`, `1006`, etc.), see [What Happens When the Server Closes the Connection](/developers/devnet/connectivity/heartbeats.md#what-happens-when-the-server-closes-the-connection).

### Service Restarts

Info service restarts close connections with code `1012`; reconnect and re-subscribe for fresh snapshots. Order Entry uses close code `1001`. Both are documented in [Service restarts](/developers/devnet/connectivity/heartbeats.md#service-restarts).

## Python SDK Example

Worked examples are included in the [pinned Python SDK onboarding snapshot](https://github.com/Reya-Labs/reya-python-sdk/tree/37450ccb2babc99398d1ac1290d48860a9a2e2fa) under [`examples/websocket/`](https://github.com/Reya-Labs/reya-python-sdk/tree/37450ccb2babc99398d1ac1290d48860a9a2e2fa/examples/websocket). The directory is split by market type:

* [`examples/websocket/perps/market_monitoring.py`](https://github.com/Reya-Labs/reya-python-sdk/blob/37450ccb2babc99398d1ac1290d48860a9a2e2fa/examples/websocket/perps/market_monitoring.py) — subscribe to perp market summaries
* [`examples/websocket/perps/prices_monitoring.py`](https://github.com/Reya-Labs/reya-python-sdk/blob/37450ccb2babc99398d1ac1290d48860a9a2e2fa/examples/websocket/perps/prices_monitoring.py) — subscribe to the price stream
* [`examples/websocket/perps/wallet_monitoring.py`](https://github.com/Reya-Labs/reya-python-sdk/blob/37450ccb2babc99398d1ac1290d48860a9a2e2fa/examples/websocket/perps/wallet_monitoring.py) — subscribe to wallet-scoped channels (positions, order changes, executions, balances)
* [`examples/websocket/spot/spot_executions.py`](https://github.com/Reya-Labs/reya-python-sdk/blob/37450ccb2babc99398d1ac1290d48860a9a2e2fa/examples/websocket/spot/spot_executions.py) — subscribe to spot execution streams
* [`examples/websocket/spot/depth_market_maker.py`](https://github.com/Reya-Labs/reya-python-sdk/blob/37450ccb2babc99398d1ac1290d48860a9a2e2fa/examples/websocket/spot/depth_market_maker.py) — bootstrap state via REST, then drive a market-maker loop off of `depth`, `accountBalances`, `openOrders`, and `spotExecutions` updates

After [installing the pinned SDK](/developers/devnet/getting-started/onboarding.md#install-the-pinned-python-sdk), run from its checkout:

```bash
.venv/bin/python -m examples.websocket.perps.market_monitoring  # for example
```

See each script's docstring for prerequisites (`.env` setup, funded test accounts on devnet1).
