Subscriptions
Push matched events to your webhook URL. Subscriptions bind to a subgraph table — every row your handler writes that matches the filter triggers a signed HTTP POST. For pull semantics see Streams.
Quickstart
# Scaffold a receiver project; runtimes: inngest | trigger | cloudflare | node
sl create subscription my-watcher --runtime node
# Or programmatically:
import { createClient } from "@secondlayer/sdk";
await createClient(...).subscriptions.create({
name: "my-watcher",
subgraphName: "my-watcher",
tableName: "transfers",
url: "https://my-app.com/webhook",
format: "standard-webhooks",
});Filters
// {column: value} maps. Bare value = eq. Operators: eq, neq, gt, gte, lt, lte, in.
{
filter: {
recipient: "SP1ABC...",
amount: { gte: "1000000" }
}
}on.* factories
import { on } from "@secondlayer/stacks";
// Each factory takes {subgraph, table} first — bind to a table you own.
const spec = on.transferTo(
{ subgraph: "my-watcher", table: "transfers" },
"SP1ABC...",
{ asset: "SP1...usdc::usdc-token" },
);
await sdk.subscriptions.create({ ...spec, name: "watch", url: "https://..." });
// Available: on.transferTo, on.sip010Transfer, on.sip009Transfer,
// on.bnsName, on.poxStack, on.sbtcDeposit, on.sbtcWithdrawalSigning
# Default format: standard-webhooks. Every delivery carries:
webhook-id: msg_<id>
webhook-timestamp: <unix-seconds>
webhook-signature: v1,<base64-hmac>
# signature = HMAC-SHA256("<id>.<timestamp>.<body>", secret)
# Rotate via: sl subscriptions rotate-secret <id>
#
# Other formats: inngest, trigger, cloudflare, cloudevents, rawReplay & DLQ
# Failed deliveries fall back through 30s → 2m → 10m → 1h → 6h → 24h → 72h.
# After 7 attempts they land in the DLQ; 20 consecutive failures pause the sub.
# Inspect at /platform/subgraphs/<name>/subscriptions/<id>.
await sdk.subscriptions.replay(id, { fromBlock: 123000, toBlock: 124000 });