Connection & Lifecycle
Endpoints
| Endpoint | Description |
|---|---|
ws://host:8443/v1/ws | Primary WebSocket endpoint |
ws://host:8443/ | Alias for /v1/ws |
Optional query parameter: ?resume_from=<seq> to resume the stream (see Reliability).
Connection Lifecycle
Client Server
│ │
│ WebSocket upgrade (?resume_from=N) │
├──────────────────────────────────────────────►│
│ │
│ [replay if resume_from is valid] │
│◄──────────────────────────────────────────────┤ frames 1..N
│ │
│ Subscribe {"subscribe": [...]} │
├──────────────────────────────────────────────►│ required
│ │
│ Hello {protocol_version, limits, ...} │
│◄──────────────────────────────────────────────┤ before first Subscribe response
│ │
│ [current state snapshot] │
│◄──────────────────────────────────────────────┤ TPS, contention, lifecycle
│ │
│ [live events] │
│◄──────────────────────────────────────────────┤ continuous stream
│ │
│ Subscribe {"subscribe": [...]} │
├──────────────────────────────────────────────►│ update (unlimited)
│ │
│ Ping (client-initiated) │
├──────────────────────────────────────────────►│ keeps connection alive
│ Pong │
│◄──────────────────────────────────────────────┤
│ │
│ Close │
├──────────────────────────────────────────────►│
Key points:
- Hello is sent before the first Subscribe response, not immediately upon connection
- No events are sent until a Subscribe message is received
- Each new Subscribe fully replaces the previous subscription
Hello Frame
{
"seq": 0,
"Hello": {
"protocol_version": 1,
"server_version": "0.2.0",
"chain_id": 10143,
"block_number": 59400000,
"available_filters": ["txn_index", "log_index", "address", "topics", "sender", "to", "function_selector"],
"blocked_events": ["AccountAccess", "StorageAccess", "RecordError", "EvmError"],
"limits": {
"backfill_events": 100000,
"buffer_per_client": 4096,
"slow_off_limit": 10000,
"heartbeat_interval": 30,
"heartbeat_timeout": 60,
"max_subscribes": 3
}
}
}
| Field | Type | Description |
|---|---|---|
protocol_version | u32 | Protocol version (current: 1). The server logs a warning if the client sends a different version, but continues operating (forward-compatible). |
server_version | string | Server version (semver) |
chain_id | u64 | Network ID (e.g. 10143 = Monad Devnet) |
block_number | u64 | Latest block number seen by the server at the time of Hello |
available_filters | string[] | Supported field filter names for advanced subscriptions |
blocked_events | string[] | Event types suppressed by the server's blacklist. These events will not be delivered regardless of your subscription. |
limits.backfill_events | usize | Number of entries in the resume ring buffer (default: 100,000). This is how many past messages the server can replay on reconnect. See Reliability. |
limits.buffer_per_client | usize | Per-client outbound send queue capacity (default: 4,096 messages). When full, new messages are dropped. See Reliability — Backpressure. |
limits.slow_off_limit | u64 | Maximum cumulative message drops before the server disconnects the client (default: 10,000). |
limits.heartbeat_interval | u64 | How often the server checks for client activity (seconds). |
limits.heartbeat_timeout | u64 | Maximum seconds without any client activity before disconnect. |
limits.max_subscribes | usize | Maximum number of items (event types + metrics) allowed in a single subscribe message. 0 = unlimited. |
Note: The Hello frame uses
seq: 0. This is a control message; clients must not update their resume cursor fromseq: 0frames. Only trackseq > 0for?resume_from=.
Heartbeat & Liveness
Unlike typical WebSocket servers, the Monad Events Stream does not send Ping frames. Instead, it uses a timeout-based liveness model:
- Every
heartbeat_intervalseconds (default: 30s), the server checks when the last client activity was received - If more than
heartbeat_timeoutseconds (default: 60s) have elapsed without any activity from the client, the connection is closed - "Activity" includes: any text message, Ping frame, Pong frame
What this means for clients:
- Your WebSocket client must send periodic Ping frames or messages to stay alive
- Most WebSocket libraries handle Ping/Pong automatically at the transport layer
- If you're using a raw WebSocket implementation, send a Ping every ~25 seconds
- Subscribing to high-frequency events (e.g.,
BlockStart) already generates enough server-to-client traffic, but the server only tracks client-to-server activity