Streams deliver real-time blockchain events to your webhook. Define filters for the on-chain activity you care about — transfers, contract calls, deployments, token events — and Second Layer pushes matching events to your endpoint as each block is processed.

Delivery is at-least-once. Handlers should be idempotent.


Getting started

Create a stream via the SDK. You'll get back a webhook secret for verifying deliveries.

import { SecondLayer } from "@secondlayer/sdk"

const sl = new SecondLayer({ apiKey: "sk-sl_..." })

const { stream, webhookSecret } = await sl.streams.create({
  name: "my-stream",
  webhookUrl: "https://example.com/webhook",
  filters: [
    { type: "stx_transfer" },
  ],
})

Filters

Each stream takes an array of filters. A block matches if any filter matches. Filters narrow by type and optional fields like contract, sender, recipient, or amount.

filters: [
  // STX transfers over 1 STX
  { type: "stx_transfer", minAmount: 1_000_000 },

  // Calls to a specific contract function
  {
    type: "contract_call",
    contractId: "SP1234...::marketplace",
    functionName: "list-asset",
  },

  // NFT mints from a specific collection
  {
    type: "nft_mint",
    assetIdentifier: "SP1234...::my-nft::nft-token",
  },

  // Contract deployments by a specific address
  { type: "contract_deploy", deployer: "SP1234..." },

  // Print events matching a topic
  { type: "print_event", contractId: "SP1234...::token", topic: "transfer" },
]
Filter types
stx_transfersender, recipient, minAmount, maxAmount
stx_mintrecipient, minAmount
stx_burnsender, minAmount
stx_locklockedAddress, minAmount
ft_transfersender, recipient, assetIdentifier, minAmount
ft_mintrecipient, assetIdentifier, minAmount
ft_burnsender, assetIdentifier, minAmount
nft_transfersender, recipient, assetIdentifier, tokenId
nft_mintrecipient, assetIdentifier, tokenId
nft_burnsender, assetIdentifier, tokenId
contract_callcontractId, functionName, caller
contract_deploydeployer, contractName
print_eventcontractId, topic, contains

Webhook payload

Each delivery posts a JSON payload to your webhook URL with the matching block, transactions, and events.

{
  streamId: "uuid",
  streamName: "my-stream",
  block: {
    height: 150000,
    hash: "0x...",
    parentHash: "0x...",
    burnBlockHeight: 800000,
    timestamp: 1710000000,
  },
  matches: {
    transactions: [{
      txId: "0x...",
      type: "stx_transfer",
      sender: "SP1234...",
      status: "success",
      contractId: null,
      functionName: null,
    }],
    events: [{
      txId: "0x...",
      eventIndex: 0,
      type: "stx_transfer",
      data: { ... },
    }],
  },
  isBackfill: false,
  deliveredAt: "2026-03-10T00:00:00Z",
}

Management

Streams can be enabled, disabled, updated, and deleted. Use partial IDs for convenience — the SDK resolves them automatically.

// List streams
const { streams } = await sl.streams.list({ status: "active" })

// Get by ID (supports partial IDs)
const stream = await sl.streams.get("a1b2c3")

// Update
await sl.streams.update("a1b2c3", {
  webhookUrl: "https://new-endpoint.com/webhook",
  filters: [{ type: "stx_transfer", minAmount: 5_000_000 }],
})

// Enable / disable
await sl.streams.enable("a1b2c3")
await sl.streams.disable("a1b2c3")

// Bulk pause / resume all streams
await sl.streams.pauseAll()
await sl.streams.resumeAll()

// Rotate webhook secret
const { secret } = await sl.streams.rotateSecret("a1b2c3")

// Delete
await sl.streams.delete("a1b2c3")

Replay

Replay historical blocks through a stream. The webhook payload includes isBackfill: true for replayed deliveries. Maximum 10,000 blocks per replay request.

// Via SDK — replay blocks 150,000 to 151,000
await sl.streams.replay("a1b2c3", {
  startBlock: 150_000,
  endBlock: 151_000,
})

// Replay failed deliveries
await sl.streams.replayFailed("a1b2c3")

CLI

Manage streams from the command line. The CLI generates config files and registers them with the API.

# Generate a new stream config
sl streams new my-stream

# Register from config file
sl streams register ./my-stream.json

# List / get / delete
sl streams ls
sl streams get a1b2c3
sl streams delete a1b2c3

# View delivery logs
sl streams logs a1b2c3

# Replay a block range
sl streams replay a1b2c3 --start 150000 --end 151000

# Rotate webhook secret
sl streams rotate-secret a1b2c3

Props

CreateStream
namestringrequired
webhookUrlstringrequired
filtersStreamFilter[]required
optionsStreamOptions
startBlocknumber
endBlocknumber
StreamOptions
decodeClarityValuesbooleantrue
includeRawTxbooleanfalse
includeBlockMetadatabooleantrue
rateLimitnumber10 (max 100)
timeoutMsnumber10000 (max 30000)
maxRetriesnumber3 (max 10)
StreamResponse
idstring
status'inactive' | 'active' | 'paused' | 'failed'
totalDeliveriesnumber
failedDeliveriesnumber
lastTriggeredAtstring | null
lastTriggeredBlocknumber | null