Changelog archive
Releases before July 2026, kept as history of the withdrawn hosted service; keys, tiers, and hosted URLs below no longer apply. Current releases: Changelog.
Build your app index with consume()
Index now ships the loop. index.events.consume() and index.contractCalls.consume() are checkpointed consumers over decoded rows, with no node, decoders, or sync loop to hand-roll.
- Write your rows inside
onBatch, return the cursor you committed, and crash-restart resumes exactly there. - Reorgs rewind the cursor to the fork point automatically.
finalizedOnlyholds delivery to rows at or belowtip.finalized_height(which the Index tip now reports).fromHeight: 0backfills from genesis.- Runnable example:
sales-indexindexes every Gamma marketplace sale into your own Postgres in ~50 lines, then graduates to the same table as a one-file Subgraph.
Empirical print-event schemas
Clarity ABIs don't describe print payloads, and the shape varies per topic within one contract. New GET /v1/index/contracts/:id/print-schema infers each topic's real payload schema from indexed history: sampled print events deserialized from raw Clarity, with exact Clarity types, TS types, and subgraph column types per field.
sl subgraphs create <name> --from-contract <id>scaffolds a fully typed subgraph (per-topicprintsmaps to typedevent.data;--table-per-topicto normalize).codegen --payloadsemits the payload types as a.d.ts.- Deploys now warn when a handler reads a field never observed on-chain.
- SDK:
sl.index.printSchema(). MCP:index_print_schema.
PoX-4 stacking decoded
Index now decodes PoX-4 stacking. stack-stx, delegate-stx, and reward actions are normalized into typed rows at /v1/index/stacking; filter by stacker, caller, or PoX function with no node and no decoders of your own.
Keyless reads, plus pay-as-you-go credits
Index reads need no API key. Anonymous requests work with wildcard CORS, and a free-tier key reads Index too (never slower than anonymous).
- Free and anonymous reads cover the recent 24-hour window; a read into older history returns
402 UPGRADE_REQUIRED. - Top up prepaid credits with a card (
POST /api/billing/topup, packs $10–$100) to read older history without a plan. - A positive balance reads beyond the window, unthrottled, debited $5 per 1,000,000 rows across both Index and Streams from one shared balance, with the prepaid balance as a hard cap.
- Public chain data stays public.
Trait-filtered event queries
Restrict any decoded event type or contract call to a SIP standard with
?trait=. sip-010, sip-009, and sip-013 scope results to conforming
contracts without enumerating every contract id.
Aggregates
Roll up a subgraph table server-side instead of paging every row. The new
/aggregate endpoint runs count, sum, min, max, and distinct counts
over the same filters as the list endpoint.
const stats = await sl.subgraphs.sbtcFlows.transfers.aggregate({
where: { sender: "SP3PE7Q9..." },
sum: ["amount"],
countDistinct: ["recipient"],
});sum/min/max are numeric-only (enforced at compile time) and return
lossless strings; the result shape is inferred from the spec. Also available
over REST and MCP. See SDK aggregates.
Safer schema changes on your own database
For bring-your-own-database subgraphs, a breaking schema change (removed table/column, changed type, or a forced reindex) is now refused, keeping your data intact.
- The deploy returns the exact migration plan: the
DROP SCHEMA … CASCADEplus the rebuild DDL, so you can run it yourself and re-deploy. sl subgraphs deployprints the plan.- The SDK throws a typed
ByoBreakingChangeErrorexposingreasonsandplan.
Subscriptions went polymorphic. A subscription is now one of two kinds: subgraph (fires on subgraph table rows, as before) or chain, a webhook on raw chain events with no subgraph at all. The lambda for Stacks.
Direct chain subscriptions
Pass a triggers array instead of a subgraph table. A chain subscription is forward-looking: it starts at the chain tip and fires the moment a matching event lands, with no deploy and no backfill.
import { trigger } from "@secondlayer/sdk";
await sl.subscriptions.create({
name: "amm-swaps",
url: "https://my-app.com/webhook",
triggers: [
trigger.contractCall({ contractId: "SP....amm", functionName: "swap-*" }),
trigger.ftTransfer({ trait: "sip-010", minAmount: "1000000" }),
],
});trigger.* builders cover every event type (contractCall, contractDeploy, printEvent, and the stx*/ft*/nft* transfer/mint/burn variants), with * wildcards and a trait filter that scopes to a whole SIP. Over REST, POST /api/subscriptions accepts the same triggers array (1–50).
Apply / rollback delivery
Chain deliveries carry a typed envelope: a match sends chain.{type}.apply
(with block_hash, block_height, tx_id, canonical, the matched trigger,
and the event), and a reorg sends chain.reorg.rollback (with
fork_point_height and the orphaned events) so you can cleanly undo. Delivery
stays at-least-once and HMAC-signed; key your state on (tx_id, block_hash).
The raw-event availability layer matured across the board.
@secondlayer/sdk@5.9.0, @secondlayer/cli@8.2.0, @secondlayer/shared@6.11.0.
Query
New payload filters on /v1/streams/events: sender, recipient, and
asset_identifier (exact-match), alongside the existing contract_id and
types. Event types that lack a field simply don't match, so the firehose
narrows naturally.
curl -H "Authorization: Bearer $SL_API_KEY" \
"https://api.secondlayer.tools/v1/streams/events?types=ft_transfer&sender=SP...&limit=50"Finality
/v1/streams/tip now returns finalized_height, and every event carries a
finalized flag. Finality is anchored to Bitcoin (burn-block) confirmations
rather than a fixed count of Stacks blocks, so a finalized event will not reorg.
Caching
Fully-finalized pages (a closed range with to_height ≤ finalized_height) are
served Cache-Control: public, max-age=31536000, immutable with a weak ETag
and 304 on If-None-Match. Tip-spanning requests stay private, max-age=2,
so historical reads are cheap while the live edge stays fresh.
Proofs
Streams responses are signed with ed25519. Each read carries an X-Signature
(over the exact response body) and X-Signature-KeyId. Fetch the public key at
GET /public/streams/signing-key and verify, or set verify on the SDK
client; you can trust the data without trusting the server.
const streams = createStreamsClient({ apiKey, verify: true });Bulk & backfill
Download all of it. Finalized history is published as bulk dumps with an ed25519-signed manifest; the SDK verifies that signature by default (verifyDumpsManifest) before trusting any per-file sha256 it lists.
- SDK:
client.dumps.list()/download(file)and a one-callevents.replay({ from: "genesis", onDumpFile, onBatch })that backfills cold history from dumps, then tails live from the manifest's finalized cursor with no gap or duplicate at the seam. - CLI:
sl streams pull --to ./dumpdownloads finalized dumps locally (dumps are public, no API key).
Reliability
Reorgs now archive orphaned rows instead of deleting them, keeping the raw log intact and auditable. The indexer gained leader election, making it safe to run as multiple instances.