QubicTypeScript
Packages

@qubic.org/bob

Live Qubic node client with WebSocket subscriptions, automatic reconnection, and catch-up buffering.

Introduction

Why a persistent connection?

The Qubic network produces a new tick roughly every second. Polling getTickInfo over HTTP works, but a WebSocket subscription is more efficient — one round-trip to subscribe, then zero-overhead push for every tick. @qubic.org/bob manages that connection lifecycle for you.

Topics

Bob supports two subscription topics:

TopicWhat it delivers
"tickInfo"Every new tick — number, epoch, duration, and initial tick
"eventLogs"Account event logs for a specific identity — transfers, asset changes, contract messages

Automatic reconnection and resubscription

Bob reconnects automatically after a connection drop. With autoResubscribe: true, all active subscriptions are re-registered on reconnect — you don't call subscribe again. Without it, listen to the "connect" event and re-subscribe manually.

Catch-up buffering

After a reconnect, the server may send buffered ticks that arrived during the gap. The meta.isCatchUp flag in the "tickInfo" callback identifies these so you can skip or process them separately.


Installation

bun add @qubic.org/bob
npm install @qubic.org/bob
pnpm add @qubic.org/bob

API reference

PageWhat's covered
ConnectioncreateBobClient, connect, disconnect, autoResubscribe, events
Subscriptionssubscribe("tickInfo"), subscribe("eventLogs"), unsubscribeAll

All exports

import {
  createBobClient,
} from "@qubic.org/bob"

const bob = createBobClient({
  autoResubscribe: true,
  reconnectDelayMs: 1000,
})

await bob.connect()

Examples

Subscribe to live tick updates

import { createBobClient } from "@qubic.org/bob"

const bob = createBobClient({ autoResubscribe: true })
await bob.connect()

bob.subscribe("tickInfo", (tick, meta) => {
  if (meta.isCatchUp) return // skip buffered ticks from reconnect gap

  console.log(`Tick ${tick.tick} (epoch ${tick.epoch}, ${tick.duration}ms)`)
})

process.on("SIGINT", async () => {
  await bob.disconnect()
  process.exit(0)
})

Subscribe to account event logs

import { createBobClient } from "@qubic.org/bob"
import { toIdentity } from "@qubic.org/types"

const bob = createBobClient({ autoResubscribe: true })
await bob.connect()

const identity = toIdentity("CFBMEMZOIDEXQAUXYYSZIURADQLAPWPMNJXQSNVQZAHYVOPYUKKJBJUCTVJL")

bob.subscribe("eventLogs", identity, (events) => {
  for (const event of events) {
    console.log(`Event type ${event.eventType} at tick ${event.tick}`)
    console.log(`TX: ${event.txId}`)
  }
})

React to lifecycle events

import { createBobClient } from "@qubic.org/bob"

const bob = createBobClient()

bob.on("connect",    ()         => console.log("Connected"))
bob.on("disconnect", ({ code }) => console.log("Disconnected:", code))
bob.on("reconnect",  ({ attempt }) => console.log("Reconnecting, attempt", attempt))
bob.on("error",      (err)     => console.error("Error:", err.message))

await bob.connect()

Watch multiple identities

import { createBobClient } from "@qubic.org/bob"
import { toIdentity } from "@qubic.org/types"

const bob = createBobClient({ autoResubscribe: true })
await bob.connect()

const watchlist = [
  toIdentity("CFBMEMZOIDEX..."),
  toIdentity("ANOTHERIDENT..."),
]

for (const identity of watchlist) {
  bob.subscribe("eventLogs", identity, (events) => {
    handleEvents(identity, events)
  })
}

On this page