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

# `sl.index`

Decoded rows: transfers, events, contract calls, blocks, transactions, sBTC, PoX and Runes. The same data the [Index API](/docs/api/index) serves over HTTP.

## Methods

### sl.index.ftTransfers(params)

Callable: `index.ftTransfers(params)` ≡ `index.ftTransfers.list(params)`.

**Overload 1**

```ts
sl.index.ftTransfers(
  params: FtTransfersListParams & { fields: readonly F[] },
): Promise<FtTransfersEnvelope<FtTransferFields<F>>>
```

**Overload 2**

```ts
sl.index.ftTransfers(
  params?: FtTransfersListParams,
): Promise<FtTransfersEnvelope<FtTransfer>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `readonly F[]` | yes | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. Omitting `block_time` also lets the server skip the blocks join. |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `contractId` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |

### sl.index.ftTransfers.list(params)

Read one page of fungible-token transfers, plus the cursor for the next.

Filter by `contractId`, `assetIdentifier`, `sender`, `recipient`, and a height range. Filters are equality only: there is no amount filter. `order: "desc"` reads newest first. Pass `next_cursor` back as `cursor` to continue. `fields` returns only those columns and narrows the row type; `cursor`, `block_height`, and `event_type` always come back. `reorgs` on the envelope lists forks that overlap the page.

**Overload 1**

```ts
sl.index.ftTransfers.list<const F extends keyof FtTransfer & string>(
  params: FtTransfersListParams & { fields: readonly F[] },
): Promise<FtTransfersEnvelope<FtTransferFields<F>>>
```

**Overload 2**

```ts
sl.index.ftTransfers.list(
  params?: FtTransfersListParams,
): Promise<FtTransfersEnvelope<FtTransfer>>
```

**Overload 3**

```ts
sl.index.ftTransfers.list<const F extends keyof FtTransfer & string>(
  params: FtTransfersListParams & { fields: readonly F[] },
): Promise<FtTransfersEnvelope<FtTransferFields<F>>>
```

**Overload 4**

```ts
sl.index.ftTransfers.list(
  params?: FtTransfersListParams,
): Promise<FtTransfersEnvelope<FtTransfer>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `readonly F[]` | yes | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. Omitting `block_time` also lets the server skip the blocks join. |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `contractId` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |

### sl.index.ftTransfers.walk(params)

Iterate every fungible-token transfer, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip, or with `order: "desc"` when it reaches `fromHeight` (genesis by default). `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

**Overload 1**

```ts
sl.index.ftTransfers.walk<const F extends keyof FtTransfer & string>(
  params: {
    cursor?: string | null;
    fromHeight?: number;
    toHeight?: number;
    contractId?: string;
    sender?: string;
    recipient?: string;
    assetIdentifier?: string;
    fromCursor?: string | null;
    fields?: readonly ("cursor" | "sender" | "recipient" | "block_height" | "event_type" | "block_time" | "tx_id" | "tx_index" | "event_index" | "contract_id" | "asset_identifier" | "amount")[];
    order?: IndexOrder;
  }
  & PageRetryOptions
  & { batchSize?: number; signal?: AbortSignal }
  & { fields: readonly F[] },
): AsyncIterable<FtTransferFields<F>, any, any>
```

**Overload 2**

```ts
sl.index.ftTransfers.walk(
  params?: FtTransfersWalkParams,
): AsyncIterable<FtTransfer, any, any>
```

**Overload 3**

```ts
sl.index.ftTransfers.walk<const F extends keyof FtTransfer & string>(
  params: {
    cursor?: string | null;
    fromHeight?: number;
    toHeight?: number;
    contractId?: string;
    sender?: string;
    recipient?: string;
    assetIdentifier?: string;
    fromCursor?: string | null;
    fields?: readonly ("cursor" | "sender" | "recipient" | "block_height" | "event_type" | "block_time" | "tx_id" | "tx_index" | "event_index" | "contract_id" | "asset_identifier" | "amount")[];
    order?: IndexOrder;
  }
  & PageRetryOptions
  & { batchSize?: number; signal?: AbortSignal }
  & { fields: readonly F[] },
): AsyncIterable<FtTransferFields<F>, any, any>
```

**Overload 4**

```ts
sl.index.ftTransfers.walk(
  params?: FtTransfersWalkParams,
): AsyncIterable<FtTransfer, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `contractId` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `fields` | `readonly F[]` | yes | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. Omitting `block_time` also lets the server skip the blocks join. |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.nftTransfers(params)

Callable: `index.nftTransfers(params)` ≡ `index.nftTransfers.list(params)`.

**Overload 1**

```ts
sl.index.nftTransfers(
  params: NftTransfersListParams & { fields: readonly F[] },
): Promise<NftTransfersEnvelope<NftTransferFields<F>>>
```

**Overload 2**

```ts
sl.index.nftTransfers(
  params?: NftTransfersListParams,
): Promise<NftTransfersEnvelope<NftTransfer>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `readonly F[]` | yes | Columns to return (see `FtTransfersListParams.fields`). |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `contractId` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |

### sl.index.nftTransfers.list(params)

Read one page of NFT transfers, plus the cursor for the next.

Filter by `contractId`, `assetIdentifier`, `sender`, `recipient`, and a height range. `order: "desc"` reads newest first. Pass `next_cursor` back as `cursor` to continue. `fields` returns only those columns and narrows the row type; `cursor`, `block_height`, and `event_type` always come back.

**Overload 1**

```ts
sl.index.nftTransfers.list<const F extends keyof NftTransfer & string>(
  params: NftTransfersListParams & { fields: readonly F[] },
): Promise<NftTransfersEnvelope<NftTransferFields<F>>>
```

**Overload 2**

```ts
sl.index.nftTransfers.list(
  params?: NftTransfersListParams,
): Promise<NftTransfersEnvelope<NftTransfer>>
```

**Overload 3**

```ts
sl.index.nftTransfers.list<const F extends keyof NftTransfer & string>(
  params: NftTransfersListParams & { fields: readonly F[] },
): Promise<NftTransfersEnvelope<NftTransferFields<F>>>
```

**Overload 4**

```ts
sl.index.nftTransfers.list(
  params?: NftTransfersListParams,
): Promise<NftTransfersEnvelope<NftTransfer>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `readonly F[]` | yes | Columns to return (see `FtTransfersListParams.fields`). |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `contractId` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |

### sl.index.nftTransfers.walk(params)

Iterate every NFT transfer, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip, or with `order: "desc"` when it reaches `fromHeight` (genesis by default). `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

**Overload 1**

```ts
sl.index.nftTransfers.walk<const F extends keyof NftTransfer & string>(
  params: {
    cursor?: string | null;
    fromHeight?: number;
    toHeight?: number;
    contractId?: string;
    sender?: string;
    recipient?: string;
    assetIdentifier?: string;
    fromCursor?: string | null;
    fields?: readonly ("cursor" | "sender" | "recipient" | "block_height" | "event_type" | "block_time" | "tx_id" | "tx_index" | "event_index" | "contract_id" | "asset_identifier" | "value")[];
    order?: IndexOrder;
  }
  & PageRetryOptions
  & { batchSize?: number; signal?: AbortSignal }
  & { fields: readonly F[] },
): AsyncIterable<NftTransferFields<F>, any, any>
```

**Overload 2**

```ts
sl.index.nftTransfers.walk(
  params?: NftTransfersWalkParams,
): AsyncIterable<NftTransfer, any, any>
```

**Overload 3**

```ts
sl.index.nftTransfers.walk<const F extends keyof NftTransfer & string>(
  params: {
    cursor?: string | null;
    fromHeight?: number;
    toHeight?: number;
    contractId?: string;
    sender?: string;
    recipient?: string;
    assetIdentifier?: string;
    fromCursor?: string | null;
    fields?: readonly ("cursor" | "sender" | "recipient" | "block_height" | "event_type" | "block_time" | "tx_id" | "tx_index" | "event_index" | "contract_id" | "asset_identifier" | "value")[];
    order?: IndexOrder;
  }
  & PageRetryOptions
  & { batchSize?: number; signal?: AbortSignal }
  & { fields: readonly F[] },
): AsyncIterable<NftTransferFields<F>, any, any>
```

**Overload 4**

```ts
sl.index.nftTransfers.walk(
  params?: NftTransfersWalkParams,
): AsyncIterable<NftTransfer, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `contractId` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `fields` | `readonly F[]` | yes | Columns to return (see `FtTransfersListParams.fields`). |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.events(params)

Generic decoded events by `event_type` (the full /v1/index/events surface).
Callable: `index.events(params)` ≡ `index.events.list(params)`.

**Overload 1**

```ts
sl.index.events(
  params: EventsListParams<T> & { fields: readonly F[] },
): Promise<EventsEnvelope<T, IndexEventFields<T, F>>>
```

**Overload 2**

```ts
sl.index.events(
  params: EventsListParams<T>,
): Promise<EventsEnvelope<T, IndexEventOf<T>>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `eventType` | `T` | yes | Decoded types or VM types (`nested_contract_call`, `map_set`, …). |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `contractId` | `string \| readonly string[]` | no | One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`. |
| `assetIdentifier` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `functionName` | `string` | no |  |
| `caller` | `string` | no |  |
| `map` | `string` | no |  |
| `varName` | `string` | no |  |
| `txId` | `string` | no | Restrict to one transaction. |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |
| `trait` | `string` | no | Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId; contract-keyed event types only. |
| `txContext` | `boolean` | no | Join the submitting transaction into each event — populates `tx_sender`, `tx_type`, `tx_status`, `tx_contract_id`, `tx_function_name`. Off by default. Avoids a `/v1/index/transactions` call per event; for `print` events it's the only source of the submitting sender. |
| `fields` | `readonly F[]` | yes | Return only these columns. The server projects the SELECT, so an unrequested field is physically absent — and the returned row type narrows to match, making a read of one a compile error rather than `undefined`. `cursor`, `block_height`, and `event_type` come back regardless: the first two are the consume contract, the third carries the discriminant. Don't list them. Cost note: this does NOT change your bill — Index meters per row read, not per field. What it buys is wire bytes, plus skipping the `blocks` join when `block_time` is omitted. |

### sl.index.events.list(params)

Read one page of decoded events of one type.

`eventType` is required: a decoded type (`ft_transfer`, `print`, ...) or a VM type (`nested_contract_call`, `map_set`, ...), and a literal narrows the row type. Scope with `contractId` (one id or up to 20) or `trait`, never both, plus `assetIdentifier`, `sender`, `recipient`, `functionName`, `caller`, `map`, `varName`, `txId`, and a height range. `order: "desc"` reads newest first. `txContext: true` joins the submitting transaction onto each event. `fields` returns only those columns and narrows the row type; it saves wire bytes but does not change what you are billed, since rows are metered, not columns.

**Overload 1**

```ts
sl.index.events.list<T extends IndexEventType, const F extends keyof IndexEventOf<T> & string>(
  params: EventsListParams<T> & { fields: readonly F[] },
): Promise<EventsEnvelope<T, IndexEventFields<T, F>>>
```

**Overload 2**

```ts
sl.index.events.list<T extends IndexEventType>(
  params: EventsListParams<T>,
): Promise<EventsEnvelope<T, IndexEventOf<T>>>
```

**Overload 3**

```ts
sl.index.events.list<T extends IndexEventType, const F extends keyof IndexEventOf<T> & string>(
  params: EventsListParams<T> & { fields: readonly F[] },
): Promise<EventsEnvelope<T, IndexEventFields<T, F>>>
```

**Overload 4**

```ts
sl.index.events.list<T extends IndexEventType>(
  params: EventsListParams<T>,
): Promise<EventsEnvelope<T, IndexEventOf<T>>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `eventType` | `T` | yes | Decoded types or VM types (`nested_contract_call`, `map_set`, …). |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `contractId` | `string \| readonly string[]` | no | One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`. |
| `assetIdentifier` | `string` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `functionName` | `string` | no |  |
| `caller` | `string` | no |  |
| `map` | `string` | no |  |
| `varName` | `string` | no |  |
| `txId` | `string` | no | Restrict to one transaction. |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |
| `trait` | `string` | no | Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId; contract-keyed event types only. |
| `txContext` | `boolean` | no | Join the submitting transaction into each event — populates `tx_sender`, `tx_type`, `tx_status`, `tx_contract_id`, `tx_function_name`. Off by default. Avoids a `/v1/index/transactions` call per event; for `print` events it's the only source of the submitting sender. |
| `fields` | `readonly F[]` | yes | Return only these columns. The server projects the SELECT, so an unrequested field is physically absent — and the returned row type narrows to match, making a read of one a compile error rather than `undefined`. `cursor`, `block_height`, and `event_type` come back regardless: the first two are the consume contract, the third carries the discriminant. Don't list them. Cost note: this does NOT change your bill — Index meters per row read, not per field. What it buys is wire bytes, plus skipping the `blocks` join when `block_time` is omitted. |

### sl.index.events.walk(params)

Iterate every decoded event of one type, paging for you.

