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

# WebSocket

> Long-running WebSocket connection with event-driven command hooks for native, SignalR, and Socket.IO protocols

The WebSocket command (Command Type: 1) establishes a long-running WebSocket connection with event-driven command hooks. Supports native, SignalR, and Socket.IO protocols.

## Command Type

**Command Type ID:** 1

## Parameters

| Parameter               | Type                      | Description                                                                                                                                  | Required |
| ----------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| **url**                 | string                    | WebSocket server URL                                                                                                                         | Yes      |
| **ciphers**             | string                    | TLS cipher suites override                                                                                                                   | No       |
| **headers**             | object                    | Custom HTTP headers sent on the WebSocket handshake. Use this to authenticate, e.g. an `Authorization: Bearer <token>` or an API-key header. | No       |
| **heartbeatIntervalMs** | number                    | Interval in ms at which the command re-publishes the current accumulated result as a heartbeat (defaults to 3000)                            | No       |
| **onClose**             | Array of Command          | Commands to run when the connection closes                                                                                                   | No       |
| **onConnect**           | Array of Command          | Commands to run once the connection is established                                                                                           | No       |
| **onError**             | Array of Command          | Commands to run when a socket error occurs                                                                                                   | No       |
| **onInterval**          | WebSocketOnInterval       | Commands to run on a recurring interval while connected                                                                                      | No       |
| **onMessage**           | Array of Command          | Commands to run for each incoming message (raw message placed in `data`)                                                                     | No       |
| **protocol**            | string                    | Protocol variant: `"native"`, `"signalr"`, `"socketio"` (defaults to `"native"`)                                                             | No       |
| **proxy**               | ProxySettings             | Proxy settings for the connection                                                                                                            | No       |
| **signalr**             | WebSocketSignalRSettings  | Configuration for SignalR protocol                                                                                                           | No       |
| **socketio**            | WebSocketSocketIOSettings | Configuration for Socket.IO protocol                                                                                                         | No       |

### Stop On

These connection stop conditions are handled automatically by the platform. They are **not blueprint parameters** — you cannot configure them in `params`, and there is no `stopOn` object in saved blueprints.

| Condition              | What happens                                                                                                                                 |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **Inactive**           | After the platform max lifetime (default 3 hours), the connection is recycled: the socket closes and a new job reconnects for the same item. |
| **Project Deactivate** | When the project is deactivated, all in-flight WebSocket jobs are cancelled and their sockets are closed.                                    |
| **Item Deleted**       | When an item is cancelled or deleted upstream, its WebSocket job is disposed and the socket is closed.                                       |
| **Server Close**       | When the remote server closes the socket, buffered frames are flushed, the `onClose` hook runs, and the job ends.                            |

Use `onClose` or `onError` hooks to react when a connection ends. An explicit [WebSocket Close](/stream/commands/ws/websocket-close) command in a hook also closes the connection.

### WebSocketOnInterval

| Field          | Type             | Description                                         | Required |
| -------------- | ---------------- | --------------------------------------------------- | -------- |
| **intervalMs** | number           | Interval between command executions in milliseconds | Yes      |
| **commands**   | Array of Command | Commands to execute on each tick                    | Yes      |

### WebSocketSignalRSettings

| Field               | Type            | Description                                                      | Required |
| ------------------- | --------------- | ---------------------------------------------------------------- | -------- |
| **methods**         | Array of string | SignalR hub methods to subscribe to                              | Yes      |
| **skipNegotiation** | boolean         | Skip the SignalR negotiation handshake                           | No       |
| **transport**       | string          | Transport: `"webSockets"`, `"serverSentEvents"`, `"longPolling"` | No       |

### WebSocketSocketIOSettings

| Field      | Type            | Description                      | Required |
| ---------- | --------------- | -------------------------------- | -------- |
| **events** | Array of string | Socket.IO events to subscribe to | Yes      |
| **path**   | string          | Socket.IO server path            | No       |

## Hook Firing Semantics

Each hook fires at a specific point in the connection lifecycle:

