.md

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.

@secondlayer/sdk@15.5.046 helpersfrom source with openpkg
buildDoctorReport(input: { webhook: WebhookDetail; deliveries: DeliveryRow[]; dead: DeadRow[] }): DoctorReport

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).

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

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.

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 }>

Liveness for a consume loop.

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

Fold one read into a ContextField: the value, or null with the failure described.

contextField<T>(read: Promise<T>): Promise<ContextField<T>>
createArchiveClient(options?: CreateArchiveClientOptions): ArchiveClient
createStreamsClient(options: CreateStreamsClientOptions): StreamsClient

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.

decode(event: StreamsEvent): IndexEvent

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

decodeChainWebhook(rawBody: string): ChainWebhookDelivery

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

decodeClarityValue(hex: string): unknown
decodeFtBurn(event: StreamsEvent): DecodedFtBurn
decodeFtMint(event: StreamsEvent): DecodedFtMint
decodeFtTransfer(event: StreamsEvent): DecodedFtTransfer
decodeNftBurn(event: StreamsEvent): DecodedNftBurn
decodeNftMint(event: StreamsEvent): DecodedNftMint
decodeNftTransfer(event: StreamsEvent): DecodedNftTransfer
decodePrint(event: StreamsEvent): DecodedPrint
decodeStxBurn(event: StreamsEvent): DecodedStxBurn
decodeStxLock(event: StreamsEvent): DecodedStxLock
decodeStxMint(event: StreamsEvent): DecodedStxMint
decodeStxTransfer(event: StreamsEvent): DecodedStxTransfer

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.

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

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

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(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(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(event: StreamsEvent): event is StreamsEventOf<"ft_transfer", FtTransferPayload>

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

isHostedApiUrl(url: string): boolean
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(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(event: StreamsEvent): event is StreamsEventOf<"nft_transfer", NftTransferPayload>
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(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(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(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(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" }

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

isSuccessDelivery(row: DeliveryRow): boolean

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

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

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

resolveAccountKey(accountKey?: string): string | undefined

Resolve the bearer credential; the key follows the host.

resolveApiKey(apiKey?: string, baseUrl?: string): string | undefined
resolveBaseUrl(explicit?: string): string

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

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

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

toJsonSafe(value: unknown): unknown

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

verifySecondlayerSignature(rawBody: string, headers: WebhookHeaderInput, publicKeyPem: string): boolean
verifyStandardWebhooksHeaders(body: string, headers: StandardWebhooksHeaders | Record<string, string | string[] | undefined>, secret: string, opts?: VerifyOptions): boolean

Verify a transaction-inclusion proof.

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

Verify a Secondlayer webhook delivery signature.

verifyWebhookSignature(rawBody: string, headers: WebhookHeaderInput, secret: string, toleranceSeconds?: number): boolean
Edit on GitHub