<!-- source: https://secondlayer.tools/docs/sdk-reference/helpers -->

# Helpers

Functions you import from `@secondlayer/sdk` rather than call on the client: decoders and type guards, signature and proof checks, and consume-loop helpers.

### buildDoctorReport

```ts
buildDoctorReport(input: { webhook: WebhookDetail; deliveries: DeliveryRow[]; dead: DeadRow[] }): DoctorReport
```

### buildListIssue

The list page's own reduced detector: computed from `WebhookSummary` alone (no deliveries, no dead-letter rows — the list endpoint returns neither, and fetching per-row detail or deliveries here would fan out one request per row).

```ts
buildListIssue(webhook: { status: WebhookStatus; circuitOpenedAt: string | null; lastSuccessAt: string | null }): DoctorIssue | null
```

### consumeIndexFeed

Checkpointed pull loop over a cursor-paginated Index feed — the Index port of `consumeStreamsEvents`, sharing its contract: at-least-once delivery, client-owned checkpoints (`onBatch` may return the cursor it committed), and automatic reorg rewind to the lowest fresh fork point.

```ts
consumeIndexFeed<TItem extends IndexFeedItem, TEnvelope extends IndexFeedEnvelope, TTx = never>(opts: IndexConsumeOptions<TItem, TEnvelope, TTx> & { fetchPage: IndexFeedFetcher<TEnvelope>; itemsOf: (envelope: TEnvelope) => TItem[] }): Promise<{ cursor: string | null; pages: number; emptyPolls: number }>
```

### consumerHealth

Liveness for a consume loop.

```ts
consumerHealth(options?: { staleAfterMs?: number }): ConsumerHealth
```

### contextField

