Watch events
React to new blocks, address activity and asset events without polling.
| Source | Endpoint | Notes |
|---|---|---|
| Node | GET /v1/events (Server-Sent Events) | chain events from one node; resumable with Last-Event-ID (last 1,000 events kept) |
| Gateway | GET /gateway/v1/stream (SSE) or /gateway/v1/ws (WebSocket) | chain, wallet and issuer events; filters; resumable with a cursor; needs an API key or stream token |
The SDKs expose both as an async iterator, watchEvents({ signal?, lastEventId? }), which
reconnects when the connection drops and resumes from the last event id on both streams. The
node keeps its last 1,000 events; if the gap is older, it sends a stream.reset event and you
resync from the REST API. The gateway keeps a longer, persistent history.
Things to handle
- Reorgs. Every event of an orphaned block is followed by an
event.retractedthat names it. Undo whatever you did for the retracted event. - At-least-once delivery. Deduplicate on the event
id. - Expired cursors. The gateway keeps history for a retention window (7 days by default). An
older cursor gets a
stream.resetevent: rebuild state over REST, then continue. - Browsers can't send API keys on EventSource; exchange the key server-side for a short-lived
stream token (
POST /gateway/v1/stream/tokens).
/**
* Watch live events (async iterator over SSE); resumes from the last event id on reconnect.
*
* ARREKTA_URL=http://127.0.0.1:28332 ARREKTA_ADMIN_TOKEN=... npx tsx examples/watch-events.ts
* # gateway: ARREKTA_GATEWAY_URL=https://… ARREKTA_API_KEY=arrk_… npx tsx examples/watch-events.ts
*/
import { createClient, createGatewayClient, fromHex, getChain, getCoins, keychainFromSeed, mine, sendPayment, watchEvents } from 'arrekta-sdk';
const URL = process.env.ARREKTA_URL ?? 'http://127.0.0.1:28332';
const ADMIN = process.env.ARREKTA_ADMIN_TOKEN;
const SEED = fromHex(process.env.ARREKTA_SEED ?? 'a1'.repeat(32));
// 1. Connect (node, or the gateway with an API key).
const client = process.env.ARREKTA_GATEWAY_URL
? createGatewayClient({ url: process.env.ARREKTA_GATEWAY_URL, apiKey: process.env.ARREKTA_API_KEY })
: createClient({ url: URL, adminToken: ADMIN });
const { network } = await getChain(client);
const wallet = keychainFromSeed(SEED, { network });
const stop = new AbortController();
// 2. Subscribe; every stream starts with `stream.open`.
for await (const event of watchEvents(client, { signal: stop.signal })) {
console.log(`[${event.id ?? '-'}] ${event.type}: ${JSON.stringify(event.data)}`);
// 3. Once live, cause activity: a payment, then a block (regtest).
if (event.type === 'stream.open' && ADMIN) {
if ((await getCoins(client, { wallet })).length === 0) await mine(client, wallet.address(0), 101);
await sendPayment(client, { wallet, to: wallet.address(1), amount: '0.1' });
await mine(client, wallet.address(0), 1);
}
// 4. Stop after the first new block (keep `event.id` to resume later).
if (event.type === 'block_connected') stop.abort();
}"""Watch live events (async iterator over SSE); resumes from the last event id on reconnect.
ARREKTA_URL=http://127.0.0.1:28332 ARREKTA_ADMIN_TOKEN=... python examples/watch_events.py
"""
import asyncio
import os
from arrekta_sdk import AsyncClient, keychain_from_seed, watch_events
URL = os.environ.get("ARREKTA_URL", "http://127.0.0.1:28332")
ADMIN = os.environ.get("ARREKTA_ADMIN_TOKEN")
SEED = bytes.fromhex(os.environ.get("ARREKTA_SEED", "a1" * 32))
async def main() -> None:
# 1. Connect with the async client.
async with AsyncClient(URL, admin_token=ADMIN) as client:
network = (await client.get_chain())["network"]
wallet = keychain_from_seed(SEED, network=network)
stop = asyncio.Event()
# 2. Subscribe; every stream starts with `stream.open`.
async for event in watch_events(client, stop=stop):
print(f"[{event.id}] {event.type}: {event.data}")
# 3. Once live, cause activity: a payment, then a block (regtest).
if event.type == "stream.open" and ADMIN:
if not await client.get_coins(wallet):
await client.mine(wallet.address(0), 101)
await client.send_payment(wallet, wallet.address(1), "0.1")
await client.mine(wallet.address(0), 1)
# 4. Stop after the first new block (keep `event.id` to resume later).
if event.type == "block_connected":
stop.set()
if __name__ == "__main__":
asyncio.run(main())Source: sdk/typescript/examples/watch-events.ts (updated 2026-11-02) · sdk/python/examples/watch_events.py (updated 2026-11-02). Runnable against a regtest node; CI runs every example.
Full event catalogue, filters and the WebSocket protocol: Live event streams.