Channels / SDK

SDK

Talk to your instance from TypeScript without hand-writing cursor walks, reorg rewind, or signature checks.

Every export, with signatures: SDK reference.

bun add @secondlayer/sdk
import { SecondLayer } from "@secondlayer/sdk";

const sl = new SecondLayer({
  apiKey: process.env.INSTANCE_TOKEN, // from secondlayer init; omit on loopback
  // default: http://127.0.0.1:3800  (or SECONDLAYER_API_URL)
});

Hosted Index, Streams, and archive use accountKey / SECONDLAYER_API_KEY. See Authentication.

  • sl.streams: raw, ordered chain events (cursor-paginated, replayable).
  • sl.index: decoded rows: FT/NFT transfers, all event types, contract calls, and printSchema(contractId).
  • sl.contracts: find deployed contracts by trait (SIP-009/010/013).
  • sl.subgraphs: your app-specific tables, plus open /v1 reads (rows).
  • sl.webhooks: create and manage webhook webhooks (subgraph rows or raw chain events).
const tip = await sl.streams.tip();
const page = await sl.streams.events.list({
  types: ["ft_transfer"],
  contractId: "SP000000000000000000002Q6VF78.sbtc-token",
  limit: 10,
});

Each surface documents its own calls:

TaskCallDocumented in
Webhook on raw chain eventssl.webhooks.create({ name, url, triggers })Webhooks
Re-deliver historysl.webhooks.replay(id, { fromBlock, toBlock })Replay
Check a delivery signatureverifyWebhookSignature, verifySecondlayerSignatureReceiving deliveries
Prove a tx is in a blockverifyTransactionProof, fetchRewardSetVerification
Tail the live firehosesl.streams.events.subscribe(opts)Streams
Read subgraph tablessl.subgraphs.rows, sl.subgraphs.typedSubgraphs
Stream or roll up rowssubgraph.<table>.subscribe, .aggregateReading rows
Let a sink own the transactionkyselySink, drizzleSink, bunSqliteSinkSinks

trigger.* is not on.*

trigger.* (from @secondlayer/sdk) configures chain webhooks. on.* (from @secondlayer/stacks) configures subgraph sources in a handler config. They configure different things. @secondlayer/stacks is the typed chain client, documented at stacks.secondlayer.tools.

consume polls and commits a cursor so you never miss or double-process. Return the committed cursor from onBatch. Available on sl.streams.events, sl.index.events (takes eventType), and sl.index.contractCalls.

await sl.index.contractCalls.consume({
  contractId: "SP...marketplace-v4",
  functionName: "purchase-asset",
  fromCursor: await loadCheckpoint(), // null on first run
  fromHeight: 0,                      // first run: backfill as far as the instance holds
  onBatch: async (calls, envelope, ctx) => {
    await commitRowsAndCheckpoint(calls, ctx.cursor);
    return ctx.cursor;
  },
  onReorg: async (reorg) => {
    await rollbackFromHeight(reorg.fork_point_height); // inclusive of the fork block
  },
});

Reorgs rewind the cursor to the fork point and fire onReorg. Fold running balances in a sink's onRollback, not there: Sinks. finalizedOnly holds delivery to rows at or below tip.finalized_height, and fromHeight: 0 backfills as far as your instance holds (archive restore is metered past that). Full options and the batch envelope: Index.

createStreamsClient({ apiKey }) verifies both REST reads (X-Signature) and SSE frames. The key is fetched once from /public/streams/signing-key; a rotated X-Signature-KeyId triggers a single refresh.

verifyBehavior
default (lenient)Verify signed responses; pass unsigned through; throw on invalid
true (strict)A missing signature throws too
{ publicKey }Pin a known PEM
falseDisable verification

verify lives on createStreamsClient

new SecondLayer() does not accept verify; use createStreamsClient when you need it.