Fold one read into a [`ContextField`](/docs/sdk-reference/client#t-ContextField): the value, or `null` with the failure described.

```ts
contextField<T>(read: Promise<T>): Promise<ContextField<T>>
```

### createArchiveClient

```ts
createArchiveClient(options?: CreateArchiveClientOptions): ArchiveClient
```

### createStreamsClient

```ts
createStreamsClient(options: CreateStreamsClientOptions): StreamsClient
```

### decode

Decode a raw Streams event into the SAME flat, `event_type`-discriminated row shape Index serves — so Streams consumption reads identically to Index consumption, with one call instead of eleven guard+decode pairs.

```ts
decode(event: StreamsEvent): IndexEvent
```

### decodeChainWebhook

Decode + narrow a chain webhook delivery body into a typed `ChainWebhookDelivery`.

```ts
decodeChainWebhook(rawBody: string): ChainWebhookDelivery
```

### decodeClarityValue

Decode a hex-encoded Clarity value to JSON-safe JS (uints as strings, buffers as `0x…` hex, tuples as objects).

```ts
decodeClarityValue(hex: string): unknown
```

### decodeFtBurn

```ts
decodeFtBurn(event: StreamsEvent): DecodedFtBurn
```

### decodeFtMint

```ts
decodeFtMint(event: StreamsEvent): DecodedFtMint
```

### decodeFtTransfer

```ts
decodeFtTransfer(event: StreamsEvent): DecodedFtTransfer
```

### decodeNftBurn

```ts
decodeNftBurn(event: StreamsEvent): DecodedNftBurn
```

### decodeNftMint

```ts
decodeNftMint(event: StreamsEvent): DecodedNftMint
```

### decodeNftTransfer

```ts
decodeNftTransfer(event: StreamsEvent): DecodedNftTransfer
```

### decodePrint

```ts
decodePrint(event: StreamsEvent): DecodedPrint
```

### decodeStxBurn

```ts
decodeStxBurn(event: StreamsEvent): DecodedStxBurn
```

### decodeStxLock

```ts
decodeStxLock(event: StreamsEvent): DecodedStxLock
```

### decodeStxMint

```ts
decodeStxMint(event: StreamsEvent): DecodedStxMint
```

### decodeStxTransfer

```ts
decodeStxTransfer(event: StreamsEvent): DecodedStxTransfer
```

### fetchRewardSet

Resolve a reward set directly from a stacks-node (`/v3/stacker_set/{cycle}`), so a caller can verify the consensus layer against a node IT trusts rather than the reward set Secondlayer embedded in the proof.

```ts
fetchRewardSet(opts: { nodeUrl: string; cycle: number; fetchImpl?: typeof fetch }): Promise<RewardSet | null>
```

### getSubgraph

Returns a typed client for a subgraph defined with `defineSubgraph()`.

```ts
getSubgraph<T extends { name: string; schema: Record<string, unknown> }>(def: T, options?: Subgraphs | SecondLayer | { baseUrl?: string; apiKey?: string; accountKey?: string; fetchImpl?: FetchLike; dumpsBaseUrl?: string; archiveBaseUrl?: string; archiveOpsUrl?: string; origin?: "cli" | "mcp" | "session"; verify?: boolean | { publicKey: string; }; verifyDumpsManifest?: boolean; requestTimeoutMs?: number; etagCache?: false | EtagCache }): InferSubgraphClient<T>
```

### isFtBurn

```ts
isFtBurn(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "ft_burn" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "ft_burn" }
```

### isFtMint

```ts
isFtMint(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "ft_mint" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "ft_mint" }
```

### isFtTransfer

```ts
isFtTransfer(event: StreamsEvent): event is StreamsEventOf<"ft_transfer", FtTransferPayload>
```

### isHostedApiUrl

True when `url` is the hosted Secondlayer API (`api.secondlayer.tools`).

```ts
isHostedApiUrl(url: string): boolean
```

### isNftBurn

```ts
isNftBurn(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "nft_burn" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "nft_burn" }
```

### isNftMint

```ts
isNftMint(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "nft_mint" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "nft_mint" }
```

### isNftTransfer

```ts
isNftTransfer(event: StreamsEvent): event is StreamsEventOf<"nft_transfer", NftTransferPayload>
```

### isPrint

```ts
isPrint(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "print" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "print" }
```

### isStxBurn

```ts
isStxBurn(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "stx_burn" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "stx_burn" }
```

### isStxLock

```ts
isStxLock(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "stx_lock" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "stx_lock" }
```

### isStxMint

```ts
isStxMint(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "stx_mint" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "stx_mint" }
```

### isStxTransfer

```ts
isStxTransfer(event: StreamsEvent): event is StreamsEventBase & { event_type: "stx_transfer"; payload: StxTransferPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "stx_mint"; payload: StxMintPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "stx_burn"; payload: StxBurnPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "stx_lock"; payload: StxLockPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "ft_transfer"; payload: FtTransferPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "ft_mint"; payload: FtMintPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "ft_burn"; payload: FtBurnPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "nft_transfer"; payload: NftTransferPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "nft_mint"; payload: NftMintPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "nft_burn"; payload: NftBurnPayload } & { event_type: "stx_transfer" } | StreamsEventBase & { event_type: "print"; payload: PrintPayload } & { event_type: "stx_transfer" }
```

### isSuccessDelivery

Webhook diagnosis, shared by `secondlayer webhooks doctor` and the dashboard's webhook detail page.

```ts
isSuccessDelivery(row: DeliveryRow): boolean
```

### parseRetryAfter

Parse a `Retry-After` header: delta-seconds or an HTTP-date.

```ts
parseRetryAfter(value?: string | null): number | undefined
```

### resolveAccountKey

Resolve the hosted account key for archive quote/fetch/credits.

```ts
resolveAccountKey(accountKey?: string): string | undefined
```

### resolveApiKey

Resolve the bearer credential; the key follows the host.

```ts
resolveApiKey(apiKey?: string, baseUrl?: string): string | undefined
```

### resolveBaseUrl

```ts
resolveBaseUrl(explicit?: string): string
```

### shutdownSignal

An `AbortSignal` wired to SIGTERM/SIGINT (redeploys arrive as SIGTERM).

```ts
shutdownSignal(options?: { signals?: readonly ("SIGTERM" | "SIGINT")[] }): AbortSignal
```

### toJsonSafe

Make a cvToValue result JSON-serializable: Clarity (u)ints decode to bigint, which JSON.stringify can't handle — convert recursively to strings.

```ts
toJsonSafe(value: unknown): unknown
```

### verifySecondlayerSignature

Verify the universal Secondlayer authenticity signature that every delivery carries, regardless of body format (`raw`, `cloudevents`, `standard-webhooks`, …).

```ts
verifySecondlayerSignature(rawBody: string, headers: WebhookHeaderInput, publicKeyPem: string): boolean
```

### verifyStandardWebhooksHeaders

```ts
verifyStandardWebhooksHeaders(body: string, headers: StandardWebhooksHeaders | Record<string, string | string[] | undefined>, secret: string, opts?: VerifyOptions): boolean
```

### verifyTransactionProof

Verify a transaction-inclusion proof.

```ts
verifyTransactionProof(proof: TransactionProof, opts?: { rewardSet?: RewardSet }): TransactionProofVerifyResult
```

### verifyWebhookSignature

Verify a Secondlayer webhook delivery signature.

```ts
verifyWebhookSignature(rawBody: string, headers: WebhookHeaderInput, secret: string, toleranceSeconds?: number): boolean
```

## Notes for agents

- These are named exports: 'import { verifyWebhookSignature } from "@secondlayer/sdk"'. They are not methods on the client.
- A name on this page is a real export of the shipped package.

## Setup

Ensure setup once, skipping any step already done:
- Skill: `bunx skills add ryanwaits/secondlayer --skill secondlayer -y`
- CLI: `bun add -g @secondlayer/cli`
- Instance: `secondlayer init` writes `INSTANCE_TOKEN`. Loopback `/v1` reads need no key.
- Hosted/archive: export `SECONDLAYER_API_KEY` (`sk-sl_*`) or buy credits with `secondlayer credits buy` (`balance`, `refill`), then `secondlayer whoami`
