Read and watch
List rules and receipts, read a rule's audit trail, follow workers, and get live updates over one WebSocket.
Reading needs no wallet: createTriggers() with no options can read everything.
Rules and their status
const rules = await triggers.listRules({ creator: myAddress })
// each: rule fields + status ('active' | 'fired' | 'failed') + deadline (block) + receiptsA rule with an ok or skipped receipt is fired; one with only failed receipts is failed;
anything else is active. deadline is the block it fires at (for expire rules, from the target's
current expiry).
One rule's audit trail
const trail = await triggers.getRule(key)
// { rule, status, deadline, targetExpiresAt, claims, receipts }claims are the live 60-second leases (a worker is firing it right now); receipts are what happened.
Receipts
await triggers.listReceipts({ creator: myAddress }) // every receipt for your rules, newest first
await triggers.listReceipts({ rule: key }) // one rule
await triggers.listReceipts({ creator, sinceBlock }) // only newer onesWorkers
const workers = await triggers.listWorkers()
// worker, online, mode, actions, lastBlock, version, sealKey, operator, operatorVerifiedLive updates
const stop = triggers.watch({
creator: myAddress,
onReceipt: (r) => {}, // a worker fired one of your rules
onRule: (r) => {}, // you created a rule (from any app)
onHeartbeat: (w) => {}, // a worker appeared or renewed its heartbeat
onStatus: (s) => {}, // 'live' | 'reconnecting'
minIntervalMs: 8000,
})
// later: stop()watch opens one WebSocket. Arkiv's event feed is unfiltered and carries no attributes, so each
burst of events triggers a few small filtered queries ("receipts since the last one I saw") instead
of fetching every new entity. It reconnects by itself, once per drop, and re-reads on every reconnect
so nothing is missed.
Times and blocks
const clock = await triggers.clock() // { block, timeMs, blockSeconds }
const date = await triggers.toDate(rule.deadline)
const block = await triggers.toBlock(new Date())The pure toDate(block, clock) and toBlock(date, clock) exports do the same without a network call,
which is handy for countdowns.
Reading from a browser
A browser cannot hold an access key, so it reads on Arkiv's anonymous quota. Two habits keep a page light:
- Create the public client with
http(url, { retryCount: 0 }). With retries, a rate-limited read waits outRetry-Afterand looks frozen; without them it fails in about a second withHttpRequestError(details: "ANON_COST_LIMITED"), and you can say so and retry later. - Use
minIntervalMsof 5 to 10 seconds onwatch, and poll the clock rarely (count blocks locally).
import { createPublicClient } from '@arkiv-network/sdk'
import { tiramisu } from '@arkiv-network/sdk/chains'
import { http } from 'viem'
const publicClient = createPublicClient({ chain: tiramisu, transport: http(undefined, { retryCount: 0 }) })
const triggers = createTriggers({ publicClient })See Access keys and quota for a server-side relay that adds a key only when a browser is rate-limited.