> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withconvexity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Blockchain Events webhooks & capabilities

> Capabilities that gate Blockchain Events operations, supported networks, the event payload your webhook receives, and how to verify its signature.

The Blockchain Events service watches contracts on supported chains, decodes the events you subscribe
to, and delivers them to your webhook as signed payloads. This guide covers the
cross-cutting concerns that apply across the [Blockchain Events endpoints](/api-reference/indexer/list-event-catalog):
the capabilities that authorize each operation, the supported networks, the event
payload your webhook receives, and how to verify its signature.

<Info>
  **Project context required**

  Every Blockchain Events endpoint requires a token bound to a project and business. A token
  without that context returns `401: Token is missing project or business context`.
</Info>

## Capabilities

Each operation is gated on a capability granted by your plan. A missing capability
returns `403: Missing required capability: <capability>`.

| Capability                           | Grants                                    |
| ------------------------------------ | ----------------------------------------- |
| `indexer.subscribe.evm`              | Create EVM subscriptions.                 |
| `indexer.subscribe.solana`           | Create Solana subscriptions.              |
| `indexer.subscription.list`          | List subscriptions and the event catalog. |
| `indexer.subscription.read`          | Read a single subscription.               |
| `indexer.subscription.update`        | Update a subscription.                    |
| `indexer.subscription.delete`        | Delete a subscription.                    |
| `indexer.subscription.rotate_secret` | Rotate a subscription's signing secret.   |
| `indexer.history.read`               | Read delivery logs.                       |
| `indexer.webhook.replay`             | Re-send (replay) delivery logs.           |

## Supported networks

`ATC` · `BSC` · `ETH` · `BASE` · `POL` · `SOL` · `LISK`

## Receiving events

When a subscribed event is decoded, the service sends an HTTP `POST` to your
subscription's `webhookUrl` with a JSON body and an `X-Indexer-Signature` header.
Acknowledge with any `2xx` response; a non-2xx response or a timeout is recorded as a
**failed** delivery and may be retried automatically. No fixed attempt count or
backoff schedule is published for these retries, so don't rely on a specific number
of attempts: inspect every delivery attempt and manually replay any failures via the
delivery logs (gated on `indexer.history.read` and `indexer.webhook.replay`). See
[Webhook signatures](/getting-started/conventions#webhook-signatures) for how this
compares to Wallet's documented retry schedule.

```json theme={null}
{
  "id": "0x9c3eab12...d41a0000:7",
  "network": "BASE",
  "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  "eventType": "transfer",
  "transactionHash": "0x9c3eab12...d41a0000",
  "blockNumber": 18234567,
  "from": "0x1111111111111111111111111111111111111111",
  "to": "0x2222222222222222222222222222222222222222",
  "amount": "1500000",
  "status": "success",
  "timestamp": 1781773262000,
  "decoded": { "from": "0x1111...", "to": "0x2222...", "value": "1500000" }
}
```

| Field             | Type      | Description                                    |
| ----------------- | --------- | ---------------------------------------------- |
| `id`              | `string`  | Unique event id (`txHash:logIndex`).           |
| `network`         | `string`  | Source network (e.g. `BASE`).                  |
| `contractAddress` | `string`  | Contract that emitted the event.               |
| `eventType`       | `string`  | Catalog key (e.g. `transfer`).                 |
| `transactionHash` | `string`  | Source transaction hash.                       |
| `blockNumber`     | `integer` | Block the event was included in.               |
| `from` / `to`     | `string`  | Event participants (for transfer-like events). |
| `amount`          | `string`  | Token amount in the token's smallest unit.     |
| `status`          | `string`  | Event status (e.g. `success`).                 |
| `timestamp`       | `integer` | Event time, epoch milliseconds.                |
| `decoded`         | `object`  | Decoded event fields, keyed by parameter name. |

## Verifying webhook signatures

Each delivered webhook carries an `X-Indexer-Signature` header computed with the
subscription's signing secret. The secret is returned **only once**, when you
[create a subscription](/api-reference/indexer/create-subscription) or
[rotate the secret](/api-reference/indexer/rotate-signing-secret). Store it securely.

To verify a delivery, recompute the HMAC over the **raw** request body and compare it
to the header value in constant time. Reject any request whose signature does not
match before trusting the payload.

<Warning>
  Verify against the exact bytes you received. Parsing and re-serializing the JSON
  before hashing will change the body and break the comparison.
</Warning>

```js theme={null}
import crypto from "node:crypto";

// `rawBody` is the unparsed request body (a Buffer or string).
function verifyIndexerSignature(rawBody, signatureHeader, signingSecret) {
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(rawBody)
    .digest("hex");

  const a = Buffer.from(signatureHeader, "utf8");
  const b = Buffer.from(expected, "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

<Note>
  Signatures are **HMAC-SHA256** over the raw request body, hex-encoded: exactly what
  the snippet above computes.
</Note>
