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

# Overview

> Real-time contracts, quotes, order books, trades, positions, account state, and order management for Rails Options.

export const url_0 = "wss://ws.sandbox.rails.xyz?product=options&market=BTC-USDT&expiration=24JUN26&strike=62000&type=C&subscriptions=optionsOrders,optionsContracts,optionsQuotes,optionsPublicOrderBook,optionsPublicTrades,optionsPositions,optionsOpenOrders,optionsRecentOrders,optionsSettlements,optionsAccountSummary&sessionId=d785d51c-054e-4e33-9738-92d04ede9fab"

The Rails WebSocket API provides real-time contracts, quotes, order books, trades, positions, and account state, and is used to place and manage orders — with the guarantees required by professional trading systems.

It is designed to remain predictable, secure, and low-latency even during periods of extreme market activity.

The API adopts a subscription model that gives you control over which real-time streams your connection receives. Streams can be requested at connection time via query parameters, or added and removed afterwards using explicit requests.

#### URL

<pre>
  <code>
    {url_0}
  </code>
</pre>

***

#### Authorizations

See [Get Access Token](/latest/get-access-token) on how to retrieve the token. Set `Sec-WebSocket-Protocol` during the handshake or on `connect()`.

<ParamField header="Sec-WebSocket-Protocol" required type="string">
  Example value (authorization token):

  ```
  authorization#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdGFnaW5nLmZ1bmdpYmxlLnh5eiIsInN1YiI6ImQxM2I1MzBmLWFmNzMtNDBmOS04ZjhlLWVkNzk1OTU3YTU3ZiIsImF1ZCI6WyJzdGFnaW5nLWFwcC5mdW5naWJsZS54eXoiXSwiZXhwIjoxNzAzMzA1NzQwLCJpYXQiOjE3MDMzMDM5NDB9.vsHe4G_yEkRfz8XNoTKcX83udA-LUysWD4q80wfCC8k
  ```
</ParamField>

#### Query Parameters

<ParamField query="product" required type="string">
  Always `options` for this API.
</ParamField>

<ParamField query="market" required type="string">
  Market name. Example value: `BTC-USDT`. To connect across every contract on every market, use the value `ALL`.
</ParamField>

<ParamField query="expiration" type="string">
  Expiration of the contracts to filter on. Day, three-letter month, two-digit year — e.g. `24JUN26`, `1JUN26`, `31DEC26`. Omit to match every expiration for the market.
</ParamField>

<ParamField query="strike" type="string">
  Strike price of the contracts to filter on — e.g. `62000`. Must be a positive number. Requires `expiration`. Omit to match every strike.
</ParamField>

<ParamField query="type" type="string">
  Option type: `C` (call) or `P` (put). Requires `strike`. Omit to match both calls and puts.
</ParamField>