| Hook           | When it fires                                                                                                                           |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **onConnect**  | Once, right after the connection is established and before message processing begins. Use it to send subscription messages.             |
| **onMessage**  | Once per incoming frame. The raw frame is placed in `data` for the hook's commands to parse and publish.                                |
| **onInterval** | On a recurring timer while connected, every `onInterval.intervalMs` milliseconds. Use it for periodic keep-alive or re-subscribe sends. |
| **onClose**    | Once, when the socket closes. Any frames still buffered are processed before the close hook runs.                                       |
| **onError**    | When a socket error occurs. The connection is then torn down.                                                                           |

On startup the command connects, runs `onConnect` a single time, and only then begins processing incoming frames. Frames that arrive while `onConnect` is still running are buffered and processed in order once it completes, so subscription setup always happens before the first message is handled.

## Heartbeat Re-publishing

`heartbeatIntervalMs` controls how often the command re-publishes the current accumulated result, independent of whether new frames have arrived (defaults to 3000 ms). On each tick, if a result has been produced, the latest accumulated result is published again. This is what lets the last (streaming) step emit on a steady cadence even during quiet periods with no new messages. `onInterval` is separate: it runs your own commands (for example a keep-alive send) on its own `intervalMs`, while the heartbeat only re-publishes the current result.

## Frame Model: Snapshots and Deltas

Many live feeds send an initial **snapshot** of the full state followed by a stream of incremental **deltas** that carry only what changed. WebSocket accumulates state across frames for the life of the connection: each `onMessage` run can read and update values carried in the step's state and local variables, so your hook commands can merge the snapshot and subsequent deltas into one complete event before publishing. Because state persists across frames, the streaming step can always publish the current, fully merged event, including on a heartbeat tick when no new delta has arrived.

## Published Output Shape

After your `onMessage` commands run, the step publishes a result whose `data` field holds whatever your transform produced — typically the parsed (and, for streaming steps, fully merged) event. For example, given an incoming frame:

```json theme={null}
{ "eventId": "123", "score": { "home": 1, "away": 0 }, "ts": 1719223456 }
```

an `onMessage` chain that parses and shapes it publishes:

```json theme={null}
{
  "data": {
    "eventId": "123",
    "homeScore": 1,
    "awayScore": 0,
    "updatedAt": 1719223456
  }
}
```

Notes:

* Only the `data` field is meaningful to downstream consumers; shape it with [Parse](/stream/commands/parse) / transform commands to your provider-event format.
* If `data` is `undefined` (nothing was produced for a frame), nothing is published for that frame.
* On a heartbeat tick the **current accumulated** `data` is re-published unchanged, so consumers see a steady cadence even with no new frame.

## Error Handling and Reconnection

Use `onError` to react to a socket error — for example to publish a status/marker or run cleanup commands. When an error occurs, the command runs `onError` and then the connection is torn down; the hook is **not** a place to reconnect.

Reconnection is handled automatically at the job level: a dropped connection ends the job and the item is rescheduled/retried, which re-establishes the connection. You do not (and should not) implement reconnect/retry loops inside `onError`.

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "wss://stream.example.com/feed",
    "onMessage": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.data" } }
    ],
    "onError": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.error" } }
    ]
  }
}
```

## Usage Examples

### Authenticated Handshake (Bearer token / API key)

Most live feeds require credentials at the handshake. Pass them through `headers` (placeholders such as `${secrets.feedToken}` resolve at runtime):

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "wss://stream.example.com/feed",
    "headers": {
      "Authorization": "Bearer ${secrets.feedToken}",
      "X-Api-Key": "${secrets.feedApiKey}"
    },
    "onMessage": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.data" } }
    ]
  }
}
```

Some feeds instead expect the token in the URL query string or in a subscribe message sent from `onConnect` (via [WebSocket Send](/stream/commands/ws/websocket-send)) — check the provider's docs for which the feed uses.