Takes the same filters as `list`, including `eventType`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip, or with `order: "desc"` when it reaches `fromHeight` (genesis by default). `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

**Overload 1**

```ts
sl.index.events.walk<T extends IndexEventType, const F extends keyof IndexEventOf<T> & string>(
  params: {
    cursor?: string | null;
    fromHeight?: number;
    toHeight?: number;
    contractId?: string | readonly string[];
    sender?: string;
    recipient?: string;
    assetIdentifier?: string;
    fromCursor?: string | null;
    functionName?: string;
    trait?: string;
    txContext?: boolean;
    fields?: readonly (keyof Extract<IndexFtTransfer, { event_type: T; }> & keyof Extract<IndexNftTransfer, { event_type: T; }> & keyof Extract<IndexStxTransfer, { event_type: T; }> & keyof Extract<IndexStxMint, { event_type: T; }> & keyof Extract<IndexStxBurn, { event_type: T; }> & keyof Extract<IndexStxLock, { event_type: T; }> & keyof Extract<IndexFtMint, { event_type: T; }> & keyof Extract<IndexFtBurn, { event_type: T; }> & keyof Extract<IndexNftMint, { event_type: T; }> & keyof Extract<IndexNftBurn, { event_type: T; }> & keyof Extract<IndexPrint, { event_type: T; }> & keyof Extract<IndexNestedContractCall, { event_type: T; }> & keyof Extract<IndexVarSet, { event_type: T; }> & keyof Extract<IndexMapSet, { event_type: T; }> & keyof Extract<IndexMapInsert, { event_type: T; }> & keyof Extract<IndexMapDelete, { event_type: T; }> & string)[];
    caller?: string;
    order?: IndexOrder;
    eventType: T;
    map?: string;
    varName?: string;
    txId?: string;
  }
  & PageRetryOptions
  & { batchSize?: number; signal?: AbortSignal }
  & { fields: readonly F[] },
): AsyncIterable<IndexEventFields<T, F>, any, any>
```

**Overload 2**

```ts
sl.index.events.walk<T extends IndexEventType>(
  params: EventsWalkParams<T>,
): AsyncIterable<IndexEventOf<T>, any, any>
```

**Overload 3**

```ts
sl.index.events.walk<T extends IndexEventType, const F extends keyof IndexEventOf<T> & string>(
  params: {
    cursor?: string | null;
    fromHeight?: number;
    toHeight?: number;
    contractId?: string | readonly string[];
    sender?: string;
    recipient?: string;
    assetIdentifier?: string;
    fromCursor?: string | null;
    functionName?: string;
    trait?: string;
    txContext?: boolean;
    fields?: readonly (keyof Extract<IndexFtTransfer, { event_type: T; }> & keyof Extract<IndexNftTransfer, { event_type: T; }> & keyof Extract<IndexStxTransfer, { event_type: T; }> & keyof Extract<IndexStxMint, { event_type: T; }> & keyof Extract<IndexStxBurn, { event_type: T; }> & keyof Extract<IndexStxLock, { event_type: T; }> & keyof Extract<IndexFtMint, { event_type: T; }> & keyof Extract<IndexFtBurn, { event_type: T; }> & keyof Extract<IndexNftMint, { event_type: T; }> & keyof Extract<IndexNftBurn, { event_type: T; }> & keyof Extract<IndexPrint, { event_type: T; }> & keyof Extract<IndexNestedContractCall, { event_type: T; }> & keyof Extract<IndexVarSet, { event_type: T; }> & keyof Extract<IndexMapSet, { event_type: T; }> & keyof Extract<IndexMapInsert, { event_type: T; }> & keyof Extract<IndexMapDelete, { event_type: T; }> & string)[];
    caller?: string;
    order?: IndexOrder;
    eventType: T;
    map?: string;
    varName?: string;
    txId?: string;
  }
  & PageRetryOptions
  & { batchSize?: number; signal?: AbortSignal }
  & { fields: readonly F[] },
): AsyncIterable<IndexEventFields<T, F>, any, any>
```

**Overload 4**

```ts
sl.index.events.walk<T extends IndexEventType>(
  params: EventsWalkParams<T>,
): AsyncIterable<IndexEventOf<T>, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `contractId` | `string \| readonly string[]` | no | One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`. |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `functionName` | `string` | no |  |
| `trait` | `string` | no | Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId; contract-keyed event types only. |
| `txContext` | `boolean` | no | Join the submitting transaction into each event — populates `tx_sender`, `tx_type`, `tx_status`, `tx_contract_id`, `tx_function_name`. Off by default. Avoids a `/v1/index/transactions` call per event; for `print` events it's the only source of the submitting sender. |
| `fields` | `readonly F[]` | yes | Return only these columns. The server projects the SELECT, so an unrequested field is physically absent — and the returned row type narrows to match, making a read of one a compile error rather than `undefined`. `cursor`, `block_height`, and `event_type` come back regardless: the first two are the consume contract, the third carries the discriminant. Don't list them. Cost note: this does NOT change your bill — Index meters per row read, not per field. What it buys is wire bytes, plus skipping the `blocks` join when `block_time` is omitted. |
| `caller` | `string` | no |  |
| `order` | `IndexOrder` | no | `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder). |
| `eventType` | `T` | yes | Decoded types or VM types (`nested_contract_call`, `map_set`, …). |
| `map` | `string` | no |  |
| `varName` | `string` | no |  |
| `txId` | `string` | no | Restrict to one transaction. |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.events.consume(params)

Run a checkpointed, reorg-aware indexer over decoded events.

Write your rows in `onBatch` and commit `ctx.cursor` in the same transaction; resume with `fromCursor`. Without `fromCursor` or `fromHeight` the API serves only its recent default window, so pass `fromHeight: 0` for a full backfill. A reorg at or below your checkpoint calls `onReorg`, then the loop re-reads from the fork point. Delivery is at-least-once, so keep writes idempotent. With a `sink`, the checkpoint and rollback are handled for you. `finalizedOnly` holds delivery to finalized rows, so no reorg handling is needed. `mode: "tail"` (default) keeps polling at the tip; `mode: "bounded"` returns on the first empty page. Resolves to `{ cursor, pages, emptyPolls }`.

