SDK
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, deleteSubgraphs
// 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