Subgraphs are your own indexer, minus the node. Pick the events your app needs, write handlers, and get typed Postgres tables shaped exactly for your product — queryable over a REST API, the SDK, or a generated typed client.

It's the L3 surface: where raw events become the data your app actually wants. Install with bun add @secondlayer/subgraphs.

Set up subgraphs with your agent.
Ensure setup once, skipping any step already done:
- Skill: `bunx skills add ryanwaits/secondlayer --skill secondlayer -y`
- CLI: `bun add -g @secondlayer/cli`
- Auth: `sl login` then `sl whoami`
- Instance: `sl instance info`; if missing, start a trial from Billing or run `sl instance create --plan launch`

Subgraphs are declarative SQL tables that auto-index Stacks blockchain activity into queryable Postgres tables. Define named sources, a typed schema, and handlers in TypeScript, then deploy and query.

/secondlayer Help me create a subgraph from a Stacks contract. Ask me for the contract id and the events or function calls I care about. Scaffold with `sl subgraphs scaffold` so the module package and dependencies are prepared, explain the generated named sources and tables, let me review or customize the handlers, deploy with `sl subgraphs deploy`, query recent rows, then ask whether I want a subscription webhook.

How it works

subgraph.config.tsRaw eventsL1 (+ decoded)Your handlersdefineSubgraph()Your tables/api/subgraphs · typedApp

your shape, not a fixed one

The runtime feeds matched events into your handlers per block; the handlers write rows into your own schema. No node, no backfill scripts — redeploy and it reindexes for you.


Define a subgraph

A subgraph has three parts: sources (what events to listen for), a schema (what tables to create), and handlers (how to turn each event into rows). Writes are batched and flushed atomically per block.

import { defineSubgraph } from "@secondlayer/subgraphs";

export default defineSubgraph({
  name: "stx-transfers",
  sources: {
    transfer: { type: "stx_transfer" },
  },
  schema: {
    transfers: {
      columns: {
        sender: { type: "principal", indexed: true },
        recipient: { type: "principal", indexed: true },
        amount: { type: "uint" },
        memo: { type: "text", nullable: true },
      },
    },
  },
  handlers: {
    transfer: (event, ctx) => {
      ctx.insert("transfers", {
        sender: event.sender,
        recipient: event.recipient,
        amount: event.amount,
        memo: event.memo,
      });
    },
  },
});

Each subgraph gets its own Postgres schema with system columns added automatically (_id, _block_height, _tx_id, _created_at). The full column-type list, the handler context API (upsert, findOne, aggregates), and monitoring patterns are in the docs.


Deploy & query

Scaffold from a contract's ABI, deploy, and query — via the CLI, the SDK, or the generated typed client:

# Scaffold from a deployed contract's ABI
sl subgraphs scaffold SP1234ABCD.token-transfers --output subgraphs/token-transfers.ts

# Deploy (or 'dev' to watch + auto-redeploy)
sl subgraphs deploy subgraphs/token-transfers.ts

# Generate a typed client (autocompletion + table types)
sl subgraphs generate token-transfers --output src/clients/token-transfers.ts
// SDK — filters, comparison operators, ordering, pagination
const { data } = await client.subgraphs.queryTable(
  "token-transfers",
  "transfers",
  {
    sort: "_block_height",
    order: "desc",
    limit: 25,
    filters: { sender: "SP1234...", "amount.gte": "1000000" },
  },
);

// HTTP
// GET https://api.secondlayer.tools/api/subgraphs/token-transfers/transfers
//   ?_sort=_block_height&_order=desc&_limit=25&sender=SP1234...