This tutorial builds a small worker that watches one or more accounts for incoming transactions and prints a summary. It's the foundation for things like payment notifications, audit logs, and on-chain webhook relays.
TypeScriptimport { Client } from 'xrpl' const client = new Client('wss://honeycluster.io') const WATCH: string[] = [ 'rExampleAccountAddressXXXXXXXXXXX', ] async function main() { await client.connect() await subscribe(WATCH) client.on('transaction', handleTransaction) // Re-subscribe after automatic reconnection. client.on('connected', () => subscribe(WATCH)) } main().catch((err) => { console.error(err) process.exit(1) })
TypeScriptasync function subscribe(accounts: string[]) { if (accounts.length === 0) return await client.request({ command: 'subscribe', accounts }) console.log(`watching ${accounts.length} account(s)`) }
Keeping subscribe as a standalone function means the connected listener
can re-send the subscription after reconnects — the upstream doesn't persist
subscriptions across connection drops.
TypeScriptfunction handleTransaction(event: any) { const tx = event.transaction if (!tx) return const amount = typeof tx.Amount === 'string' ? `${Number(tx.Amount) / 1_000_000} XRP` : tx.Amount console.log( new Date(event.engine_result_code ? Date.now() : Date.now()).toISOString(), tx.TransactionType, tx.Account, '→', 'Destination' in tx ? tx.Destination : '—', amount ?? '' ) }
A production worker rarely watches a fixed list. Expose an HTTP endpoint
(or a queue consumer) that calls subscribe / unsubscribe to mutate the
active set:
TypeScriptconst watched = new Set<string>(WATCH) async function watch(address: string) { if (watched.has(address)) return watched.add(address) await client.request({ command: 'subscribe', accounts: [address] }) } async function unwatch(address: string) { if (!watched.delete(address)) return await client.request({ command: 'unsubscribe', accounts: [address] }) }
Persist watched to Redis or Postgres if the worker needs to survive pod
restarts — on recovery, re-hydrate the set and resubscribe before processing
new events.
TypeScriptprocess.on('SIGTERM', async () => { await client.disconnect() process.exit(0) })
Disconnecting cleanly emits a close frame upstream so regional-proxy resources are freed immediately, and lets downstream monitoring systems distinguish graceful shutdowns from crashes.