```ts
sl.index.events.consume<T extends IndexEventType, TTx = never>(
  params: {
    fromHeight?: number;
    toHeight?: number;
    contractId?: string | readonly string[];
    sender?: string;
    recipient?: string;
    assetIdentifier?: string;
    functionName?: string;
    trait?: string;
    txContext?: boolean;
    caller?: string;
    eventType: T;
    map?: string;
    varName?: string;
    txId?: string;
  }
  & IndexConsumeOptions<IndexEventOf<T>, EventsEnvelope<T, IndexEventOf<T>>, TTx>
  & { sink?: ConsumerSink<TTx> },
): Promise<{ cursor: string | null; pages: number; emptyPolls: number }>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fromHeight` | `number` | no | Start a fresh sweep at this height (e.g. `0` for genesis backfill). Ignored once a cursor exists (including a sink's committed cursor). |
| `toHeight` | `number` | no |  |
| `contractId` | `string \| readonly string[]` | no |  |
| `sender` | `string` | no |  |
| `recipient` | `string` | no |  |
| `assetIdentifier` | `string` | no |  |
| `functionName` | `string` | no |  |
| `trait` | `string` | no |  |
| `txContext` | `boolean` | no |  |
| `caller` | `string` | no |  |
| `eventType` | `T` | yes |  |
| `map` | `string` | no |  |
| `varName` | `string` | no |  |
| `txId` | `string` | no |  |
| `fromCursor` | `string \| null` | no | Resume from a committed checkpoint. Without it (and without `fromHeight`) the API serves only the recent default window. |
| `mode` | `"tail" \| "bounded"` | no | `tail` (default) keeps polling at the tip; `bounded` returns on the first empty page. |
| `finalizedOnly` | `boolean` | no | Emit only rows at or below the tip's `finalized_height`; the unfinalized tail is re-read each poll until it settles. Finalized data never reorgs, so `onReorg` is skipped entirely. |
| `batchSize` | `number` | no |  |
| `sink` | `ConsumerSink<TTx>` | no |  |
| `onProgress` | `(ctx: ConsumerBatchContext) => void` | no | Fires once per page, before `onBatch` and before any early return — an empty page still proves the loop is alive. Feed it to `consumerHealth().record`. |
| `onBatch` | `(items: TItem[], envelope: TEnvelope, ctx: ConsumerBatchContext & [TTx] extends [never] ? unknown : { tx: TTx }) => string \| Promise<void> \| Promise<string \| undefined \| null> \| undefined \| null \| void` | yes |  |
| `onReorg` | `(reorg: IndexReorg, ctx: { cursor: string \| null }) => Promise<void> \| void` | no | Called once per new reorg at or below the checkpoint, before the loop rewinds to `ctx.cursor` (`null` = pre-genesis) and re-reads. A reorg whose fork sits ABOVE the checkpoint is not a rollback: nothing past it was written, so it is noted and the page is read as normal. |
| `maxRollbackDepth` | `number` | no | Deepest rewind one reorg may make below the checkpoint before the loop refuses with `ValidationError`. Default 1000 blocks. The fork point is server supplied and drives a delete on every declared table, so raise this only for a source you trust. |
| `sleep` | `Sleep` | no |  |
| `emptyBackoffMs` | `number` | no |  |
| `maxPages` | `number` | no |  |
| `maxEmptyPolls` | `number` | no |  |
| `signal` | `AbortSignal` | no |  |
| `retryCount` | `number` | no | Page-fetch retries after the first failure (429/5xx/network only — 4xx and handler throws always propagate). Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep. Cannot change the retry decision — the policy owns it. |

```ts
import { Database } from "bun:sqlite";
import { SecondLayer } from "@secondlayer/sdk";
import { bunSqliteSink } from "@secondlayer/sdk/sinks/bun-sqlite";

const SBTC = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token";

const db = new Database("transfers.db");
db.exec(`create table if not exists transfers (
  cursor text primary key,
  block_height integer not null,
  sender text not null,
  recipient text not null,
  amount text not null
)`);

const sl = new SecondLayer({
	apiKey: process.env.SECONDLAYER_API_KEY,
});

await sl.index.events.consume({
	eventType: "ft_transfer",
	contractId: SBTC,
	fromHeight: 0,
	sink: bunSqliteSink(db, {
		id: "sbtc-transfers",
		tables: ["transfers"],
		height: "block_height",
	}),
	onBatch: (events, _envelope, ctx) => {
		for (const event of events) {
			ctx.tx.run("insert or ignore into transfers values (?, ?, ?, ?, ?)", [
				event.cursor,
				event.block_height,
				event.sender,
				event.recipient,
				event.amount,
			]);
		}
	},
});
```

### sl.index.contractCalls.list(params)

Read one page of decoded contract calls.

Filter by `contractId` (one id or up to 20) or `trait`, never both, plus `functionName`, `sender`, and a height range. Each row has the function name, arguments, result, and status. Pass `next_cursor` back as `cursor` to continue.

```ts
sl.index.contractCalls.list(
  params?: ContractCallsListParams,
): Promise<ContractCallsEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `contractId` | `string \| readonly string[]` | no | One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`. |
| `functionName` | `string` | no |  |
| `sender` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `trait` | `string` | no | Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId. |

### sl.index.contractCalls.walk(params)

Iterate every decoded contract call, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.contractCalls.walk(
  params?: ContractCallsWalkParams,
): AsyncIterable<IndexContractCall, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `contractId` | `string \| readonly string[]` | no | One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`. |
| `sender` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `functionName` | `string` | no |  |
| `trait` | `string` | no | Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId. |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.contractCalls.consume(params)

Run a checkpointed, reorg-aware indexer over contract calls.

Write your rows in `onBatch` and commit `ctx.cursor` in the same transaction; resume with `fromCursor`. Without `fromCursor` or `fromHeight` the API serves only its recent default window, so pass `fromHeight: 0` for a full backfill. A reorg at or below your checkpoint calls `onReorg`, then the loop re-reads from the fork point. Delivery is at-least-once, so keep writes idempotent. With a `sink`, the checkpoint and rollback are handled for you. `finalizedOnly` holds delivery to finalized rows, so no reorg handling is needed. `mode: "tail"` (default) keeps polling at the tip; `mode: "bounded"` returns on the first empty page. Resolves to `{ cursor, pages, emptyPolls }`. Calls already carry `sender` and `function_name`, so there is no `txContext`.

```ts
sl.index.contractCalls.consume(
  params: {
    fromHeight?: number;
    toHeight?: number;
    contractId?: string | readonly string[];
    sender?: string;
    functionName?: string;
    trait?: string;
  }
  & IndexConsumeOptions<IndexContractCall, ContractCallsEnvelope, TTx>
  & { sink?: ConsumerSink<TTx> },
): Promise<{ cursor: string | null; pages: number; emptyPolls: number }>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fromHeight` | `number` | no | Start a fresh sweep at this height (e.g. `0` for genesis backfill). Ignored once a cursor exists (including a sink's committed cursor). |
| `toHeight` | `number` | no |  |
| `contractId` | `string \| readonly string[]` | no |  |
| `sender` | `string` | no |  |
| `functionName` | `string` | no |  |
| `trait` | `string` | no |  |
| `fromCursor` | `string \| null` | no | Resume from a committed checkpoint. Without it (and without `fromHeight`) the API serves only the recent default window. |
| `mode` | `"tail" \| "bounded"` | no | `tail` (default) keeps polling at the tip; `bounded` returns on the first empty page. |
| `finalizedOnly` | `boolean` | no | Emit only rows at or below the tip's `finalized_height`; the unfinalized tail is re-read each poll until it settles. Finalized data never reorgs, so `onReorg` is skipped entirely. |
| `batchSize` | `number` | no |  |
| `sink` | `ConsumerSink<TTx>` | no |  |
| `onProgress` | `(ctx: ConsumerBatchContext) => void` | no | Fires once per page, before `onBatch` and before any early return — an empty page still proves the loop is alive. Feed it to `consumerHealth().record`. |
| `onBatch` | `(items: TItem[], envelope: TEnvelope, ctx: ConsumerBatchContext & [TTx] extends [never] ? unknown : { tx: TTx }) => string \| Promise<void> \| Promise<string \| undefined \| null> \| undefined \| null \| void` | yes |  |
| `onReorg` | `(reorg: IndexReorg, ctx: { cursor: string \| null }) => Promise<void> \| void` | no | Called once per new reorg at or below the checkpoint, before the loop rewinds to `ctx.cursor` (`null` = pre-genesis) and re-reads. A reorg whose fork sits ABOVE the checkpoint is not a rollback: nothing past it was written, so it is noted and the page is read as normal. |
| `maxRollbackDepth` | `number` | no | Deepest rewind one reorg may make below the checkpoint before the loop refuses with `ValidationError`. Default 1000 blocks. The fork point is server supplied and drives a delete on every declared table, so raise this only for a source you trust. |
| `sleep` | `Sleep` | no |  |
| `emptyBackoffMs` | `number` | no |  |
| `maxPages` | `number` | no |  |
| `maxEmptyPolls` | `number` | no |  |
| `signal` | `AbortSignal` | no |  |
| `retryCount` | `number` | no | Page-fetch retries after the first failure (429/5xx/network only — 4xx and handler throws always propagate). Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep. Cannot change the retry decision — the policy owns it. |

### sl.index.canonical.list(params)

Read one page of the canonical block-hash map.

Each row is a block's hash, its parent hash, and its burn-chain anchor. Compare it with your own tables to confirm you follow the current chain.

```ts
sl.index.canonical.list(params?: CanonicalListParams): Promise<CanonicalEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |

### sl.index.canonical.walk(params)

Iterate every canonical block hash, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.canonical.walk(
  params?: CanonicalWalkParams,
): AsyncIterable<IndexCanonicalBlock, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.stateWrites.list(params)

Read one page of the contract-state writes each block committed.

Rows are the exact key and value writes, in the node's write order (`ordinal`), so you can see which key a transaction changed when events alone do not show it. Filter by `blockHeight` (excludes `fromHeight` and `toHeight`) or `contractId`. `txContext: true` joins the writing transaction. Rows exist only from the height your node first subscribed to state writes.

```ts
sl.index.stateWrites.list(params?: StateWritesListParams): Promise<StateWritesEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `blockHeight` | `number` | no | One block. Excludes `fromHeight`/`toHeight`; a cursor must sit at it. |
| `contractId` | `string` | no | One contract (`<address>.<name>`): only keys under `vm::<contractId>::`. |
| `txContext` | `boolean` | no | Join the writing transaction's `tx_*` fields onto each row. |

### sl.index.stateWrites.walk(params)

Iterate every state write, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.stateWrites.walk(
  params?: StateWritesWalkParams,
): AsyncIterable<IndexStateWrite, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `contractId` | `string` | no | One contract (`<address>.<name>`): only keys under `vm::<contractId>::`. |
| `fromCursor` | `string \| null` | no |  |
| `blockHeight` | `number` | no | One block. Excludes `fromHeight`/`toHeight`; a cursor must sit at it. |
| `txContext` | `boolean` | no | Join the writing transaction's `tx_*` fields onto each row. |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.blocks.list(params)

Read one page of canonical blocks.

Each block has its hash, parent hash, burn-chain anchor, and `block_time`. Filter by a height range.

```ts
sl.index.blocks.list(params?: BlocksListParams): Promise<BlocksEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |

### sl.index.blocks.walk(params)

Iterate every canonical block, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.blocks.walk(params?: BlocksWalkParams): AsyncIterable<IndexBlock, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.blocks.get(ref)

Get one block by height or hash, or `null`.

Resolves to `null` when no such block exists (404).

```ts
sl.index.blocks.get(ref: string | number): Promise<BlockEnvelope | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `ref` | `string \| number` | yes |  |

### sl.index.transactions.list(params)

Read one page of full transaction documents.

Filter by `type`, `sender`, `contractId`, and a height range. Each document has the decoded post conditions and the payload for its `tx_type`; enrichment fields are null when the raw transaction cannot be decoded. `fields` returns only those columns and narrows the row type; `cursor`, `block_height`, and `tx_id` always come back.

**Overload 1**

```ts
sl.index.transactions.list(
  params: TransactionsListParams & { fields: readonly F[] },
): Promise<TransactionsEnvelope<TransactionFields<F>>>
```

**Overload 2**

```ts
sl.index.transactions.list(
  params?: TransactionsListParams,
): Promise<TransactionsEnvelope<IndexTransaction>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `readonly F[]` | yes | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `type` | `string` | no |  |
| `sender` | `string` | no |  |
| `contractId` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |

### sl.index.transactions.walk(params)

Iterate every transaction document, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.transactions.walk(
  params?: TransactionsWalkParams,
): AsyncIterable<IndexTransaction, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `contractId` | `string` | no |  |
| `sender` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `fields` | `readonly ("cursor" \| "sender" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "tx_type" \| "status" \| "fee" \| "nonce" \| "sponsored" \| "anchor_mode" \| "post_condition_mode" \| "post_conditions" \| "contract_call" \| "token_transfer" \| "smart_contract" \| "coinbase" \| "tenure_change")[]` | no | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |
| `type` | `string` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.transactions.get(txId)

Get one transaction by id, or `null`.

Resolves to `null` when the transaction is not found (404).

```ts
sl.index.transactions.get(txId: string): Promise<TransactionEnvelope | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `txId` | `string` | yes |  |

### sl.index.transactions.getProof(txId)

Get a transaction's inclusion proof, or `null`.

The proof is the raw transaction, the Nakamoto block header, and the merkle path. Check it yourself with `verifyTransactionProof`; nothing in it needs to be trusted. Resolves to `null` on 404. A 503 (`PROOF_TX_SET_INCOMPLETE` or `PROOF_NODE_UNAVAILABLE`) throws an `ApiError`: the proof cannot be assembled on this deployment right now.

```ts
sl.index.transactions.getProof(txId: string): Promise<TransactionProof | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `txId` | `string` | yes |  |

```ts
import { SecondLayer, verifyTransactionProof } from "@secondlayer/sdk";

const txId = process.argv[2];
if (!txId) throw new Error("usage: bun transactions.getProof.ts <tx_id>");

const sl = new SecondLayer({
	apiKey: process.env.SECONDLAYER_API_KEY,
});

const proof = await sl.index.transactions.getProof(txId);
if (!proof) throw new Error(`no proof for ${txId}`);

const result = verifyTransactionProof(proof);

console.log(result.level, result.ok ? "verified" : result.errors.join("; "));
```

### sl.index.stacking.list(params)

Read one page of decoded PoX-4 stacking actions.

One row per stacking contract call. Filter by `functionName`, `stacker`, `caller`, and a height range. The feed is empty, with a `notes` hint, when the instance's PoX-4 decoder is disabled. For the current PoX, use `pox5.events` and `pox.cycles`.

```ts
sl.index.stacking.list(params?: StackingListParams): Promise<StackingEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `functionName` | `string` | no |  |
| `stacker` | `string` | no |  |
| `caller` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |

### sl.index.stacking.walk(params)

Iterate every PoX-4 stacking action, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.stacking.walk(
  params?: StackingWalkParams,
): AsyncIterable<IndexStackingAction, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `functionName` | `string` | no |  |
| `stacker` | `string` | no |  |
| `caller` | `string` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.mempool.list(params)

Read one page of pending, unconfirmed transactions.

Filter by `sender`, `type`, `contractId`, and `functionName`. There is no height filter: rows carry `received_at` and a sequence cursor instead of a block position.

```ts
sl.index.mempool.list(params?: MempoolListParams): Promise<MempoolEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `sender` | `string` | no |  |
| `type` | `string` | no |  |
| `contractId` | `string` | no | Filter to pending calls to a single contract (e.g. `SP….contract`). |
| `functionName` | `string` | no | Filter to pending calls to a single function (composes with `contractId`). |

### sl.index.mempool.walk(params)

Iterate every pending transaction, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.mempool.walk(
  params?: MempoolWalkParams,
): AsyncIterable<IndexMempoolTransaction, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `contractId` | `string` | no | Filter to pending calls to a single contract (e.g. `SP….contract`). |
| `sender` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `functionName` | `string` | no | Filter to pending calls to a single function (composes with `contractId`). |
| `type` | `string` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.mempool.get(txId)

Get one pending transaction, or `null`.

Resolves to `null` once the transaction has confirmed or dropped.

```ts
sl.index.mempool.get(txId: string): Promise<MempoolTransactionEnvelope | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `txId` | `string` | yes |  |

### sl.index.sbtc.deposits.list(params)

Read one page of completed sBTC deposits.

Filter by `sender`, `bitcoinTxid`, and a height range. `confirmed: true` keeps only deposits past the finality boundary. `fields` returns only those columns and narrows the row type; `cursor` and `block_height` always come back.

**Overload 1**

```ts
sl.index.sbtc.deposits.list(
  params: SbtcDepositsListParams & { fields: readonly F[] },
): Promise<SbtcDepositsEnvelope<SbtcDepositFields<F>>>
```

**Overload 2**

```ts
sl.index.sbtc.deposits.list(
  params?: SbtcDepositsListParams,
): Promise<SbtcDepositsEnvelope<IndexSbtcDeposit>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `confirmed` | `boolean` | no | Clamp to the finality boundary — only settled deposits past the reorg margin. |
| `sender` | `string` | no |  |
| `bitcoinTxid` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `fields` | `readonly F[]` | yes | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |

### sl.index.sbtc.deposits.walk(params)

Iterate every completed sBTC deposit, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.sbtc.deposits.walk(
  params?: SbtcDepositsWalkParams,
): AsyncIterable<IndexSbtcDeposit, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `sender` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `fields` | `readonly ("cursor" \| "sender" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "amount" \| "bitcoin_txid" \| "output_index" \| "recipient_btc_version" \| "recipient_btc_hashbytes")[]` | no | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |
| `confirmed` | `boolean` | no | Clamp to the finality boundary — only settled deposits past the reorg margin. |
| `bitcoinTxid` | `string` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.sbtc.deposits.consume(params)

Run a checkpointed, reorg-aware indexer over completed deposits.

Deposits are append-only, so they are safe to mirror. See [`IndexConsumeOptions`](/docs/sdk-reference/index#t-IndexConsumeOptions).

```ts
sl.index.sbtc.deposits.consume(
  params: {
    fromHeight?: number;
    toHeight?: number;
    sender?: string;
    confirmed?: boolean;
    bitcoinTxid?: string;
  }
  & IndexConsumeOptions<IndexSbtcDeposit, SbtcDepositsEnvelope<IndexSbtcDeposit>, TTx>
  & { sink?: ConsumerSink<TTx> },
): Promise<{ cursor: string | null; pages: number; emptyPolls: number }>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fromHeight` | `number` | no | Start a fresh sweep at this height (e.g. `0` for genesis backfill). Ignored once a cursor exists (including a sink's committed cursor). |
| `toHeight` | `number` | no |  |
| `sender` | `string` | no |  |
| `confirmed` | `boolean` | no |  |
| `bitcoinTxid` | `string` | no |  |
| `fromCursor` | `string \| null` | no | Resume from a committed checkpoint. Without it (and without `fromHeight`) the API serves only the recent default window. |
| `mode` | `"tail" \| "bounded"` | no | `tail` (default) keeps polling at the tip; `bounded` returns on the first empty page. |
| `finalizedOnly` | `boolean` | no | Emit only rows at or below the tip's `finalized_height`; the unfinalized tail is re-read each poll until it settles. Finalized data never reorgs, so `onReorg` is skipped entirely. |
| `batchSize` | `number` | no |  |
| `sink` | `ConsumerSink<TTx>` | no |  |
| `onProgress` | `(ctx: ConsumerBatchContext) => void` | no | Fires once per page, before `onBatch` and before any early return — an empty page still proves the loop is alive. Feed it to `consumerHealth().record`. |
| `onBatch` | `(items: TItem[], envelope: TEnvelope, ctx: ConsumerBatchContext & [TTx] extends [never] ? unknown : { tx: TTx }) => string \| Promise<void> \| Promise<string \| undefined \| null> \| undefined \| null \| void` | yes |  |
| `onReorg` | `(reorg: IndexReorg, ctx: { cursor: string \| null }) => Promise<void> \| void` | no | Called once per new reorg at or below the checkpoint, before the loop rewinds to `ctx.cursor` (`null` = pre-genesis) and re-reads. A reorg whose fork sits ABOVE the checkpoint is not a rollback: nothing past it was written, so it is noted and the page is read as normal. |
| `maxRollbackDepth` | `number` | no | Deepest rewind one reorg may make below the checkpoint before the loop refuses with `ValidationError`. Default 1000 blocks. The fork point is server supplied and drives a delete on every declared table, so raise this only for a source you trust. |
| `sleep` | `Sleep` | no |  |
| `emptyBackoffMs` | `number` | no |  |
| `maxPages` | `number` | no |  |
| `maxEmptyPolls` | `number` | no |  |
| `signal` | `AbortSignal` | no |  |
| `retryCount` | `number` | no | Page-fetch retries after the first failure (429/5xx/network only — 4xx and handler throws always propagate). Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep. Cannot change the retry decision — the policy owns it. |

### sl.index.sbtc.deposits.get(bitcoinTxid)

Fetch a completed deposit by Bitcoin txid; 404 → null.

```ts
sl.index.sbtc.deposits.get(bitcoinTxid: string): Promise<SbtcDepositEnvelope | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `bitcoinTxid` | `string` | yes |  |

### sl.index.sbtc.withdrawals.list(params)

Read one page of sBTC withdrawals, each with its current status.

Filter by `status` (`REQUESTED`, `ACCEPTED`, `REJECTED`), `sender`, `requestId`, `settlementConfirmed`, and a height range. A row changes as the peg-out moves along, which is why withdrawals have no `consume`. `fields` returns only those columns and narrows the row type; `cursor` and `request_id` always come back.

**Overload 1**

```ts
sl.index.sbtc.withdrawals.list(
  params: SbtcWithdrawalsListParams & { fields: readonly F[] },
): Promise<SbtcWithdrawalsEnvelope<SbtcWithdrawalFields<F>>>
```

**Overload 2**

```ts
sl.index.sbtc.withdrawals.list(
  params?: SbtcWithdrawalsListParams,
): Promise<SbtcWithdrawalsEnvelope<IndexSbtcWithdrawal>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `readonly F[]` | yes | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `confirmed` | `boolean` | no |  |
| `status` | `SbtcWithdrawalStatus` | no |  |
| `sender` | `string` | no |  |
| `requestId` | `number` | no |  |
| `settlementConfirmed` | `boolean` | no | Filter by BTC L1 settlement: true → only confirmed sweeps; false → not yet confirmed (pending or no sweep). |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |

### sl.index.sbtc.withdrawals.walk(params)

Iterate every sBTC withdrawal, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.sbtc.withdrawals.walk(
  params?: SbtcWithdrawalsWalkParams,
): AsyncIterable<IndexSbtcWithdrawal, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `sender` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `status` | `SbtcWithdrawalStatus` | no |  |
| `fields` | `readonly ("cursor" \| "sender" \| "amount" \| "status" \| "recipient_btc_version" \| "recipient_btc_hashbytes" \| "request_id" \| "sweep_txid" \| "settlement_confirmed" \| "btc_confirmations" \| "btc_block_height" \| "confirmed_at" \| "requested_at" \| "resolved_at")[]` | no | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |
| `confirmed` | `boolean` | no |  |
| `requestId` | `number` | no |  |
| `settlementConfirmed` | `boolean` | no | Filter by BTC L1 settlement: true → only confirmed sweeps; false → not yet confirmed (pending or no sweep). |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.sbtc.withdrawals.get(requestId)

Fetch a withdrawal's full lifecycle by request id; 404 → null.

```ts
sl.index.sbtc.withdrawals.get(requestId: number): Promise<SbtcWithdrawalEnvelope | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `requestId` | `number` | yes |  |

### sl.index.sbtc.events.list(params)

Read one page of the raw sBTC peg event log.

Filter by `topic`, `sender`, `requestId`, `bitcoinTxid`, and a height range. `confirmed: true` keeps only events past the finality boundary.

```ts
sl.index.sbtc.events.list(params?: SbtcEventsListParams): Promise<SbtcEventsEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `confirmed` | `boolean` | no |  |
| `topic` | `SbtcEventTopic` | no |  |
| `sender` | `string` | no |  |
| `requestId` | `number` | no |  |
| `bitcoinTxid` | `string` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |

### sl.index.sbtc.events.walk(params)

Iterate every sBTC peg event, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.sbtc.events.walk(
  params?: SbtcEventsWalkParams,
): AsyncIterable<IndexSbtcEvent, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `sender` | `string` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `confirmed` | `boolean` | no |  |
| `bitcoinTxid` | `string` | no |  |
| `requestId` | `number` | no |  |
| `topic` | `SbtcEventTopic` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.sbtc.events.consume(params)

Run a checkpointed, reorg-aware indexer over the raw peg event log.

The log is append-only across all six topics. See [`IndexConsumeOptions`](/docs/sdk-reference/index#t-IndexConsumeOptions).

```ts
sl.index.sbtc.events.consume(
  params: {
    fromHeight?: number;
    toHeight?: number;
    sender?: string;
    confirmed?: boolean;
    bitcoinTxid?: string;
    requestId?: number;
    topic?: SbtcEventTopic;
  }
  & IndexConsumeOptions<IndexSbtcEvent, SbtcEventsEnvelope, TTx>
  & { sink?: ConsumerSink<TTx> },
): Promise<{ cursor: string | null; pages: number; emptyPolls: number }>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fromHeight` | `number` | no | Start a fresh sweep at this height (e.g. `0` for genesis backfill). Ignored once a cursor exists (including a sink's committed cursor). |
| `toHeight` | `number` | no |  |
| `sender` | `string` | no |  |
| `confirmed` | `boolean` | no |  |
| `bitcoinTxid` | `string` | no |  |
| `requestId` | `number` | no |  |
| `topic` | `SbtcEventTopic` | no |  |
| `fromCursor` | `string \| null` | no | Resume from a committed checkpoint. Without it (and without `fromHeight`) the API serves only the recent default window. |
| `mode` | `"tail" \| "bounded"` | no | `tail` (default) keeps polling at the tip; `bounded` returns on the first empty page. |
| `finalizedOnly` | `boolean` | no | Emit only rows at or below the tip's `finalized_height`; the unfinalized tail is re-read each poll until it settles. Finalized data never reorgs, so `onReorg` is skipped entirely. |
| `batchSize` | `number` | no |  |
| `sink` | `ConsumerSink<TTx>` | no |  |
| `onProgress` | `(ctx: ConsumerBatchContext) => void` | no | Fires once per page, before `onBatch` and before any early return — an empty page still proves the loop is alive. Feed it to `consumerHealth().record`. |
| `onBatch` | `(items: TItem[], envelope: TEnvelope, ctx: ConsumerBatchContext & [TTx] extends [never] ? unknown : { tx: TTx }) => string \| Promise<void> \| Promise<string \| undefined \| null> \| undefined \| null \| void` | yes |  |
| `onReorg` | `(reorg: IndexReorg, ctx: { cursor: string \| null }) => Promise<void> \| void` | no | Called once per new reorg at or below the checkpoint, before the loop rewinds to `ctx.cursor` (`null` = pre-genesis) and re-reads. A reorg whose fork sits ABOVE the checkpoint is not a rollback: nothing past it was written, so it is noted and the page is read as normal. |
| `maxRollbackDepth` | `number` | no | Deepest rewind one reorg may make below the checkpoint before the loop refuses with `ValidationError`. Default 1000 blocks. The fork point is server supplied and drives a delete on every declared table, so raise this only for a source you trust. |
| `sleep` | `Sleep` | no |  |
| `emptyBackoffMs` | `number` | no |  |
| `maxPages` | `number` | no |  |
| `maxEmptyPolls` | `number` | no |  |
| `signal` | `AbortSignal` | no |  |
| `retryCount` | `number` | no | Page-fetch retries after the first failure (429/5xx/network only — 4xx and handler throws always propagate). Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep. Cannot change the retry decision — the policy owns it. |

### sl.index.sbtc.summary()

Get the all-time sBTC peg scoreboard.

A single aggregate, with no pagination.

```ts
sl.index.sbtc.summary(): Promise<SbtcSummaryEnvelope>
```

### sl.index.pox.cycles.list(params)

Read one page of PoX reward cycles, newest first.

`cursor` is a `reward_cycle` number, and the page returns the cycles below it. `next_cursor` is the next cycle number to page from, or `null` at the end. This reports the current PoX (PoX-5); PoX-4 history is not served here, so read `stacking` for it.

```ts
sl.index.pox.cycles.list(params?: PoxCyclesListParams): Promise<PoxCyclesEnvelope>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `number \| null` | no | A `reward_cycle` to page from; the page returns cycles below it (descending). |
| `limit` | `number` | no |  |

### sl.index.pox.cycles.walk(params)

Iterate every reward cycle, paging for you.

Takes the same filters as `list` (it pages by reward cycle number). Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.pox.cycles.walk(
  params?: PoxCyclesWalkParams,
): AsyncIterable<IndexPoxCycle, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `number \| null` | no | A `reward_cycle` to page from; the page returns cycles below it (descending). |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.pox.cycles.get(rewardCycle)

Get one reward cycle by number, or `null`.

Resolves to `null` on 404: an unknown cycle, or one below pox-5's first
reward cycle (141), whose PoX-4 history is final and not served here.

```ts
sl.index.pox.cycles.get(rewardCycle: number): Promise<PoxCycleEnvelope | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `rewardCycle` | `number` | yes |  |

### sl.index.pox5.events.list(params)

Read one page of decoded PoX-5 staking events.

Filter by `topic`, `staker`, `signer`, `signerManager`, `bondIndex`, `rewardCycle`, and a height range. `confirmed: true` keeps only rows past the finality boundary. `fields` returns only those columns and narrows the row type; `cursor`, `block_height`, and `topic` always come back.

**Overload 1**

```ts
sl.index.pox5.events.list(
  params: Pox5EventsListParams & { fields: readonly F[] },
): Promise<Pox5EventsEnvelope<Pox5EventFields<F>>>
```

**Overload 2**

```ts
sl.index.pox5.events.list(
  params?: Pox5EventsListParams,
): Promise<Pox5EventsEnvelope<IndexPox5Event>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `readonly F[]` | yes | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |
| `confirmed` | `boolean` | no | Clamp to the finality boundary — only rows past the reorg margin. |
| `topic` | `IndexPox5EventTopic` | no |  |
| `staker` | `string` | no |  |
| `signer` | `string` | no |  |
| `signerManager` | `string` | no |  |
| `bondIndex` | `number` | no |  |
| `rewardCycle` | `number` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |

### sl.index.pox5.events.walk(params)

Iterate every PoX-5 event, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.pox5.events.walk(
  params?: Pox5EventsWalkParams,
): AsyncIterable<IndexPox5Event, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `fields` | `readonly ("cursor" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "topic" \| "staker" \| "signer" \| "signer_manager" \| "bond_index" \| "amount_ustx" \| "amount_sats" \| "reward_cycle" \| "first_reward_cycle" \| "unlock_cycle" \| "unlock_burn_height" \| "is_l1_lock" \| "signer_key" \| "data")[]` | no | Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. |
| `confirmed` | `boolean` | no | Clamp to the finality boundary — only rows past the reorg margin. |
| `topic` | `IndexPox5EventTopic` | no |  |
| `staker` | `string` | no |  |
| `signer` | `string` | no |  |
| `signerManager` | `string` | no |  |
| `bondIndex` | `number` | no |  |
| `rewardCycle` | `number` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.pox5.events.consume(params)

Run a checkpointed, reorg-aware indexer over PoX-5 events.

The pox-5 print log is append-only across all 19 topics. See [`IndexConsumeOptions`](/docs/sdk-reference/index#t-IndexConsumeOptions).

```ts
sl.index.pox5.events.consume(
  params: {
    fromHeight?: number;
    toHeight?: number;
    fields?: readonly ("cursor" | "block_height" | "block_time" | "tx_id" | "tx_index" | "event_index" | "topic" | "staker" | "signer" | "signer_manager" | "bond_index" | "amount_ustx" | "amount_sats" | "reward_cycle" | "first_reward_cycle" | "unlock_cycle" | "unlock_burn_height" | "is_l1_lock" | "signer_key" | "data")[];
    confirmed?: boolean;
    topic?: IndexPox5EventTopic;
    staker?: string;
    signer?: string;
    signerManager?: string;
    bondIndex?: number;
    rewardCycle?: number;
  }
  & IndexConsumeOptions<IndexPox5Event, Pox5EventsEnvelope<IndexPox5Event>, TTx>
  & { sink?: ConsumerSink<TTx> },
): Promise<{ cursor: string | null; pages: number; emptyPolls: number }>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fromHeight` | `number` | no | Start a fresh sweep at this height (e.g. `0` for genesis backfill). Ignored once a cursor exists (including a sink's committed cursor). |
| `toHeight` | `number` | no |  |
| `fields` | `readonly ("cursor" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "topic" \| "staker" \| "signer" \| "signer_manager" \| "bond_index" \| "amount_ustx" \| "amount_sats" \| "reward_cycle" \| "first_reward_cycle" \| "unlock_cycle" \| "unlock_burn_height" \| "is_l1_lock" \| "signer_key" \| "data")[]` | no |  |
| `confirmed` | `boolean` | no |  |
| `topic` | `IndexPox5EventTopic` | no |  |
| `staker` | `string` | no |  |
| `signer` | `string` | no |  |
| `signerManager` | `string` | no |  |
| `bondIndex` | `number` | no |  |
| `rewardCycle` | `number` | no |  |
| `fromCursor` | `string \| null` | no | Resume from a committed checkpoint. Without it (and without `fromHeight`) the API serves only the recent default window. |
| `mode` | `"tail" \| "bounded"` | no | `tail` (default) keeps polling at the tip; `bounded` returns on the first empty page. |
| `finalizedOnly` | `boolean` | no | Emit only rows at or below the tip's `finalized_height`; the unfinalized tail is re-read each poll until it settles. Finalized data never reorgs, so `onReorg` is skipped entirely. |
| `batchSize` | `number` | no |  |
| `sink` | `ConsumerSink<TTx>` | no |  |
| `onProgress` | `(ctx: ConsumerBatchContext) => void` | no | Fires once per page, before `onBatch` and before any early return — an empty page still proves the loop is alive. Feed it to `consumerHealth().record`. |
| `onBatch` | `(items: TItem[], envelope: TEnvelope, ctx: ConsumerBatchContext & [TTx] extends [never] ? unknown : { tx: TTx }) => string \| Promise<void> \| Promise<string \| undefined \| null> \| undefined \| null \| void` | yes |  |
| `onReorg` | `(reorg: IndexReorg, ctx: { cursor: string \| null }) => Promise<void> \| void` | no | Called once per new reorg at or below the checkpoint, before the loop rewinds to `ctx.cursor` (`null` = pre-genesis) and re-reads. A reorg whose fork sits ABOVE the checkpoint is not a rollback: nothing past it was written, so it is noted and the page is read as normal. |
| `maxRollbackDepth` | `number` | no | Deepest rewind one reorg may make below the checkpoint before the loop refuses with `ValidationError`. Default 1000 blocks. The fork point is server supplied and drives a delete on every declared table, so raise this only for a source you trust. |
| `sleep` | `Sleep` | no |  |
| `emptyBackoffMs` | `number` | no |  |
| `maxPages` | `number` | no |  |
| `maxEmptyPolls` | `number` | no |  |
| `signal` | `AbortSignal` | no |  |
| `retryCount` | `number` | no | Page-fetch retries after the first failure (429/5xx/network only — 4xx and handler throws always propagate). Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep. Cannot change the retry decision — the policy owns it. |

### sl.index.runes.activity.list(params)

Read one page of Rune etch, mint, transfer, and burn events.

Filter by `rune` (an id or a name), `address`, `kind`, `txid`, and a height range. The cursor is `<block_height>:<event_index>`, and the envelope's tip is a Bitcoin tip. `notes` says so when Runes is not configured on the instance.

```ts
sl.index.runes.activity.list(
  params?: RuneActivityListParams,
): Promise<RuneActivityEnvelope<IndexRuneEvent>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `limit` | `number` | no |  |
| `rune` | `RuneRef` | no |  |
| `address` | `string` | no |  |
| `kind` | `readonly IndexRuneEventKind[]` | no |  |
| `txid` | `string` | no |  |

### sl.index.runes.activity.walk(params)

Iterate every Rune event, paging for you.

Takes the same filters as `list`. Not a live index: it does not checkpoint, ignores reorgs, and ends when it catches the tip. `batchSize` defaults to 200 (max 1000); page fetches retry on 429 and 5xx; aborting `signal` rejects the iterator.

```ts
sl.index.runes.activity.walk(
  params?: RuneActivityWalkParams,
): AsyncIterable<IndexRuneEvent, any, any>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cursor` | `string \| null` | no |  |
| `fromHeight` | `number` | no |  |
| `toHeight` | `number` | no |  |
| `fromCursor` | `string \| null` | no |  |
| `rune` | `RuneRef` | no |  |
| `address` | `string` | no |  |
| `kind` | `readonly IndexRuneEventKind[]` | no |  |
| `txid` | `string` | no |  |
| `retryCount` | `number` | no | Retries after the first failure. Default 3; `0` disables. |
| `retryDelay` | `number` | no | Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000. |
| `onError` | `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void` | no | Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it. |
| `batchSize` | `number` | no | Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000). |
| `signal` | `AbortSignal` | no |  |

### sl.index.runes.list(params)

The rune catalog. `search` prefix-matches the name; `sort` is etch
order (default) or busiest-first.

```ts
sl.index.runes.list(params?: RunesListParams): Promise<RunesEnvelope<IndexRuneEntry>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `search` | `string` | no | Name prefix, spacers and case ignored (`dog` matches `DOG•GO•TO•THE•MOON`). |
| `sort` | `"number" \| "mints"` | no | `number` (etch order, oldest first — default) or `mints` (busiest first). |
| `cursor` | `string \| null` | no |  |
| `limit` | `number` | no |  |

### sl.index.runes.get(rune)

Get one rune by id or name, or `null`.

Resolves to `null` on 404: no such rune, or Runes isn't configured on
this instance.

```ts
sl.index.runes.get(rune: RuneRef): Promise<RuneEnvelope | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `rune` | `RuneRef` | yes |  |

### sl.index.runes.balances(params)

Current per-outpoint rune balances by address or outpoint.

```ts
sl.index.runes.balances(
  params: RuneBalancesListParams,
): Promise<RuneBalancesEnvelope<IndexRuneBalance>>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `params` | `RuneBalancesListParams` | yes |  |

### sl.index.bitcoin.outspend(txid, vout)

Whether a Bitcoin output is spent, and by which transaction.

A mempool spend shows on the next read (`mempool: true`); a confirmed
one names its block. Resolves to `null` on 404: no such transaction,
no output at `vout`, or an OP_RETURN output.

```ts
sl.index.bitcoin.outspend(
  txid: string,
  vout: number,
): Promise<BitcoinOutputUnspent | BitcoinOutputSpent | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `txid` | `string` | yes |  |
| `vout` | `number` | yes |  |

### sl.index.discover()

Get the live vocabulary of what Index can query.

The discovery doc lists every endpoint and each event type's columns,
allowed and equality filters, and required-non-null fields. Read it to
learn what's queryable (and which types accept `trait`) instead of
hardcoding.

REST twin: `GET /v1/index`

```ts
sl.index.discover(): Promise<IndexDiscovery>
```

### sl.index.printSchema(contractId)

Get the print payload schema a contract actually emits.

Lists the topics it emits and each field's observed Clarity, TS, and
column types. Anonymous read; resolves to `null` on 404.

REST twin: `GET /v1/index/contracts/{contractId}/print-schema`

```ts
sl.index.printSchema(contractId: string): Promise<PrintSchemaResponse | null>
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `contractId` | `string` | yes |  |

## Types

- `BitcoinOutputSpent`: A Bitcoin output that some transaction spends.
  - `txid`: `string`
  - `vout`: `number`
  - `spent`: `true`
  - `spending_txid`: `string \| null`. Null for a confirmed spend deeper than the blocks a lookup reads (144 below the tip); `note` says so. Still spent.
  - `spending_vin`: `number \| null`. The spending input's index in `spending_txid`.
  - `confirmed`: `boolean`. In a block of the active chain.
  - `mempool`: `boolean`. Only in the mempool so far.
  - `block_height`: `number \| null`. Null in the mempool.
  - `block_hash`: `string \| null`
  - `note`?: `string`. Why the spender is not named, when it isn't.
- `BitcoinOutputUnspent`: A Bitcoin output that no transaction spends yet.
  - `txid`: `string`
  - `vout`: `number`
  - `spent`: `false`
- `BlockEnvelope`: One block and the chain tip.
  - `block`: `IndexBlock`
  - `tip`: `IndexTip`
- `BlocksEnvelope`: One page of blocks, with the next cursor and chain tip.
  - `blocks`: `IndexBlock[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
- `BlocksListParams`: Paging and height filters for listing blocks.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
- `BlocksWalkParams`: `BlocksListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `fromCursor`?: `string \| null`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `CanonicalEnvelope`: One page of canonical blocks, with the next cursor and chain tip.
  - `canonical`: `IndexCanonicalBlock[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
- `CanonicalListParams`: Paging and height filters for listing canonical blocks.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
- `CanonicalWalkParams`: `CanonicalListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `fromCursor`?: `string \| null`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `ColumnType`: Supported column types for subgraph schemas
- `ConsumerBatchContext`: What the loop knows at the end of a page, handed to `onBatch`.
  - `cursor`: `string \| null`. Checkpoint to commit alongside your rows.
  - `height`: `number \| null`. Highest canonical block a row was DELIVERED from. `null` before the first row of a fresh consume. With a sparse filter this parks at the last matching event — it measures event recency, not progress.
  - `scannedHeight`: `number \| null`. Highest canonical block the sweep has VERIFIED through: an empty page is the server confirming nothing matches between the cursor and the tip, so a caught-up tail scans to the tip even when `height` is far below it. Rolls back with reorg rewinds.
  - `tipHeight`: `number`. Chain tip as of this page's read.
  - `blocksBehind`: `number \| null`. `tipHeight - scannedHeight`, floored at 0 — the consumer's actual backlog. `null` until a position is known. A caught-up consumer on a quiet contract reads ~0 here while `tipHeight - height` grows — that difference is event age, not lag.
  - `tip`: `TTip`. The full tip for this page — `StreamsTip` or `IndexTip` depending on the surface. Exposed here so a handler never needs the `envelope` parameter, which is the only way to reach it today and is also how the worst `finalizedOnly` mistake becomes reachable (`return envelope.next_cursor` jumps the cursor past events that were filtered out and never delivered).
  - `reorgs`: `readonly TReorg[]`. Reorgs reported alongside this page; empty when none. Rollback still belongs in `onReorg` — this is for observability inside a batch.
- `ConsumerSink`: A consumer sink: the destination adapter a consume loop writes through.
  - `_tx`?: `Tx`. Phantom marker carrying `Tx` in a directly-inferable position, so the consume loops' `TTx` type parameter resolves from the `sink` option (never set at runtime).
  - `capabilities`?: `{ finalizedOnly?: boolean }`. Static capabilities, read by the loop before the first fetch to fail fast on impossible pairings.
  - `loadCursor`: `() => Promise<string \| null>`. The committed checkpoint, or `null` on first run. Called once, before the first page. Implementations may create their checkpoint storage here and SHOULD validate their rollback preconditions (e.g. that every declared table carries the height column).
  - `commitBatch`: `(cursor: string, write: (tx: Tx) => Promise<void> \| void) => Promise<void>`. Apply one batch atomically: open a transaction, run `write(tx)` (the user's inserts), and commit the rows AND `cursor` together. A throw from `write` must abort the whole transaction — leaving neither rows nor cursor, so a crashed batch is simply re-read on restart.
  - `rollback`: `(forkPointHeight: number, rewindCursor: string \| null) => Promise<void>`. Roll the projection back to the fork: delete everything AT OR ABOVE `forkPointHeight` (inclusive `>=` — the new chain re-supplies the fork block) and commit `rewindCursor` in the SAME transaction. Deleting without the rewound cursor is the classic silent-gap bug: a crash between the two writes resumes above the fork and the deleted range is never re-read. On a multi-fork page this is called once PER fork, every call with the same `rewindCursor` (the lowest fork point) but its own `forkPointHeight` — scope the undo by `forkPointHeight` only (contract invariant #11), and expect re-application after a crash (#10). `rewindCursor` is `null` only for a fork at genesis: clear the checkpoint so the next run starts from the first event.
- `ContractCallsEnvelope`: One page of contract calls, with the cursor, tip, and reorgs.
  - `contract_calls`: `IndexContractCall[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
- `ContractCallsListParams`: Paging and contract filters for listing contract calls.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `contractId`?: `string \| readonly string[]`. One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`.
  - `functionName`?: `string`
  - `sender`?: `string`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `trait`?: `string`. Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId.
- `ContractCallsWalkParams`: `ContractCallsListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `contractId`?: `string \| readonly string[]`. One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`.
  - `sender`?: `string`
  - `fromCursor`?: `string \| null`
  - `functionName`?: `string`
  - `trait`?: `string`. Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId.
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `EventsEnvelope`: One page of decoded events, with the cursor, tip, and reorgs.
  - `events`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
- `EventsListParams`: Event type, paging, and filters for listing decoded events.
  - `eventType`: `T`. Decoded types or VM types (`nested_contract_call`, `map_set`, …).
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `contractId`?: `string \| readonly string[]`. One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`.
  - `assetIdentifier`?: `string`
  - `sender`?: `string`
  - `recipient`?: `string`
  - `functionName`?: `string`
  - `caller`?: `string`
  - `map`?: `string`
  - `varName`?: `string`
  - `txId`?: `string`. Restrict to one transaction.
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `order`?: `IndexOrder`. `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder).
  - `trait`?: `string`. Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId; contract-keyed event types only.
  - `txContext`?: `boolean`. Join the submitting transaction into each event — populates `tx_sender`, `tx_type`, `tx_status`, `tx_contract_id`, `tx_function_name`. Off by default. Avoids a `/v1/index/transactions` call per event; for `print` events it's the only source of the submitting sender.
  - `fields`?: `readonly (keyof Extract<IndexFtTransfer, { event_type: T; }> & keyof Extract<IndexNftTransfer, { event_type: T; }> & keyof Extract<IndexStxTransfer, { event_type: T; }> & keyof Extract<IndexStxMint, { event_type: T; }> & keyof Extract<IndexStxBurn, { event_type: T; }> & keyof Extract<IndexStxLock, { event_type: T; }> & keyof Extract<IndexFtMint, { event_type: T; }> & keyof Extract<IndexFtBurn, { event_type: T; }> & keyof Extract<IndexNftMint, { event_type: T; }> & keyof Extract<IndexNftBurn, { event_type: T; }> & keyof Extract<IndexPrint, { event_type: T; }> & keyof Extract<IndexNestedContractCall, { event_type: T; }> & keyof Extract<IndexVarSet, { event_type: T; }> & keyof Extract<IndexMapSet, { event_type: T; }> & keyof Extract<IndexMapInsert, { event_type: T; }> & keyof Extract<IndexMapDelete, { event_type: T; }> & string)[]`. Return only these columns. The server projects the SELECT, so an unrequested field is physically absent — and the returned row type narrows to match, making a read of one a compile error rather than `undefined`. `cursor`, `block_height`, and `event_type` come back regardless: the first two are the consume contract, the third carries the discriminant. Don't list them. Cost note: this does NOT change your bill — Index meters per row read, not per field. What it buys is wire bytes, plus skipping the `blocks` join when `block_time` is omitted.
- `EventsWalkParams`: `EventsListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `contractId`?: `string \| readonly string[]`. One id, or several to scope the sweep to a set of contracts (max 20). Mutually exclusive with `trait`.
  - `sender`?: `string`
  - `recipient`?: `string`
  - `assetIdentifier`?: `string`
  - `fromCursor`?: `string \| null`
  - `functionName`?: `string`
  - `trait`?: `string`. Restrict to contracts conforming to a trait/standard (e.g. "sip-010"). Mutually exclusive with contractId; contract-keyed event types only.
  - `txContext`?: `boolean`. Join the submitting transaction into each event — populates `tx_sender`, `tx_type`, `tx_status`, `tx_contract_id`, `tx_function_name`. Off by default. Avoids a `/v1/index/transactions` call per event; for `print` events it's the only source of the submitting sender.
  - `fields`?: `readonly (keyof Extract<IndexFtTransfer, { event_type: T; }> & keyof Extract<IndexNftTransfer, { event_type: T; }> & keyof Extract<IndexStxTransfer, { event_type: T; }> & keyof Extract<IndexStxMint, { event_type: T; }> & keyof Extract<IndexStxBurn, { event_type: T; }> & keyof Extract<IndexStxLock, { event_type: T; }> & keyof Extract<IndexFtMint, { event_type: T; }> & keyof Extract<IndexFtBurn, { event_type: T; }> & keyof Extract<IndexNftMint, { event_type: T; }> & keyof Extract<IndexNftBurn, { event_type: T; }> & keyof Extract<IndexPrint, { event_type: T; }> & keyof Extract<IndexNestedContractCall, { event_type: T; }> & keyof Extract<IndexVarSet, { event_type: T; }> & keyof Extract<IndexMapSet, { event_type: T; }> & keyof Extract<IndexMapInsert, { event_type: T; }> & keyof Extract<IndexMapDelete, { event_type: T; }> & string)[]`. Return only these columns. The server projects the SELECT, so an unrequested field is physically absent — and the returned row type narrows to match, making a read of one a compile error rather than `undefined`. `cursor`, `block_height`, and `event_type` come back regardless: the first two are the consume contract, the third carries the discriminant. Don't list them. Cost note: this does NOT change your bill — Index meters per row read, not per field. What it buys is wire bytes, plus skipping the `blocks` join when `block_time` is omitted.
  - `caller`?: `string`
  - `order`?: `IndexOrder`. `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder).
  - `eventType`: `T`. Decoded types or VM types (`nested_contract_call`, `map_set`, …).
  - `map`?: `string`
  - `varName`?: `string`
  - `txId`?: `string`. Restrict to one transaction.
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `FtTransfer`: A decoded fungible-token transfer: sender, recipient, asset, amount.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `event_type`: `"ft_transfer"`
  - `contract_id`: `string`
  - `asset_identifier`: `string`
  - `sender`: `string`
  - `recipient`: `string`
  - `amount`: `string`
- `FtTransferFields`: An ft-transfer row narrowed to the requested columns plus the always-present ones.
  - `cursor`: `string`
  - `block_height`: `number`
  - `event_type`: `"ft_transfer"`
- `FtTransfersEnvelope`: One page of fungible-token transfers, with the cursor, tip, and reorgs.
  - `events`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
- `FtTransfersListParams`: Column selection, paging, and filters for listing token transfers.
  - `fields`?: `readonly ("cursor" \| "sender" \| "recipient" \| "block_height" \| "event_type" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "contract_id" \| "asset_identifier" \| "amount")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. Omitting `block_time` also lets the server skip the blocks join.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `contractId`?: `string`
  - `assetIdentifier`?: `string`
  - `sender`?: `string`
  - `recipient`?: `string`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `order`?: `IndexOrder`. `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder).
- `FtTransfersWalkParams`: `FtTransfersListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `contractId`?: `string`
  - `sender`?: `string`
  - `recipient`?: `string`
  - `assetIdentifier`?: `string`
  - `fromCursor`?: `string \| null`
  - `fields`?: `readonly ("cursor" \| "sender" \| "recipient" \| "block_height" \| "event_type" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "contract_id" \| "asset_identifier" \| "amount")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime. Omitting `block_time` also lets the server skip the blocks join.
  - `order`?: `IndexOrder`. `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder).
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `IndexBitcoinTip`: The Bitcoin chain tip a Runes read was served against — a separate clock from the Stacks [`IndexTip`](/docs/sdk-reference/index#t-IndexTip): Runes ingest tracks Bitcoin blocks, not Stacks ones.
  - `block_height`: `number`
  - `finalized_height`: `number`
  - `lag_seconds`: `number`
- `IndexBlock`: A block resource.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_hash`: `string`
  - `parent_hash`: `string`
  - `burn_block_height`: `number`
  - `burn_block_hash`: `string \| null`
  - `block_time`: `string \| null`
  - `canonical`: `boolean`
- `IndexBtcReorg`: A Bitcoin fork the Runes indexer rolled back.
  - `id`: `string`
  - `detected_at`: `string`
  - `fork_point_height`: `number`
  - `old_hash`: `string`
  - `new_hash`: `string`
  - `orphaned_from`: `number`
  - `orphaned_to`: `number`
  - `new_tip_height`: `number`
- `IndexCanonicalBlock`: One canonical block in the sync map.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_hash`: `string`
  - `parent_hash`: `string`
  - `burn_block_height`: `number`
  - `burn_block_hash`: `string \| null`
- `IndexConsumeOptions`: Consumer options shared by `index.events.consume` and `index.contractCalls.consume`.
  - `fromCursor`?: `string \| null`. Resume from a committed checkpoint. Without it (and without `fromHeight`) the API serves only the recent default window.
  - `fromHeight`?: `number`. Start a fresh sweep at this height (e.g. `0` for genesis backfill). Ignored once a cursor exists (including a sink's committed cursor).
  - `mode`?: `"tail" \| "bounded"`. `tail` (default) keeps polling at the tip; `bounded` returns on the first empty page.
  - `finalizedOnly`?: `boolean`. Emit only rows at or below the tip's `finalized_height`; the unfinalized tail is re-read each poll until it settles. Finalized data never reorgs, so `onReorg` is skipped entirely.
  - `batchSize`?: `number`
  - `sink`?: `ConsumerSink<TTx>`
  - `onProgress`?: `(ctx: ConsumerBatchContext) => void`. Fires once per page, before `onBatch` and before any early return — an empty page still proves the loop is alive. Feed it to `consumerHealth().record`.
  - `onBatch`: `(items: TItem[], envelope: TEnvelope, ctx: ConsumerBatchContext & [TTx] extends [never] ? unknown : { tx: TTx }) => string \| Promise<void> \| Promise<string \| undefined \| null> \| undefined \| null \| void`
  - `onReorg`?: `(reorg: IndexReorg, ctx: { cursor: string \| null }) => Promise<void> \| void`. Called once per new reorg at or below the checkpoint, before the loop rewinds to `ctx.cursor` (`null` = pre-genesis) and re-reads. A reorg whose fork sits ABOVE the checkpoint is not a rollback: nothing past it was written, so it is noted and the page is read as normal.
  - `maxRollbackDepth`?: `number`. Deepest rewind one reorg may make below the checkpoint before the loop refuses with `ValidationError`. Default 1000 blocks. The fork point is server supplied and drives a delete on every declared table, so raise this only for a source you trust.
  - `sleep`?: `Sleep`
  - `emptyBackoffMs`?: `number`
  - `maxPages`?: `number`
  - `maxEmptyPolls`?: `number`
  - `signal`?: `AbortSignal`
  - `retryCount`?: `number`. Page-fetch retries after the first failure (429/5xx/network only — 4xx and handler throws always propagate). Default 3; `0` disables.
  - `retryDelay`?: `number`. Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep. Cannot change the retry decision — the policy owns it.
- `IndexContractCall`: A decoded contract call: contract, function, sender, arguments, result.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `contract_id`: `string`
  - `function_name`: `string`
  - `sender`: `string`
  - `status`: `string`
  - `args`: `unknown[]`
  - `result`: `unknown`
  - `result_hex`: `string \| null`
- `IndexDiscovery`: The `GET /v1/index` discovery doc: live endpoints and filter vocabulary.
  - `event_type_filters`?: `Record<string, IndexEventTypeFilters>`
- `IndexEventFields`: An event row narrowed to a `fields` selection (plus what always ships).
  - `cursor`: `IndexEventOf<T>["cursor"]`
  - `block_height`: `IndexEventOf<T>["block_height"]`
  - `event_type`: `IndexEventOf<T>["event_type"]`
- `IndexEventOf`: The single decoded-event member matching one `event_type` literal.
- `IndexEventTypeFilters`: Per-event-type filter vocabulary in the [`IndexDiscovery`](/docs/sdk-reference/index#t-IndexDiscovery) doc.
  - `columns`?: `string[]`
  - `allowed_filters`?: `string[]`
  - `equality_filters`?: `string[]`
  - `required_non_null`?: `string[]`
- `IndexFtBurn`: A fungible-token burn event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"ft_burn"`
  - `asset_identifier`: `string`
  - `sender`: `string`
  - `amount`: `string`
- `IndexFtMint`: A fungible-token mint event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"ft_mint"`
  - `asset_identifier`: `string`
  - `recipient`: `string`
  - `amount`: `string`
- `IndexFtTransfer`: A fungible-token transfer event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"ft_transfer"`
  - `asset_identifier`: `string`
  - `sender`: `string`
  - `recipient`: `string`
  - `amount`: `string`
- `IndexMapDelete`: A contract map entry deletion.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"map_delete"`
  - `map`: `string`
  - `raw_key`: `string`
- `IndexMapInsert`: A new contract map entry.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"map_insert"`
  - `map`: `string`
  - `raw_key`: `string`
  - `raw_value`: `string`
- `IndexMapSet`: A contract map entry overwrite.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"map_set"`
  - `map`: `string`
  - `raw_key`: `string`
  - `raw_value`: `string`
- `IndexMempoolTransaction`: A pending (unconfirmed) transaction.
  - `cursor`: `string`
  - `tx_id`: `string`
  - `tx_type`: `string`
  - `sender`: `string`
  - `received_at`?: `string \| null`
  - `fee`: `string \| null`
  - `nonce`: `string \| null`
  - `sponsored`: `boolean \| null`
  - `anchor_mode`: `string \| null`
  - `post_condition_mode`: `string \| null`
  - `post_conditions`: `IndexPostCondition[]`
  - `contract_call`?: `{ contract_id: string; function_name: string; function_args: unknown[] }`
  - `token_transfer`?: `{ recipient: string; amount: string; memo: string }`
  - `smart_contract`?: `{ clarity_version: number \| null }`
  - `coinbase`?: `{ alt_recipient: string \| null }`
  - `tenure_change`?: `{ cause: number }`
- `IndexNestedContractCall`: A contract call made from inside another contract call.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"nested_contract_call"`
  - `sender`: `string \| null`
  - `caller`: `string`
  - `function_name`: `string`
  - `function_args`: `unknown`
  - `raw_result`: `string \| null`
- `IndexNftBurn`: A non-fungible-token burn event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"nft_burn"`
  - `asset_identifier`: `string`
  - `sender`: `string`
  - `value`: `string`
- `IndexNftMint`: A non-fungible-token mint event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"nft_mint"`
  - `asset_identifier`: `string`
  - `recipient`: `string`
  - `value`: `string`
- `IndexNftTransfer`: A non-fungible-token transfer event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"nft_transfer"`
  - `asset_identifier`: `string`
  - `sender`: `string`
  - `recipient`: `string`
  - `value`: `string`
- `IndexOrder`: Read direction for decoded-event lists.
- `IndexPostCondition`: `{ type: "stx"; principal: string; condition_code: number; condition_code_name: string \| null; amount: string; } \| { type: "ft"; principal: string; asset_identifier: string; condition_code: number; condition_code_name: string \| null; amount: string; } \| { type: "nft"; principal: string; asset_identifier: string; asset_value: unknown; condition_code: number; condition_code_name: string \| null; } \| { type: "staking"; principal: string; condition_code: number; condition_code_name: string \| null; amount: string; } \| { type: "pox"; principal: string; condition_code: number; condition_code_name: str…`
- `IndexPox5Event`: A raw decoded PoX-5 print event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `topic`: `IndexPox5EventTopic`
  - `staker`: `string \| null`
  - `signer`: `string \| null`
  - `signer_manager`: `string \| null`
  - `bond_index`: `number \| null`
  - `amount_ustx`: `string \| null`. ustx, bigint-safe string.
  - `amount_sats`: `string \| null`. sats, bigint-safe string.
  - `reward_cycle`: `number \| null`
  - `first_reward_cycle`: `number \| null`
  - `unlock_cycle`: `number \| null`
  - `unlock_burn_height`: `number \| null`
  - `is_l1_lock`: `boolean \| null`
  - `signer_key`: `string \| null`
  - `data`: `unknown`. The full decoded print tuple as JSON.
- `IndexPoxCycle`: One pox-5 reward cycle, exact to the node.
  - `reward_cycle`: `number`
  - `start_burn_height`: `number`. Bitcoin block at which this cycle starts.
  - `prepare_start_burn_height`: `number`. Bitcoin block at which this cycle's totals froze (the prior cycle's prepare phase).
  - `end_burn_height`: `number`. Bitcoin block at which this cycle ends.
  - `total_stacked_ustx`: `string`. STX + bond ustx delegated to signers this cycle (bigint-safe string).
  - `reward_eligible_ustx`: `string`. STX actually counted toward rewards — signers below the 50k STX threshold don't count.
  - `stakers`: `number`. Distinct stakers (STX and bond) with a position this cycle.
  - `signers_in_set`: `number`. Signers that met the 50,000 STX delegation threshold this cycle.
  - `bond_sats`: `Record<string, string>`. Bond index (as a string key) -> sats staked for this cycle.
  - `bond_total_sats`: `string`
  - `sbtc_custodied_sats`: `string`. sBTC currently custodied by pox-5 (non-L1 bonds only) — a running total, not scoped to this cycle. Not in this endpoint: unclaimed/pending rewards.
  - `rewards_allocated_stx`: `string`
  - `rewards_allocated_bond`: `string`
  - `reserve_deposit`: `string`
  - `rewards_per_token_stx`: `string \| null`. Cumulative STX rewards per micro-STX for this cycle; `null` before the first distribution.
  - `rewards_per_token_bond`: `Record<string, string>`. Bond index (as a string key) -> cumulative rewards-per-sat for this cycle.
  - `distributions`: `number`. Distributions run for this cycle so far (0, 1, or 2).
  - `rewards_claimed`: `string`
  - `computed_through_height`: `number`. Stacks block height this rollup was last computed through.
  - `is_current`: `boolean`. True for the cycle containing the tip's burn height.
  - `is_frozen`: `boolean`. True once totals can no longer change (the tip has reached `prepare_start_burn_height`). Closed and current cycles are always frozen; only a not-yet-started future cycle is open.
- `IndexPoxCycleSigner`: One signer's share of a pox-5 reward cycle.
  - `signer`: `string`
  - `delegated_ustx`: `string`. Total ustx delegated to this signer this cycle (STX + bond).
  - `stx_only_ustx`: `string`. STX-only (non-bond) ustx delegated to this signer this cycle.
  - `reward_shares_ustx`: `string`. STX shares counted toward rewards — 0 unless the signer met the 50k threshold.
  - `in_set`: `boolean`
  - `rewards_claimed`: `string`
- `IndexPrint`: A contract `print` event with its topic and value.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"print"`
  - `payload`: `{ topic: string \| null; value: unknown; raw_value: string \| null }`
- `IndexReorg`: A chain reorg overlapping a returned page's height range.
  - `id`: `string`
  - `detected_at`: `string`
  - `fork_point_height`: `number`
  - `old_index_block_hash`: `string \| null`
  - `new_index_block_hash`: `string \| null`
  - `orphaned_range`: `{ from: string; to: string }`. Orphaned cursor span `<block_height>:<tx_index>`, inclusive.
  - `new_canonical_tip`: `string`. First position of the new canonical chain at the fork, `fork:0` (INCLUSIVE). Not an exclusive resume token — resuming a `(bh,ei) > cursor` read directly from it would skip `fork:0`. To re-read the new run, rewind to the foot of `fork_point_height` (`Cursor.atHeight`), not to this value.
- `IndexRuneBalance`: A rune's balance on one unspent output, with the rune's identity embedded.
  - `rune`: `IndexRuneRefSummary`
  - `address`: `string \| null`. The output's mainnet address, or `null` for a non-standard script.
  - `txid`: `string`
  - `vout`: `number`
  - `amount`: `string`. u128 decimal string. Never `Number()`.
- `IndexRuneEntry`: One etched rune: identity, counters, supply, and mint terms.
  - `id`: `string`
  - `name`: `string`. Spacers stripped, uppercase.
  - `spaced_name`: `string`. As etched, spacers (`•`) included.
  - `symbol`: `string \| null`
  - `divisibility`: `number`
  - `number`: `string`. Etch sequence number (u128 decimal string) — 0 is genesis.
  - `premine`: `string`. Premined amount, u128 decimal string. Never `Number()`.
  - `supply`: `string`. Computed: `premine + mints × terms.amount` (0 with no terms). u128 decimal string.
  - `burned`: `string`
  - `mints`: `string`
  - `turbo`: `boolean`
  - `etching_txid`: `string`
  - `etched_height`: `number`
  - `etched_tx_index`: `number`
  - `terms`: `IndexRuneTerms \| null`
- `IndexRuneEvent`: One rune etch, mint, transfer, or burn, with the rune's identity.
  - `cursor`: `string`
  - `block_height`: `number`
  - `tx_index`: `number`
  - `txid`: `string`
  - `event_index`: `number`
  - `kind`: `IndexRuneEventKind`
  - `amount`: `string`. u128 decimal string. Never `Number()`.
  - `vout`: `number \| null`
  - `address`: `string \| null`. The output's mainnet address. Only ever set on a `rune_transfer` event.
  - `rune`: `IndexRuneRefSummary`
- `IndexRuneEventKind`: `"rune_etch" \| "rune_mint" \| "rune_transfer" \| "rune_burn"`
- `IndexRuneRefSummary`: The rune identity bundle every Runes response embeds, so a client never needs a second lookup just to show a name next to an event or a balance.
  - `id`: `string`
  - `name`: `string`. Spacers stripped, uppercase.
  - `spaced_name`: `string`. As etched, spacers (`•`) included.
  - `symbol`: `string \| null`
  - `divisibility`: `number`
- `IndexRuneTerms`: Mint terms, or `null` for a premine-only rune that was never mintable.
  - `amount`: `string \| null`
  - `cap`: `string \| null`
  - `height_start`: `string \| null`
  - `height_end`: `string \| null`
  - `offset_start`: `string \| null`
  - `offset_end`: `string \| null`
- `IndexSbtcDeposit`: A completed sBTC peg-in, keyed by `bitcoin_txid` (deposits carry no `request_id`).
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `amount`: `string \| null`
  - `sender`: `string \| null`
  - `bitcoin_txid`: `string \| null`
  - `output_index`: `number \| null`
  - `recipient_btc_version`: `number \| null`
  - `recipient_btc_hashbytes`: `string \| null`
- `IndexSbtcDepositDetail`: A deposit fetched by Bitcoin txid — always terminal, hence `status`.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `amount`: `string \| null`
  - `sender`: `string \| null`
  - `bitcoin_txid`: `string \| null`
  - `output_index`: `number \| null`
  - `recipient_btc_version`: `number \| null`
  - `recipient_btc_hashbytes`: `string \| null`
  - `status`: `"COMPLETED"`
- `IndexSbtcEvent`: A raw decoded sBTC protocol-state event, one per event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `topic`: `SbtcEventTopic`
  - `request_id`: `number \| null`
  - `amount`: `string \| null`
  - `sender`: `string \| null`
  - `recipient_btc_version`: `number \| null`
  - `recipient_btc_hashbytes`: `string \| null`
  - `bitcoin_txid`: `string \| null`
  - `output_index`: `number \| null`
  - `sweep_txid`: `string \| null`
  - `burn_hash`: `string \| null`
  - `burn_height`: `number \| null`
  - `signer_bitmap`: `string \| null`
  - `max_fee`: `string \| null`
  - `fee`: `string \| null`
  - `governance_contract_type`: `number \| null`
  - `governance_new_contract`: `string \| null`
  - `signer_aggregate_pubkey`: `string \| null`
  - `signer_threshold`: `number \| null`
  - `signer_address`: `string \| null`
  - `signer_keys_count`: `number \| null`
- `IndexSbtcSummary`: The peg "scoreboard" — a single all-time canonical aggregate.
  - `total_deposits`: `number`
  - `total_withdrawals_requested`: `number`
  - `total_withdrawals_accepted`: `number`
  - `total_withdrawals_rejected`: `number`
  - `net_peg_flow_sats`: `string`
  - `total_locked_sats`: `string`
  - `sbtc_supply_sats`: `string \| null`
- `IndexSbtcWithdrawal`: A peg-out as one row per request, with its latest status.
  - `cursor`: `string`
  - `request_id`: `number`
  - `status`: `SbtcWithdrawalStatus`
  - `amount`: `string \| null`
  - `sender`: `string \| null`
  - `recipient_btc_version`: `number \| null`
  - `recipient_btc_hashbytes`: `string \| null`
  - `sweep_txid`: `string \| null`
  - `settlement_confirmed`: `boolean \| null`. BTC L1 settlement of the committed sweep: true once confirmed, false while pending, null when there is no sweep yet (REQUESTED).
  - `btc_confirmations`: `number \| null`. BTC confirmations on the sweep tx, null when no settlement row yet.
  - `btc_block_height`: `number \| null`. Confirming BTC block height, null while unconfirmed / no sweep.
  - `confirmed_at`: `string \| null`. When the sweep crossed the confirmation threshold (ISO), null otherwise.
  - `requested_at`?: `string \| null`
  - `resolved_at`?: `string \| null`
- `IndexSbtcWithdrawalDetail`: A single peg-out's full assembled lifecycle, fetched by `request_id`.
  - `request_id`: `number`
  - `status`: `SbtcWithdrawalStatus`
  - `amount`: `string \| null`
  - `sender`: `string \| null`
  - `recipient_btc_version`: `number \| null`
  - `recipient_btc_hashbytes`: `string \| null`
  - `requested`: `IndexSbtcWithdrawalPhase`
  - `accepted`: `(IndexSbtcWithdrawalPhase & { sweep_txid: string \| null; signer_bitmap: string \| null; }) \| null`
  - `rejected`: `IndexSbtcWithdrawalPhase \| null`
  - `settlement`: `{ sweep_txid: string \| null; btc_confirmations: number \| null; settlement_confirmed: boolean \| null; btc_block_height: number \| null; confirmed_at: string \| null }`
  - `finalized`: `boolean`
- `IndexSbtcWithdrawalPhase`: One phase of a withdrawal's lifecycle (the on-Stacks event that drove it).
  - `block_height`: `number`
  - `block_time`: `string \| null`
  - `tx_id`: `string`
- `IndexStackingAction`: A decoded PoX-4 stacking action (one per stacking contract call).
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `burn_block_height`: `number`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `function_name`: `string`
  - `caller`: `string`
  - `stacker`: `string \| null`
  - `delegate_to`: `string \| null`
  - `amount_ustx`: `string \| null`
  - `lock_period`: `number \| null`
  - `pox_addr`: `{ version: number \| null; hashbytes: string \| null; btc: string \| null }`
  - `start_cycle`: `number \| null`
  - `end_cycle`: `number \| null`
  - `reward_cycle`: `number \| null`
  - `signer_key`: `string \| null`
  - `result_ok`: `boolean`
- `IndexStateWrite`: One MARF write a block committed, in the node's write order.
  - `cursor`: `string`. `<block_height>:<ordinal>`.
  - `block_height`: `number`
  - `ordinal`: `number`. Position in the block's write order: a separate clock from `event_index`.
  - `tx_index`: `number \| null`. Null for block-level writes.
  - `key`: `string`. Full MARF key, e.g. `vm::<contract>::0::map::<hex>`.
  - `value_hex`: `string`
  - `tx_id`?: `string \| null`. The writing transaction, present only when `txContext: true` was passed; null for block-level writes.
  - `tx_sender`?: `string \| null`
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
- `IndexStxBurn`: An STX burn event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"stx_burn"`
  - `sender`: `string`
  - `amount`: `string`
- `IndexStxLock`: An STX lock event with its unlock height.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"stx_lock"`
  - `sender`: `string`
  - `amount`: `string`
  - `payload`: `{ unlock_height: string \| null }`
- `IndexStxMint`: An STX mint event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"stx_mint"`
  - `recipient`: `string`
  - `amount`: `string`
- `IndexStxTransfer`: An STX transfer event.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"stx_transfer"`
  - `sender`: `string`
  - `recipient`: `string`
  - `amount`: `string`
  - `memo`: `string \| null`
- `IndexTip`
  - `block_height`: `number`
  - `finalized_height`: `number`. Highest height treated as immutable (past the burn-confirmation finality boundary). Rows at or below it never reorg — `finalizedOnly` consumers gate on this, since Index rows carry no per-event flag.
  - `lag_seconds`: `number`
- `IndexTransaction`: Full transaction document: columnar fields plus `raw_tx`-decoded enrichment.
  - `cursor`: `string`
  - `tx_id`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_index`: `number`
  - `tx_type`: `string`
  - `sender`: `string`
  - `status`: `string`
  - `fee`: `string \| null`
  - `nonce`: `string \| null`
  - `sponsored`: `boolean \| null`
  - `anchor_mode`: `string \| null`
  - `post_condition_mode`: `string \| null`
  - `post_conditions`: `IndexPostCondition[]`
  - `contract_call`?: `{ contract_id: string; function_name: string; function_args: unknown[]; function_args_hex: string[]; result: unknown; result_hex: string \| null }`
  - `token_transfer`?: `{ recipient: string; amount: string; memo: string }`
  - `smart_contract`?: `{ contract_id: string \| null; clarity_version: number \| null }`
  - `coinbase`?: `{ alt_recipient: string \| null }`
  - `tenure_change`?: `{ cause: number }`
- `IndexVarSet`: A contract data variable write.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `contract_id`: `string \| null`
  - `tx_sender`?: `string \| null`. Submitting-transaction context, present only when `txContext: true` was requested. `tx_sender` is the real tx sender — distinct from a transfer's asset `sender`, and the only place a `print` event's sender is available. Lets a consumer build per-event tx context without a `/v1/index/transactions` call per event.
  - `tx_type`?: `string \| null`
  - `tx_status`?: `string \| null`
  - `tx_contract_id`?: `string \| null`
  - `tx_function_name`?: `string \| null`
  - `event_type`: `"var_set"`
  - `var_name`: `string`
  - `raw_value`: `string`
- `InferredPrintField`
  - `name`: `string`. Original kebab-case tuple key (the `topic` discriminant is excluded)
  - `camel_name`: `string`. What handlers see on `e.data` — exact runner camelization
  - `clarity_type`: `string`. Rendered Clarity type; buff/string lengths are the max observed
  - `ts_type`: `string`. Decoded handler value type (uint→bigint, buffer→string, …)
  - `column_type`: `ColumnType`
  - `always_present`: `boolean`. Present in 100% of this topic's decoded samples
  - `optional_some_rate`?: `number`. Only when optional observed: share of present samples that were some
- `InferredTopicSchema`: The fields seen in one `print` topic, with counts and heights.
  - `topic`: `string`
  - `count`: `number`
  - `first_height`: `number`
  - `last_height`: `number`
  - `non_tuple`: `boolean`
  - `fields`: `InferredPrintField[]`
- `MempoolEnvelope`: One page of pending transactions, with the next cursor and chain tip.
  - `mempool`: `IndexMempoolTransaction[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
- `MempoolListParams`: Paging and sender filters for listing pending transactions.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `sender`?: `string`
  - `type`?: `string`
  - `contractId`?: `string`. Filter to pending calls to a single contract (e.g. `SP….contract`).
  - `functionName`?: `string`. Filter to pending calls to a single function (composes with `contractId`).
- `MempoolTransactionEnvelope`: One pending transaction and the chain tip.
  - `transaction`: `IndexMempoolTransaction`
  - `tip`: `IndexTip`
- `MempoolWalkParams`: `MempoolListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `contractId`?: `string`. Filter to pending calls to a single contract (e.g. `SP….contract`).
  - `sender`?: `string`
  - `fromCursor`?: `string \| null`
  - `functionName`?: `string`. Filter to pending calls to a single function (composes with `contractId`).
  - `type`?: `string`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `MerkleProofStep`: One authentication-path step: the sibling hash and which side it's on.
  - `position`: `"left" \| "right"`. Side the SIBLING is on relative to the accumulator.
  - `hash`: `string`. Sibling node hash (hex).
- `NftTransfer`: A decoded non-fungible-token transfer: sender, recipient, asset, value.
  - `cursor`: `string`
  - `block_height`: `number`
  - `block_time`?: `string \| null`
  - `tx_id`: `string`
  - `tx_index`: `number`
  - `event_index`: `number`
  - `event_type`: `"nft_transfer"`
  - `contract_id`: `string`
  - `asset_identifier`: `string`
  - `sender`: `string`
  - `recipient`: `string`
  - `value`: `string`
- `NftTransferFields`: An nft-transfer row narrowed to the requested columns plus the always-present ones.
  - `cursor`: `string`
  - `block_height`: `number`
  - `event_type`: `"nft_transfer"`
- `NftTransfersEnvelope`: One page of non-fungible-token transfers, with the cursor, tip, and reorgs.
  - `events`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
- `NftTransfersListParams`: Column selection, paging, and filters for listing NFT transfers.
  - `fields`?: `readonly ("cursor" \| "sender" \| "recipient" \| "block_height" \| "event_type" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "contract_id" \| "asset_identifier" \| "value")[]`. Columns to return (see `FtTransfersListParams.fields`).
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `contractId`?: `string`
  - `assetIdentifier`?: `string`
  - `sender`?: `string`
  - `recipient`?: `string`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `order`?: `IndexOrder`. `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder).
- `NftTransfersWalkParams`: `NftTransfersListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `contractId`?: `string`
  - `sender`?: `string`
  - `recipient`?: `string`
  - `assetIdentifier`?: `string`
  - `fromCursor`?: `string \| null`
  - `fields`?: `readonly ("cursor" \| "sender" \| "recipient" \| "block_height" \| "event_type" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "contract_id" \| "asset_identifier" \| "value")[]`. Columns to return (see `FtTransfersListParams.fields`).
  - `order`?: `IndexOrder`. `"desc"` reads newest first. See [`IndexOrder`](/docs/sdk-reference/index#t-IndexOrder).
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `PageRetryOptions`: Options shared by both consume loops' page-fetch retry.
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
- `Pox5EventFields`: A Pox5Event row narrowed to the requested columns plus the always-present ones.
  - `cursor`: `string`
  - `block_height`: `number`
  - `topic`: `IndexPox5EventTopic`
- `Pox5EventsEnvelope`: One page of PoX-5 events, with the cursor, tip, and reorgs.
  - `events`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
  - `notes`?: `string`. Present only when the PoX-5 decoder is disabled, explaining an empty feed.
- `Pox5EventsListParams`: Column selection, paging, and filters for listing PoX-5 events.
  - `fields`?: `readonly ("cursor" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "topic" \| "staker" \| "signer" \| "signer_manager" \| "bond_index" \| "amount_ustx" \| "amount_sats" \| "reward_cycle" \| "first_reward_cycle" \| "unlock_cycle" \| "unlock_burn_height" \| "is_l1_lock" \| "signer_key" \| "data")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `confirmed`?: `boolean`. Clamp to the finality boundary — only rows past the reorg margin.
  - `topic`?: `IndexPox5EventTopic`
  - `staker`?: `string`
  - `signer`?: `string`
  - `signerManager`?: `string`
  - `bondIndex`?: `number`
  - `rewardCycle`?: `number`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
- `Pox5EventsWalkParams`: `Pox5EventsListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `fromCursor`?: `string \| null`
  - `fields`?: `readonly ("cursor" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "topic" \| "staker" \| "signer" \| "signer_manager" \| "bond_index" \| "amount_ustx" \| "amount_sats" \| "reward_cycle" \| "first_reward_cycle" \| "unlock_cycle" \| "unlock_burn_height" \| "is_l1_lock" \| "signer_key" \| "data")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
  - `confirmed`?: `boolean`. Clamp to the finality boundary — only rows past the reorg margin.
  - `topic`?: `IndexPox5EventTopic`
  - `staker`?: `string`
  - `signer`?: `string`
  - `signerManager`?: `string`
  - `bondIndex`?: `number`
  - `rewardCycle`?: `number`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `PoxCycleEnvelope`: One reward cycle with its signers and the chain tip.
  - `pox_version`: `5`
  - `cycle`: `IndexPoxCycle & { signers: IndexPoxCycleSigner[] }`
  - `tip`: `IndexTip`
- `PoxCyclesEnvelope`: One page of reward cycles, newest first, with the next cursor.
  - `pox_version`: `5`. The PoX version this response reports. Currently 5.
  - `cycles`: `IndexPoxCycle[]`
  - `next_cursor`: `number \| null`. The next `reward_cycle` to page from (cycles descend), or null at the end.
  - `tip`: `IndexTip`
- `PoxCyclesListParams`: Paging for listing reward cycles.
  - `cursor`?: `number \| null`. A `reward_cycle` to page from; the page returns cycles below it (descending).
  - `limit`?: `number`
- `PoxCyclesWalkParams`: `PoxCyclesListParams` without `limit`, plus walk options.
  - `cursor`?: `number \| null`. A `reward_cycle` to page from; the page returns cycles below it (descending).
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `PrintSchemaResponse`: Per-topic print payload schema for a contract, inferred from sampled events.
  - `contract_id`: `string`
  - `topics`: `InferredTopicSchema[]`
  - `sampled`: `boolean`
  - `total_events`: `number`
  - `total_events_capped`: `boolean`. True when the count hit the server-side cap (total_events is the cap).
  - `sample`: `{ size: number; newest_height: number \| null; oldest_height: number \| null }`
  - `tip`: `IndexTip`
- `RewardSet`
  - `signers`: `RewardSetSigner[]`
  - `total_weight`: `number`
- `RewardSetSigner`
  - `signing_key`: `string`. 33-byte compressed secp256k1 public key (hex, no 0x).
  - `weight`: `number`
- `RuneActivityEnvelope`: One page of rune events, with the cursor, Bitcoin tip, and reorgs.
  - `events`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexBitcoinTip`
  - `reorgs`: `IndexBtcReorg[]`
  - `notes`?: `string`
- `RuneActivityListParams`: Paging and height filters for listing rune events.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `limit`?: `number`
  - `rune`?: `RuneRef`
  - `address`?: `string`
  - `kind`?: `readonly IndexRuneEventKind[]`
  - `txid`?: `string`
- `RuneActivityWalkParams`: `RuneActivityListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `fromCursor`?: `string \| null`
  - `rune`?: `RuneRef`
  - `address`?: `string`
  - `kind`?: `readonly IndexRuneEventKind[]`
  - `txid`?: `string`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `RuneBalancesEnvelope`: One page of rune balances, with the cursor and Bitcoin tip.
  - `balances`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexBitcoinTip`
  - `reorgs`: `IndexBtcReorg[]`. Always empty — see `RunesEnvelope.reorgs`.
  - `notes`?: `string`
- `RuneBalancesListParams`: Rune balance filters; exactly one of `address` or `outpoint`.
- `RuneEnvelope`: One rune and the Bitcoin tip.
  - `rune`: `IndexRuneEntry`
  - `tip`: `IndexBitcoinTip`
- `RuneRef`: A rune id or name, in any case, with or without spacers.
- `RunesEnvelope`: One page of runes, with the cursor and Bitcoin tip.
  - `runes`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexBitcoinTip`
  - `reorgs`: `IndexBtcReorg[]`. Always empty: `list` reads a reorg-corrected snapshot table, not an append-only log — there is no stale page to reconcile.
  - `notes`?: `string`. Present only when Runes data is not configured on this instance.
- `RunesListParams`: Name search, sort order, and paging for listing runes.
  - `search`?: `string`. Name prefix, spacers and case ignored (`dog` matches `DOG•GO•TO•THE•MOON`).
  - `sort`?: `"number" \| "mints"`. `number` (etch order, oldest first — default) or `mints` (busiest first).
  - `cursor`?: `string \| null`
  - `limit`?: `number`
- `SbtcDepositEnvelope`: One sBTC deposit and the chain tip.
  - `deposit`: `IndexSbtcDepositDetail`
  - `tip`: `IndexTip`
- `SbtcDepositFields`: A deposit row narrowed to the requested columns plus the always-present ones.
  - `cursor`: `string`
  - `block_height`: `number`
- `SbtcDepositsEnvelope`: One page of sBTC deposits, with the cursor, tip, and reorgs.
  - `deposits`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
  - `notes`?: `string`. Present only when the sBTC decoder is disabled, explaining an empty feed.
- `SbtcDepositsListParams`: Column selection, paging, and filters for listing sBTC deposits.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `confirmed`?: `boolean`. Clamp to the finality boundary — only settled deposits past the reorg margin.
  - `sender`?: `string`
  - `bitcoinTxid`?: `string`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `fields`?: `readonly ("cursor" \| "sender" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "amount" \| "bitcoin_txid" \| "output_index" \| "recipient_btc_version" \| "recipient_btc_hashbytes")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
- `SbtcDepositsWalkParams`: `SbtcDepositsListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `sender`?: `string`
  - `fromCursor`?: `string \| null`
  - `fields`?: `readonly ("cursor" \| "sender" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "event_index" \| "amount" \| "bitcoin_txid" \| "output_index" \| "recipient_btc_version" \| "recipient_btc_hashbytes")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
  - `confirmed`?: `boolean`. Clamp to the finality boundary — only settled deposits past the reorg margin.
  - `bitcoinTxid`?: `string`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `SbtcEventsEnvelope`: One page of sBTC events, with the cursor, tip, and reorgs.
  - `events`: `IndexSbtcEvent[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
  - `notes`?: `string`
- `SbtcEventsListParams`: Paging and filters for listing sBTC events.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `confirmed`?: `boolean`
  - `topic`?: `SbtcEventTopic`
  - `sender`?: `string`
  - `requestId`?: `number`
  - `bitcoinTxid`?: `string`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
- `SbtcEventsWalkParams`: `SbtcEventsListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `sender`?: `string`
  - `fromCursor`?: `string \| null`
  - `confirmed`?: `boolean`
  - `bitcoinTxid`?: `string`
  - `requestId`?: `number`
  - `topic`?: `SbtcEventTopic`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `SbtcSummaryEnvelope`: The sBTC totals and the chain tip.
  - `summary`: `IndexSbtcSummary`
  - `tip`: `IndexTip`
  - `notes`?: `string`
- `SbtcWithdrawalEnvelope`: One sBTC withdrawal and the chain tip.
  - `withdrawal`: `IndexSbtcWithdrawalDetail`
  - `tip`: `IndexTip`
- `SbtcWithdrawalFields`: A SbtcWithdrawal row narrowed to the requested columns plus the always-present ones.
  - `cursor`: `string`
  - `request_id`: `number`
- `SbtcWithdrawalsEnvelope`: One page of sBTC withdrawals, with the cursor, tip, and reorgs.
  - `withdrawals`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
  - `notes`?: `string`
- `SbtcWithdrawalsListParams`: Column selection, paging, and filters for listing sBTC withdrawals.
  - `fields`?: `readonly ("cursor" \| "sender" \| "amount" \| "status" \| "recipient_btc_version" \| "recipient_btc_hashbytes" \| "request_id" \| "sweep_txid" \| "settlement_confirmed" \| "btc_confirmations" \| "btc_block_height" \| "confirmed_at" \| "requested_at" \| "resolved_at")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `confirmed`?: `boolean`
  - `status`?: `SbtcWithdrawalStatus`
  - `sender`?: `string`
  - `requestId`?: `number`
  - `settlementConfirmed`?: `boolean`. Filter by BTC L1 settlement: true → only confirmed sweeps; false → not yet confirmed (pending or no sweep).
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
- `SbtcWithdrawalStatus`: `"REQUESTED" \| "ACCEPTED" \| "REJECTED"`
- `SbtcWithdrawalsWalkParams`: `SbtcWithdrawalsListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `sender`?: `string`
  - `fromCursor`?: `string \| null`
  - `status`?: `SbtcWithdrawalStatus`
  - `fields`?: `readonly ("cursor" \| "sender" \| "amount" \| "status" \| "recipient_btc_version" \| "recipient_btc_hashbytes" \| "request_id" \| "sweep_txid" \| "settlement_confirmed" \| "btc_confirmations" \| "btc_block_height" \| "confirmed_at" \| "requested_at" \| "resolved_at")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
  - `confirmed`?: `boolean`
  - `requestId`?: `number`
  - `settlementConfirmed`?: `boolean`. Filter by BTC L1 settlement: true → only confirmed sweeps; false → not yet confirmed (pending or no sweep).
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `Sleep`: `(ms: number, signal?: AbortSignal) => Promise<void>`
- `StackingEnvelope`: One page of stacking actions, with the cursor, tip, and reorgs.
  - `stacking`: `IndexStackingAction[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
  - `notes`?: `string`. Present only when the PoX-4 decoder is disabled, explaining an empty feed.
- `StackingListParams`: Paging and filters for listing stacking actions.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `functionName`?: `string`
  - `stacker`?: `string`
  - `caller`?: `string`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
- `StackingWalkParams`: `StackingListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `fromCursor`?: `string \| null`
  - `functionName`?: `string`
  - `stacker`?: `string`
  - `caller`?: `string`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `StateWritesEnvelope`: One page of contract state writes, with the cursor, tip, and reorgs.
  - `state_writes`: `IndexStateWrite[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
- `StateWritesListParams`: Paging and filters for listing contract state writes.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `blockHeight`?: `number`. One block. Excludes `fromHeight`/`toHeight`; a cursor must sit at it.
  - `contractId`?: `string`. One contract (`<address>.<name>`): only keys under `vm::<contractId>::`.
  - `txContext`?: `boolean`. Join the writing transaction's `tx_*` fields onto each row.
- `StateWritesWalkParams`: `StateWritesListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `contractId`?: `string`. One contract (`<address>.<name>`): only keys under `vm::<contractId>::`.
  - `fromCursor`?: `string \| null`
  - `blockHeight`?: `number`. One block. Excludes `fromHeight`/`toHeight`; a cursor must sit at it.
  - `txContext`?: `boolean`. Join the writing transaction's `tx_*` fields onto each row.
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`
- `TransactionEnvelope`: One transaction and the chain tip.
  - `transaction`: `IndexTransaction`
  - `tip`: `IndexTip`
- `TransactionFields`: A Transaction row narrowed to the requested columns plus the always-present ones.
  - `cursor`: `string`
  - `block_height`: `number`
  - `tx_id`: `string`
- `TransactionProof`: Trustless transaction-inclusion proof verification.
  - `txid`: `string`
  - `index_block_hash`: `string`
  - `block_height`: `number`
  - `tx_index`: `number`
  - `raw_tx`: `string`. Raw consensus-serialized transaction bytes (hex).
  - `raw_header`: `string`. Raw Nakamoto block-header bytes (hex) — parsed + re-hashed by the verifier.
  - `tx_merkle_path`: `MerkleProofStep[]`. Authentication path from the tx leaf to `tx_merkle_root`.
  - `consensus`?: `{ reward_cycle: number; reward_set: RewardSet }`. Present when consensus-level verification is available: the reward cycle and its signer set, against which the header's signer signatures are checked.
- `TransactionsEnvelope`: One page of transactions, with the cursor, tip, and reorgs.
  - `transactions`: `TRow[]`
  - `next_cursor`: `string \| null`
  - `tip`: `IndexTip`
  - `reorgs`: `IndexReorg[]`
- `TransactionsListParams`: Column selection, paging, and filters for listing transactions.
  - `fields`?: `readonly ("cursor" \| "sender" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "tx_type" \| "status" \| "fee" \| "nonce" \| "sponsored" \| "anchor_mode" \| "post_condition_mode" \| "post_conditions" \| "contract_call" \| "token_transfer" \| "smart_contract" \| "coinbase" \| "tenure_change")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
  - `cursor`?: `string \| null`
  - `fromCursor`?: `string \| null`
  - `limit`?: `number`
  - `type`?: `string`
  - `sender`?: `string`
  - `contractId`?: `string`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
- `TransactionsWalkParams`: `TransactionsListParams` without `limit`, plus walk options.
  - `cursor`?: `string \| null`
  - `fromHeight`?: `number`
  - `toHeight`?: `number`
  - `contractId`?: `string`
  - `sender`?: `string`
  - `fromCursor`?: `string \| null`
  - `fields`?: `readonly ("cursor" \| "sender" \| "block_height" \| "block_time" \| "tx_id" \| "tx_index" \| "tx_type" \| "status" \| "fee" \| "nonce" \| "sponsored" \| "anchor_mode" \| "post_condition_mode" \| "post_conditions" \| "contract_call" \| "token_transfer" \| "smart_contract" \| "coinbase" \| "tenure_change")[]`. Columns to return. The server projects the row, so an unrequested column is physically absent — and the narrowing overload on `list` makes reading one a compile error rather than `undefined` at runtime.
  - `type`?: `string`
  - `retryCount`?: `number`. Retries after the first failure. Default 3; `0` disables.
  - `retryDelay`?: `number`. Base delay in ms; the n-th retry waits `retryDelay * n` (matches the stacks transport). A server `Retry-After` overrides it. Default 1000.
  - `onError`?: `(err: unknown, ctx: { attempt: number; retriesLeft: number; delayMs: number }) => void`. Void observer, called before each retry sleep (metrics/logging). The retry policy owns the decision; this cannot change it.
  - `batchSize`?: `number`. Rows per page request. Default 200, max `INDEX_MAX_PAGE_SIZE` (1000).
  - `signal`?: `AbortSignal`

## Notes for agents

- Methods live on 'sl.index' of 'const sl = new SecondLayer()'. Signatures and overloads are generated from the SDK source.
- Most feeds come in three forms: list (one page), walk (every page) and, for some, consume (a checkpointed loop).
- A method's REST twin is linked on its entry; the Index API page serves the same data over HTTP.

## 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`
