A TypeScript client for the platform. Deploy subgraphs, query tables, manage row-change subscriptions — same auth, same operations as the CLI and MCP server.

Install with bun add @secondlayer/sdk.

Wire the SDK 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`

/secondlayer Wire `@secondlayer/sdk` into my app. Show typed subgraph queries with `getSubgraph`, then wire subscription creation and lifecycle calls: create, list, pause, resume, rotateSecret, recentDeliveries, dead, requeueDead, replay, and delete. Use concrete names from my project when available.

Getting started

import { SecondLayer } from "@secondlayer/sdk"

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

client.subgraphs       // deploy, query, reindex
client.subscriptions   // create, list, update, pause, resume, replay, delete

Subgraphs

// Query a table — returns rows directly
const rows = await client.subgraphs.queryTable(
  "token-transfers",
  "transfers",
  {
    sort: "_block_height",
    order: "desc",
    limit: 50,
    filters: { sender: "SP1234...", "amount.gte": "1000000" },
  }
)

// Count rows
const { count } = await client.subgraphs.queryTableCount(
  "token-transfers",
  "transfers",
  { filters: { sender: "SP1234..." } }
)

Typed subgraphs

Import your subgraph definition to get a fully typed query client — table names, column names, and filter operators all inferred from your schema.

import { getSubgraph } from "@secondlayer/sdk"
import mySubgraph from "./subgraphs/token-transfers"

const typed = getSubgraph(mySubgraph, { apiKey: "sk-sl_..." })

const rows = await typed.transfers.findMany({
  where: { sender: { eq: "SP1234..." }, amount: { gte: 1000000n } },
  orderBy: { _blockHeight: "desc" },
  limit: 25,
})

const total = await typed.transfers.count({ sender: { eq: "SP1234..." } })

Subscriptions

Subscribe to row changes on any subgraph table. The emitter POSTs signed payloads (Standard Webhooks by default — verify with any Svix library) with retries and a per-subscription circuit breaker. Supported wire formats: standard-webhooks, inngest, trigger, cloudflare,cloudevents, raw.

// Create a subscription
const { subscription, signingSecret } = await client.subscriptions.create({
  name: "whale-alerts",
  subgraphName: "token-transfers",
  tableName: "transfers",
  url: "https://example.com/hooks/transfers",
  format: "standard-webhooks",               // default
  filter: { amount: { gte: "1000000000" } }, // scalar DSL
})

console.log(signingSecret)                   // returned ONCE — store server-side

await client.subscriptions.list()
await client.subscriptions.pause(subscription.id)
await client.subscriptions.resume(subscription.id)
await client.subscriptions.update(subscription.id, { url: "https://..." })
await client.subscriptions.rotateSecret(subscription.id)
await client.subscriptions.recentDeliveries(subscription.id)
await client.subscriptions.dead(subscription.id)
await client.subscriptions.replay(subscription.id, { fromBlock: 180000, toBlock: 181000 })
await client.subscriptions.delete(subscription.id)

Error handling

import { ApiError, VersionConflictError } from "@secondlayer/sdk"

try {
  await client.subgraphs.get("nonexistent")
} catch (err) {
  if (err instanceof ApiError) {
    err.status   // 404
    err.message  // "Subgraph not found"
  }
}

// 401 invalid key  |  404 not found  |  409 version conflict
// 413 bundle too large  |  429 rate limited  |  5xx server error