<ParamField query="subscriptions" type="string">
  Comma-separated list of streams to subscribe on connect — e.g. `optionsQuotes,optionsPositions`. Any of the [Supported Streams](/latest/options/websocket-api/common-requests#supported-streams) may be listed. Defaults to `optionsOrders` if omitted.
</ParamField>

<ParamField query="sessionId" type="string">
  UUID identifying a session. Enables [WebSocket Session](/latest/websocket-session) on the [Order Creation Stream](/latest/options/websocket-api/order-creation-stream) (subscription type `optionsOrders`). Omit to opt out.
</ParamField>

#### Contract Filter

The `market`, `expiration`, `strike`, and `type` query parameters compose into a contract filter that scopes the connection:

| Query                                                    | Matches                                   |
| -------------------------------------------------------- | ----------------------------------------- |
| `market=BTC-USDT&expiration=24JUN26&strike=62000&type=C` | The single contract `BTC-24JUN26-62000-C` |
| `market=BTC-USDT&expiration=24JUN26&strike=62000`        | The call and the put at that strike       |
| `market=BTC-USDT&expiration=24JUN26`                     | Every BTC contract expiring 24JUN26       |
| `market=BTC-USDT`                                        | Every BTC contract                        |
| `market=ALL`                                             | Every contract on every underlying        |

The filter narrows left to right, and each level requires the one before it: `expiration` requires `market`, `strike` requires `expiration`, and `type` requires `strike`. Each may be omitted to widen the match; a parameter sent with a blank value is treated as omitted. `market` takes the market only — the rest of the contract goes in `expiration`, `strike`, and `type`.

An invalid filter is reported with one of these error slugs:

| Error                   | Meaning                                                                                                    |
| ----------------------- | ---------------------------------------------------------------------------------------------------------- |
| `market_required`       | An `expiration`, `strike`, or `type` was sent without a `market`                                           |
| `invalid_market`        | `market` is `ALL`, a packed contract name, or an otherwise unrecognized market, while a refiner is present |
| `legacy_options_market` | `market` is a packed contract (`BTC-24JUN26-62000-C`) or a contract prefix (`BTC-24JUN26`)                 |
| `missing_expiration`    | `strike` was sent without `expiration`                                                                     |
| `missing_strike`        | `type` was sent without `strike`                                                                           |
| `invalid_expiration`    | `expiration` does not parse (expected day, three-letter month, two-digit year — e.g. `24JUN26`)            |
| `invalid_strike`        | `strike` is not a positive number                                                                          |
| `invalid_type`          | `type` is not `C` or `P`                                                                                   |

At connect time, all of them reject the WebSocket handshake with `400 Bad Request: Invalid filter parameters provided` — except a wholly missing `market`, which returns `400 Bad Request: Missing required market parameter` instead. No connection is established, so the slug is not delivered on the socket. The same filter composes on `subscribe` and `unsubscribe` requests, where an invalid one is answered with a `400` error message carrying the slug in `data.error` while the connection stays open.

<Note>
  The contract filter applies to the connection and to `subscribe` / `unsubscribe` requests. The `emitOptions*` snapshot requests are different: they take a **packed** contract name in `content.market` (e.g. `BTC-10JUL26-55000-C`, or `ALL`), while `emitOptionsContracts` and `emitOptionsQuotes` take `underlying` and `expirationDate` instead, and `emitOptionsAccountSummary` takes no market at all. When `content.market` is omitted, the connection's own filter is used, and the request can fail with `invalid_market` if that filter does not resolve to a value the request accepts.
</Note>

#### Recommended: Narrow Connection Filters

While the WebSocket API supports connecting with `market=ALL` for convenience, we strongly recommend keeping each connection's contract filter as narrow as possible — one contract, or one expiration, per connection.

**Why?**

Using `market=ALL` means a single WebSocket connection is responsible for all contract data and events. If that connection experiences issues (e.g., network instability, client-side bugs, or resource exhaustion), it can disrupt data for *all* contracts at once. In contrast, narrow connections isolate risk: a problem with one connection will not impact the others.

**Best Practice:**

* Use a narrow filter (e.g. `market=BTC-USDT&expiration=24JUN26&strike=62000&type=C`) to create one connection per contract — or per expiration — you care about.
* Reserve `market=ALL` for development, quick testing, or when you are certain your client can robustly handle all contracts in a single connection.

This approach ensures greater reliability and fault isolation for production trading systems.

#### Request Envelope

All WebSocket requests follow the same top-level structure:

```json theme={null}
{
  "message":"<requestType>",
  "content":{
    "clientRequestId":"<UUID>", // optional
      ...
  }
}
```

* `message` defines the request type
* `content` contains request-specific parameters (can be omitted if no parameters)
* An optional `clientRequestId` field is supported for correlation, and must be a UUID, unique per request

Reusing a `clientRequestId` on `createOrder`, `modifyOrder`, or `cancelOrder` within 60 seconds returns a `409` — the original request stands and is not executed a second time.

```json theme={null}
{
  "resultType": "createOrder",
  "data": {
    "statusCode": 409,
    "clientRequestId": "3e2c3bb4-be3f-4d05-9133-51d3a27b74ef",
    "error": "duplicate clientRequestId, request already accepted"
  }
}
```

`resultType` echoes the `message` of the request that was rejected.

#### Response Envelope

All WebSocket responses and stream messages follow a consistent top-level structure:

```json theme={null}
{
  "resultType": "<responseType>",
  "data": {
    // response-specific fields...
  }
}
```

* `resultType` identifies the type of response or message (e.g., "optionsContracts", "publicOrderBookDelta")
* `data` contains the response or message payload, including a `statusCode`
* Where relevant, `market` appears as a top-level field alongside `resultType` and `data`

#### Dynamic Subscriptions

Streams are added and removed after connecting with explicit `subscribe` and `unsubscribe` requests. Subscriptions are scoped by product: only options streams can be subscribed on a `product=options` connection.

Stream names are validated the same way wherever you send them, and a single bad name rejects the whole request — no subscription is applied. At connect, an unrecognized name in `subscriptions`, or a perpetuals one (`orders`, `trades`, `publicTrades`, `publicOrderBook`), rejects the handshake with `400 Bad Request: Invalid subscription parameter provided`. In a `subscribe` or `unsubscribe` request, an unrecognized name is rejected with `invalid_subscriptions` and a perpetuals one with `wrong_product_subscriptions`.

Every stream follows the connection's contract filter, or the filter composed on the `subscribe` request that added it. The one exception is `optionsAccountSummary`, which is account-wide — any contract filter is ignored for it.

<CardGroup cols={1}>
  <Card title="Common Requests" href="/latest/options/websocket-api/common-requests">
    Ping, subscribe, unsubscribe, and the connection shutdown notice - available even without any subscription
  </Card>
</CardGroup>

#### Supported Streams

Check out their respective documentation for details on the data they provide and any supported requests.

<CardGroup cols={3}>
  <Card title="Order Creation Stream" href="/latest/options/websocket-api/order-creation-stream">
    Create, modify, and cancel orders, with fill notifications
  </Card>

  <Card title="Contracts Stream" href="/latest/options/websocket-api/contracts-stream">
    Contract definitions with live mark price, IV, and Greeks
  </Card>

  <Card title="Quotes Stream" href="/latest/options/websocket-api/quotes-stream">
    Top-of-book bid/ask with implied volatility per contract
  </Card>

  <Card title="Order Book Stream" href="/latest/options/websocket-api/order-book-stream">
    Full order book snapshots and deltas for a contract
  </Card>

  <Card title="Public Trades Stream" href="/latest/options/websocket-api/public-trades-stream">
    Real-time public trade executions for a contract
  </Card>

  <Card title="Positions Stream" href="/latest/options/websocket-api/positions-stream">
    Open positions with mark price, margin, and PnL
  </Card>

  <Card title="Open Orders Stream" href="/latest/options/websocket-api/open-orders-stream">
    Your open orders and incremental updates
  </Card>

  <Card title="Recent Orders Stream" href="/latest/options/websocket-api/recent-orders-stream">
    Recent order history
  </Card>

  <Card title="Account Summary Stream" href="/latest/options/websocket-api/account-summary-stream">
    Balance, margin used, and PnL
  </Card>

  <Card title="Settlements Stream" href="/latest/options/websocket-api/settlements-stream">
    Settlement results at expiry
  </Card>
</CardGroup>

#### Deterministic Delivery

Multiple WebSocket connections with an identical contract filter and identical subscription parameters receive the same sequence of events with no per-connection filtering or divergence.

This enables:

* Multiple parallel connections for redundancy
* Hot standby consumers for failover
* Independent processes handling the same events
* Focused connections for specific stream subsets
