Skip to content
LogoLogo

Watch events

React to new blocks, address activity and asset events without polling.

SourceEndpointNotes
NodeGET /v1/events (Server-Sent Events)chain events from one node; resumable with Last-Event-ID (last 1,000 events kept)
GatewayGET /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.retracted that 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.reset event: 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();
}

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.