Skip to content

WebSocket Overview

Connecting and using WebSocket events

WebSocket endpoint: ws://<node-ip>:21000/events

REST and WebSocket share port 21000. For non-localhost or unsecured LAN deployments, TLS termination via reverse proxy is strongly recommended:

wss://<node-ip>/events


The Sec-WebSocket-Protocol header is required. Connections without it are rejected at upgrade.

Sec-WebSocket-Protocol: blockd-v1

Example using websocat:

Terminal window
websocat --protocol blockd-v1 ws://<node-ip>:21000/events

Authentication is required. The first message sent after connect must be the auth message:

{"auth": {"token": "REPLACE_ME_secret_token"}}

Server response on success:

{"event": "auth", "result": "success"}

On failure:

{"event": "auth", "result": "invalid_key"}

The connection is closed after an invalid key. Any message sent before auth is rejected with an error event.


WebSocket ping/pong operates at the transport level. Incoming Ping frames are automatically responded to by blockd with a Pong.

blockd also sends keepalive Pings to the client at 30 second intervals. Clients should respond with a Pong to maintain the connection. Most WebSocket client libraries handle this automatically.


All server-sent messages share a common envelope. The event field is always present and identifies the message type:

{"event": "<event_name>", ...}

The WebSocket API is not request/response. Subscriptions are fire-and-forget commands. Events arrive asynchronously. Use the query interface to inspect state.


List of all subscriptions:

SubscriptionDescription
server.healthPeriodic server health snapshots, including IBD progress
workflow.progressStep-level progress for running workflows
pubkeys.activityActivity on a tracked wallet’s addresses
block.activityBlock tip changes, including reorg detection
core.hashblockNew block hash from bitcoind
core.hashtxNew transaction hash from bitcoind
core.rawblockRaw block bytes from bitcoind
core.rawtxRaw transaction bytes from bitcoind
core.sequenceMempool and chain sequence events

A subscriptions message contains an array of action objects. Each action object uses either subscribe or unsubscribe as its key. Both types can appear in the same message and are all applied atomically. No acknowledgment is sent.

Subscribe:

{
"subscriptions": [
{"subscribe": "block.activity"},
{"subscribe": "pubkeys.activity", "args": {"pubkeys_set": "testnet/wallet-1"}}
]
}

Unsubscribe:

{
"subscriptions": [
{"unsubscribe": "pubkeys.activity", "args": {"pubkeys_set": "testnet/wallet-2"}}
]
}

Mixed subscribe and unsubscribe in a single message:

{
"subscriptions": [
{"subscribe": "pubkeys.activity", "args": {"pubkeys_set": "testnet/wallet-1"}},
{"unsubscribe": "pubkeys.activity", "args": {"pubkeys_set": "testnet/wallet-2"}}
]
}

A malformed message returns an error event — no actions in a malformed message are applied.

Send an empty subscriptions array to cancel all active subscriptions:

{"subscriptions": []}

Send a query to get current active subscriptions:

{"query": "subscriptions"}

Response:

{
"event": "subscriptions",
"subscriptions": [
{
"subscribe": "server.health",
"args": {"interval": 30, "tip": true, "detailed": true}
}
]
}

A development utility for verifying the connection and auth stack. Send any data and blockd echoes it back as an event:

→ {"echo": {"data": "hello"}}
← {"event": "echo", "echo": {"data": "hello"}}

Useful for confirming the full connection — protocol header, auth, and message handling — is working correctly before implementing real subscriptions.


Error events are sent when a client message fails, or when an asynchronous error occurs during workflow execution or subscription handling.

{
"event": "error",
"error": {
"kind": "subscription",
"message": "unknown topic: core.badtopic"
}
}
FieldTypeDescription
kindstringError category — see kinds table below
reasonstringMachine-readable detail within the kind. Stable snake_case string
messagestringHuman-readable description. Do not parse programmatically

Optional fields are omitted when not applicable.

KindDescription
authorizationAuthentication or permission failure
queryA client query could not be fulfilled
workflowAn async error occurred during workflow execution
subscriptionA subscription message was malformed or referenced an unknown topic
coreError originating from the bitcoind subsystem
indexerError originating from the indexer subsystem
internalUnexpected internal server error

Malformed subscription message:

→ {"subscriptions" [{"subscribe": "core.hashblock"}]}
← {"event": "error", "error": {"kind": "subscription", "message": "invalid message format: expected `:` at line 2 column 19"}}

Async workflow failure:

{
"event": "error",
"error": {
"kind": "workflow",
"reason": "workflow_failed",
"message": "indexer unavailable"
}
}

client blockd
| |
|--- WS upgrade |
| Sec-WebSocket-Protocol: |
| blockd-v1 ---------------------->|
|<-- 101 Switching Protocols ---------|
| Sec-WebSocket-Protocol: |
| blockd-v1 -----------------------|
| |
|--- {"auth":{"token":"..."}} ------->|
|<-- {"event":"auth", |
| "result":"success"} ------------|
| |
|--- {"subscriptions":[...]} -------->|
| |
|<-- events... -----------------------|