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

# WebSocket Protocol

> Lighter-native WebSocket protocol, channel mapping, and authenticated subscriptions

Each Lighter deployment advertises its native WebSocket endpoint through `Provider.wsUrl`: `lighter` uses the mainnet stream and `lighter-rh` uses the Robinhood stream. The same `lighterWsProvider()` factory binds to that provider-specific stream URL. For `lighter-rh`, also pass `LIGHTER_RH_REST_URL` so address-to-account-index lookups use the matching deployment. If you're using normalized SDK channels, see [Streaming](/sdk/streaming).

## SDK Channel Mapping

The SDK's `LighterWsProvider` maps between normalized channel names and Lighter-native channels:

| SDK channel      | Lighter channel                                             | Auth required | SDK event type                              |
| ---------------- | ----------------------------------------------------------- | ------------- | ------------------------------------------- |
| `marketsContext` | `market_stats/all`, `spot_market_stats/all`                 | No            | `Record<string, MarketContext>`             |
| `marketContext`  | `market_stats/<market_id>`, `spot_market_stats/<market_id>` | No            | `MarketContext`                             |
| `orderbook`      | `order_book/<market_id>`                                    | No            | `OrderbookResponse`                         |
| `trades`         | `trade/<market_id>`                                         | No            | `Trade[]`                                   |
| `orderUpdates`   | `account_all_orders/<account_index>`                        | **Yes**       | `{ openOrders, triggerOrders, terminated }` |
| `fills`          | `account_all_trades/<account_index>`                        | No            | `Fill[]`                                    |
| `positions`      | `account_all_positions/<account_index>`                     | **Yes**       | `Position[]`                                |
| `accountSummary` | `user_stats/<account_index>`                                | No            | `AccountSummary`                            |

Lighter does **not** expose a live OHLC channel. The SDK returns a no-op unsubscribe for `candle` subscriptions so chart UIs (rendering from REST history + price ticks) are not disrupted by a thrown error on every mount.

## Authenticated channels

`orderUpdates` and `positions` require a Lighter auth token on every subscribe and reconnect. `fills` and `accountSummary` are publicly readable. When the matching REST plugin is registered on the same SDK client, the WebSocket provider automatically calls that plugin's `resolveAuthToken`; no token callback or separate key store is needed.

```typescript theme={null}
import { createPerpsClient, PerpsWsClient } from '@lifi/perps-sdk';
import {
  lighterProvider,
  lighterRhProvider,
  LIGHTER_RH_REST_URL,
  lighterWsProvider,
} from '@lifi/perps-sdk-provider-lighter';

const client = createPerpsClient({
  integrator: 'my-app',
  providers: [lighterProvider(), lighterRhProvider()],
});

const ws = new PerpsWsClient(client, {
  wsProviders: {
    lighter: lighterWsProvider(),
    'lighter-rh': lighterWsProvider({ restUrl: LIGHTER_RH_REST_URL }),
  },
});

await ws.subscribe(
  { channel: 'orderUpdates', dex: 'lighter-rh', address: userAddress },
  (event) => console.log(event.data),
);
```

Pass `resolveAuthToken` to `lighterWsProvider({ resolveAuthToken })` only when constructing a standalone WebSocket client without the corresponding `lighterProvider()` or `lighterRhProvider()` on the same `PerpsSDKClient`. If neither source can produce a token, authenticated subscriptions fail at subscribe time.

## Keepalive

The SDK sends a `ping` frame every **30 seconds** to keep the connection alive.

## Per-subscription `dex` field

When subscribing across providers, the per-subscription field that selects the venue is named `dex` (not `provider`). Use `lighter` for mainnet or `lighter-rh` for the Robinhood deployment.
