Skip to main content

Events

ChainGate provides real-time WebSocket event endpoints so you can follow the chain as it happens — new blocks, balance changes, and the activity of any address you care about — without polling. One API key, a single wss:// connection, and you receive events the moment a block is finalized.

Endpoints

There are two families of event endpoints, one for UTXO chains and one for EVM chains:

wss://api.chaingate.dev/utxo/{network}/events?api_key=YOUR_API_KEY
wss://api.chaingate.dev/evm/{network}/events?api_key=YOUR_API_KEY

The API key travels as a query parameter, exactly like the RPC endpoints. No extra headers are required, which means these endpoints work directly from the browser.

Free tier included

Event streams are included with the free API key. Get yours at ChainGate's Dashboard.

Supported networks

Family{network} valuesDetails
UTXObitcoin, bitcoincash, litecoin, dogecoin, bitcointestnetUTXO events →
EVMethereum, avalancheEVM events →

Connect

// Browser or Node (with the `ws` package)
const ws = new WebSocket('wss://api.chaingate.dev/evm/ethereum/events?api_key=YOUR_API_KEY')

ws.onopen = () => {
ws.send(JSON.stringify({ action: 'subscribe', channel: 'blocks' }))
}

ws.onmessage = (msg) => {
const event = JSON.parse(msg.data)
console.log(event.type, event)
}

Subscribe & unsubscribe

Send one JSON object per message. A single connection can hold many subscriptions across different channels and addresses.

FieldTypeRequiredDescription
actionstringYes"subscribe" or "unsubscribe".
channelstringYesOne of the channels below.
addressstringFor address-scoped channelsThe address to follow — a normal address for the network (e.g. bc1q..., 0x...).
pendingbooleanNoOn balance / history: follow pending (unconfirmed) state instead of confirmed.
fullbooleanNoOn blocks: receive the entire block instead of a summary.

Channels

ChannelAddress?FlagsWhat you receive
blocksNofullNew block summaries — or full blocks with full: true.
balanceYespendingConfirmed balance — or the pending delta with pending: true.
historyYespendingConfirmed transaction activity — or pending txs with pending: true.
mempoolNoEvery pending transaction, as it is seen.
contract_interactionsYesNew contracts an address interacts with. (EVM only.)
{ "action": "subscribe", "channel": "blocks" }
{ "action": "subscribe", "channel": "blocks", "full": true }
{ "action": "subscribe", "channel": "balance", "address": "0xa1b2c3...abc" }
{ "action": "subscribe", "channel": "balance", "address": "0xa1b2c3...abc", "pending": true }
{ "action": "unsubscribe", "channel": "balance", "address": "0xa1b2c3...abc" }
One channel, confirmed or pending

Confirmed and pending data share a channel: subscribe to balance for the confirmed balance, or add pending: true for the pending delta — there is no separate channel to remember. The same applies to history, and to blocks with full: true.

Acknowledgements

Every subscribe/unsubscribe is answered with an acknowledgement that echoes your channel, any flags, and the address:

{ "type": "subscribed", "channel": "balance", "pending": true, "address": "0xa1b2c3...abc" }
FieldTypeDescription
typestring"subscribed" or "unsubscribed".
channelstringThe channel echoed back.
pending / fullbooleanEchoed back when you sent them.
addressstringThe address echoed back for address-scoped channels; omitted for whole-chain channels (blocks, mempool).

Errors

A bad request — malformed JSON, an unknown channel, or a missing/invalid address — is answered with an error frame. The connection stays open, so one bad subscription never interrupts your other subscriptions:

{ "type": "error", "message": "missing or invalid 'address' for this channel" }

Channels at a glance

Both families share the same protocol and the same channels. The only channel that is EVM-specific is contract_interactions — UTXO chains have no contracts.

ChannelFlagsUTXOEVM
blocksfull
balancepending
historypending
mempool
contract_interactions

Shared events use the same field names across both families. Where the underlying data differs by chain — amounts are satoshis on UTXO and wei (as a decimal string) on EVM, and the full-block and pending payloads follow each chain's transaction model — the per-network pages call it out.

Delivery & ordering

  • Events are emitted after a block is finalized, so a follow-up read through the SDK or REST API already reflects what an event told you.
  • Within a block you receive the block event first, then per-address events.
  • Treat the most recent event as the source of truth — if you ever fall behind, reconnect and re-read current state.

Heartbeat & reconnect

The server sends a ping roughly every 30 seconds and closes the connection if it hears nothing back for about 75 seconds — standard WebSocket clients answer these automatically. A connection may also be dropped if a client reads too slowly to keep up with the stream.

You can also keep the connection warm yourself by sending an application-level ping at any time — useful in the browser, where the WebSocket API cannot send protocol-level pings. The server replies immediately and the upstream is left untouched:

// → you send
{ "action": "ping" }
// ← server replies
{ "type": "pong" }

When a connection does drop: reconnect, re-send your subscriptions, and re-read current state (balances, history) through the SDK or REST API to catch up on anything you may have missed.

Reconnect automatically
function connect() {
const ws = new WebSocket('wss://api.chaingate.dev/evm/ethereum/events?api_key=YOUR_API_KEY')
ws.onopen = () => ws.send(JSON.stringify({ action: 'subscribe', channel: 'blocks' }))
ws.onclose = () => setTimeout(connect, 1000) // re-connect, then re-subscribe
return ws
}
connect()