### Native WebSocket with onMessage Hook

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "wss://stream.example.com/feed",
    "onConnect": [
      { "id": 3, "type": "WS", "params": { "sendMessage": "{\"type\":\"subscribe\"}" } }
    ],
    "onMessage": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.data" } }
    ]
  }
}
```

### SignalR Connection

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "https://hub.example.com/signalr",
    "protocol": "signalr",
    "signalr": {
      "methods": ["ReceiveOdds", "ReceiveEvent"]
    },
    "onMessage": [
      { "id": 6, "type": "PARSE", "params": { "path": "$.arguments[0]" } }
    ]
  }
}
```

SignalR delivers each hub message as an envelope of the form `{ "type", "target", "arguments" }`, where `arguments` is the array of values the hub method was invoked with. Parse the payload from `$.arguments[0]` (or the relevant index), not a top-level `data` field.

### Socket.IO Connection

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "https://io.example.com",
    "protocol": "socketio",
    "socketio": {
      "events": ["odds", "event"],
      "path": "/socket.io"
    },
    "onMessage": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.data" } }
    ]
  }
}
```

### With Heartbeat Interval

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "wss://stream.example.com/feed",
    "heartbeatIntervalMs": 5000,
    "onInterval": {
      "intervalMs": 30000,
      "commands": [
        { "id": 3, "type": "WS", "params": { "sendMessage": "ping" } }
      ]
    }
  }
}
```

## Worked Example: Sports, Events, Event Data

A typical live feed is collected in three steps that walk from a broad list down to per-event streams. The first two are **discovery** steps that open the socket, capture a one-time snapshot, and close. The final **streaming** step keeps the socket open and publishes on each heartbeat.

### Step 1: Sports (discovery)

Opens the socket, subscribes to the sports list, parses the snapshot, then closes with the [WebSocket Close](/stream/commands/ws/websocket-close) command (Type 9). The list of sports is published once, and each sport becomes an item for the next step.

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "wss://feed.example.com/stream",
    "protocol": "native",
    "onConnect": [
      { "id": 3, "type": "WS", "params": { "sendMessage": "{\"subscribe\":\"sports\"}" } }
    ],
    "onMessage": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.sports" } },
      { "id": 9, "type": "WS", "params": {} }
    ],
    "onClose": [],
    "onError": []
  }
}
```

### Step 2: Events (discovery)

Runs once per sport. It subscribes to that sport's events, grabs the events snapshot, then closes. The events are published once and fan out into items for the final step.

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "wss://feed.example.com/stream",
    "protocol": "native",
    "onConnect": [
      { "id": 3, "type": "WS", "params": { "sendMessage": "{\"subscribe\":\"events\",\"sport\":\"${state.sportId}\"}" } }
    ],
    "onMessage": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.events" } },
      { "id": 9, "type": "WS", "params": {} }
    ],
    "onClose": [],
    "onError": []
  }
}
```

### Step 3: Event Data (streaming, last step)

Runs once per event and keeps the socket open. The first frame is a full snapshot; later frames are deltas merged into the accumulated event state. Every `heartbeatIntervalMs` the current merged event is re-published, so downstream consumers receive a steady cadence even when no new frame has arrived.

```json theme={null}
{
  "command": "websocket",
  "params": {
    "url": "wss://feed.example.com/stream",
    "protocol": "native",
    "heartbeatIntervalMs": 3000,
    "onConnect": [
      { "id": 3, "type": "WS", "params": { "sendMessage": "{\"subscribe\":\"event\",\"id\":\"${state.eventId}\"}" } }
    ],
    "onMessage": [
      { "id": 1002, "type": "PARSE", "params": { "path": "$.update" } }
    ],
    "onClose": [],
    "onError": []
  }
}
```

## Related Commands

* [WebSocket Send](/stream/commands/ws/websocket-send) - Send within onConnect/onMessage hooks
* [Decode](/stream/commands/parse/decode) - Decode compressed payloads (parse command, usable in WS hooks)
* [WebSocket Close](/stream/commands/ws/websocket-close) - Terminate the connection
