Subscriptions
Push instead of poll. A subscription binds to a subgraph table — every row your handler writes that matches your filter fires a signed, retried webhook. Point it at Discord, Slack, Trigger.dev, or your own backend — anything that speaks HTTP.
For pull semantics, see Streams.
How it works
push, not poll
On each matching write the row lands in an outbox; a delivery worker signs it and POSTs to your URL, retrying with backoff until it lands. No polling loop to run, no cursor to track.
Subscribe
Scaffold a receiver for your runtime, or create one programmatically against a table you own:
# Scaffold a receiver — runtimes: inngest | trigger | cloudflare | node
sl subscriptions create whale-alerts --runtime node
# Or via the SDK
await sdk.subscriptions.create({
name: "whale-alerts",
subgraphName: "token-transfers",
tableName: "transfers",
url: "https://my-app.com/webhook",
filter: { recipient: "SP1ABC...", amount: { gte: "1000000" } },
format: "standard-webhooks",
});Filters are { column: value } maps with operators (eq, gte, in, …). Typed on.* factories — on.sip010Transfer, on.sbtcDeposit, on.poxStack and more — build common specs for you.
Delivery
Deliveries are signed with standard-webhooks (HMAC-SHA256) by default. Failures back off and, if they keep failing, land in a DLQ — and you can replay any block range.
# Headers on every delivery (standard-webhooks):
webhook-id: msg_<id>
webhook-timestamp: <unix-seconds>
webhook-signature: v1,<base64-hmac>
# Retries: 30s → 2m → 10m → 1h → 6h → 24h → 72h, then DLQ.
# Replay a range:
# await sdk.subscriptions.replay(id, { fromBlock, toBlock })