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

# Webhook concepts

> How Mobula's GraphQL webhooks work — common envelope, delivery, signing, and the per-category payloads for the four supported event types.

Mobula's GraphQL webhooks let you receive on-chain events at a callback URL of your choice. After [createWebhooks](/api-reference/graphql/mutations/createWebhooks), Mobula POSTs a JSON envelope to that URL whenever a matching event is observed.

This page documents the wire-level behaviour — what we send, how we sign and dedupe it, and the exact payload for each of the four supported categories.

**Related:** [createWebhooks](/api-reference/graphql/mutations/createWebhooks) • [getWebhooks](/api-reference/graphql/queries/getWebhooks) • [deleteWebhooks](/api-reference/graphql/mutations/deleteWebhooks).

## Delivery and retries

Each event triggers an HTTP `POST` with `Content-Type: application/json`. Your endpoint should respond with a 2xx status as quickly as possible — long-running work belongs on a queue downstream of your receiver.

If delivery fails (non-2xx, timeout, connection error), Mobula retries with exponentially increasing delays. Defaults: up to **2 retries**, starting at **1 second**, capped at **30 seconds**, with a total budget of **300 seconds**. Override per-webhook via [RetrySettingsInput](/api-reference/graphql/types/RetrySettingsInput).

Delivery is **at-least-once**. The envelope's `deduplicationId` is deterministic — store it on your side and skip events you've already processed.

## Message structure

Every payload — across all four categories, single and batch — carries the same top-level keys:

| Field             | Type              | Notes                                                                                                                                                               |
| ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`            | `string`          | Category id. Single delivery: `TOKEN_PAIR_EVENT`, `TOKEN_PRICE_EVENT`, `MARKET_CAP_EVENT`, `TOKEN_TRANSFER_EVENT`. Batch delivery: same names with `_BATCH` suffix. |
| `webhookId`       | `string`          | The webhook id (matches the value returned by `createWebhooks` / `getWebhooks`).                                                                                    |
| `webhook`         | `object`          | `{ id, name }`. Always present on single envelopes. On batch envelopes only `TOKEN_TRANSFER_EVENT_BATCH` includes it.                                               |
| `groupId`         | `string \| null`  | The webhook's `bucketKey.bucketId`. For `TOKEN_PRICE_EVENT` and `MARKET_CAP_EVENT` falls back to the webhook id when no bucket is set.                              |
| `deduplicationId` | `string`          | Stable per-event id. Replays use the same value. Shape varies per category — see each section.                                                                      |
| `hash`            | `string`          | `sha256(securityToken + deduplicationId)` (hex). Lets you verify the envelope was built with the secret you supplied. See [Hash verification](#hash-verification).  |
| `data`            | `object \| array` | Category payload. Object on single delivery, array on batch.                                                                                                        |

Set `publishingType: BATCH` on `createWebhooks` to opt into batched envelopes; the default is `SINGLE` (one envelope per match).

## Hash verification

Each webhook message includes a `hash` field you can use to verify the message came from Mobula and hasn't been tampered with. This protects your endpoint from spoofed or replayed requests.

The hash is a SHA-256 digest of your webhook's `securityToken` concatenated with the message's `deduplicationId`. To verify, compute the same hash on your server and compare it to the value in the payload. If they match, the message is authentic.

```js theme={null}
const crypto = require('crypto');

const calculatedHash = crypto
  .createHash('sha256')
  .update(securityToken)
  .update(deduplicationId)
  .digest('hex');
```

***

# Event types

## TOKEN\_PAIR\_EVENT

Pair-level events (swap / mint / burn) on a watched token, pair, exchange, or maker.

**When to use it**

* Tracking every trade made by a specific wallet
* Monitoring high-value swaps on a single token or pair
* Feeding a live trade tape for a specific pool
* Alerting on liquidity events (mints, burns)

**Filter conditions** — see [TokenPairEventWebhookConditionInput](/api-reference/graphql/types/TokenPairEventWebhookConditionInput) for the full schema.

* `networkId`, `tokenAddress`, `pairAddress`, `exchangeAddress`, `maker`
* `eventType` — one of `SWAP`, `MINT`, `BURN`
* `swapValue` — USD threshold (`gt` / `gte` / `lt` / `lte` / `eq`)
* `fdvMarketCapUsd`, `circulatingMarketCapUsd`, `liquidityUsd`, `volumeUsd` — additional thresholds

### Creation example

```graphql theme={null}
mutation CreateTokenPairWebhook {
  createWebhooks(
    input: {
      tokenPairEventWebhooksInput: {
        webhooks: {
          name: "Big swaps on WETH/USDC"
          callbackUrl: "https://your-endpoint.com/webhook"
          securityToken: "your-security-token"
          alertRecurrence: INDEFINITE
          conditions: {
            pairAddress: { eq: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640" }
            networkId:   { oneOf: [1] }
            swapValue:   { gte: "10000" }
          }
        }
      }
    }
  ) {
    tokenPairEventWebhooks { id name }
  }
}
```

### Message payload (single event)

```json theme={null}
{
  "type": "TOKEN_PAIR_EVENT",
  "webhookId": "1f4b2cb9-5e25-4ee7-a9a3-b1f8a78fa6f6",
  "webhook": { "id": "1f4b2cb9-5e25-4ee7-a9a3-b1f8a78fa6f6", "name": "Big swaps on WETH/USDC" },
  "groupId": "alerts",
  "deduplicationId": "1f4b2cb9-…-0x88e6a0c2…:1-0019200000#00000012#00000045#00000000",
  "hash": "8c9a6f4e2b1c…",
  "data": {
    "event": {
      "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
      "id": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640:1",
      "networkId": 1,
      "blockNumber": 19200000,
      "transactionHash": "0xabc…def",
      "transactionIndex": 12,
      "logIndex": 45,
      "timestamp": 1714742400,
      "sortKey": "0019200000#00000012#00000045#00000000",
      "eventType": "Swap",
      "eventDisplayType": "Buy",
      "maker": "0xabcd…",
      "token0SwapValueUsd": "1235.50",
      "token1SwapValueUsd": "1234.99",
      "data": {
        "type": "Swap",
        "amount0": "1235500000",
        "amount1": "320875000000000000",
        "protocol": "uniswap-v3"
      }
    },
    "pair": {
      "id": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640:1",
      "address": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
      "networkId": 1,
      "token0": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      "token1": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
    }
  }
}
```

`deduplicationId = <webhookId>-<pairId>-<sortKey>`, where `sortKey = blockNumber#txIndex#logIndex#supplementalIndex` (each segment zero-padded).

### Message payload (batch version)

```json theme={null}
{
  "type": "TOKEN_PAIR_EVENT_BATCH",
  "webhookId": "1f4b2cb9-5e25-4ee7-a9a3-b1f8a78fa6f6",
  "groupId": "alerts",
  "deduplicationId": "1f4b2cb9-…-batch-0019200000",
  "hash": "…",
  "data": [
    { "event": { /* … */ }, "pair": { /* … */ } },
    { "event": { /* … */ }, "pair": { /* … */ } }
  ]
}
```

`deduplicationId = <webhookId>-batch-<paddedBlockNumber>` (block of the first event in the batch).

***

## TOKEN\_PRICE\_EVENT

Single-token USD-price thresholds. Fires when a swap involving the watched token crosses the configured `priceUsd`.

**When to use it**

* Price alerts for a single token (e.g. "WETH above \$4000")
* Watchlist-style notifications for a small set of tokens
* Dashboard price tickers where polling is not acceptable

**Filter conditions** — see [TokenPriceEventWebhookConditionInput](/api-reference/graphql/types/TokenPriceEventWebhookConditionInput).

* `address` (required), `networkId` (required)
* `priceUsd` (required) — `gt` / `gte` / `lt` / `lte` / `eq`

### Creation example

```graphql theme={null}
mutation CreateTokenPriceWebhook {
  createWebhooks(
    input: {
      tokenPriceEventWebhooksInput: {
        webhooks: {
          name: "WETH above $4000"
          callbackUrl: "https://your-endpoint.com/webhook"
          securityToken: "your-security-token"
          alertRecurrence: INDEFINITE
          conditions: {
            address:   { eq: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" }
            networkId: { eq: 1 }
            priceUsd:  { gte: "4000" }
          }
        }
      }
    }
  ) {
    tokenPriceEventWebhooks { id name }
  }
}
```

### Message payload (single event)

```json theme={null}
{
  "type": "TOKEN_PRICE_EVENT",
  "webhookId": "8e8f3c9d-1c5e-49a3-9f1c-2d3e4f5a6b7c",
  "webhook": { "id": "8e8f3c9d-1c5e-49a3-9f1c-2d3e4f5a6b7c", "name": "WETH above $4000" },
  "groupId": "8e8f3c9d-1c5e-49a3-9f1c-2d3e4f5a6b7c",
  "deduplicationId": "8e8f3c9d-…-0xc02aaa39…:1-0019200000#00000012#00000045#00000000",
  "hash": "…",
  "data": {
    "id": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:1",
    "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
    "networkId": 1,
    "priceUsd": "4012.55",
    "timestamp": 1714742400,
    "blockNumber": 19200000
  }
}
```

`deduplicationId = <webhookId>-<id>-<sortKey>` (4-segment sortKey, same shape as `TOKEN_PAIR_EVENT`).

### Message payload (batch version)

```json theme={null}
{
  "type": "TOKEN_PRICE_EVENT_BATCH",
  "webhookId": "8e8f3c9d-1c5e-49a3-9f1c-2d3e4f5a6b7c",
  "groupId": "8e8f3c9d-1c5e-49a3-9f1c-2d3e4f5a6b7c",
  "deduplicationId": "8e8f3c9d-…-batch-0019200000",
  "hash": "…",
  "data": [
    { "id": "0xc02aaa39…:1", "address": "0xc02aaa39…", "networkId": 1, "priceUsd": "4012.55", "timestamp": 1714742400, "blockNumber": 19200000 },
    { "id": "0xc02aaa39…:1", "address": "0xc02aaa39…", "networkId": 1, "priceUsd": "4015.10", "timestamp": 1714742460, "blockNumber": 19200005 }
  ]
}
```

`deduplicationId = <webhookId>-batch-<paddedBlockNumber>`.

***

## MARKET\_CAP\_EVENT

FDV / circulating market-cap thresholds.

**When to use it**

* Market-cap milestone alerts (e.g. "PEPE crosses \$5B FDV")
* Large-cap filtering logic for automated tools
* Portfolio-level risk dashboards

**Filter conditions** — see [MarketCapEventWebhookConditionInput](/api-reference/graphql/types/MarketCapEventWebhookConditionInput).

* `tokenAddress` (required), `networkId` (required)
* `fdvMarketCapUsd`, `circulatingMarketCapUsd` — at least one threshold expected
* Optional pair-level: `pairAddress`, `liquidityUsd`, `volumeUsd`

### Creation example

```graphql theme={null}
mutation CreateMarketCapWebhook {
  createWebhooks(
    input: {
      marketCapWebhooksInput: {
        webhooks: {
          name: "PEPE FDV > $5B"
          callbackUrl: "https://your-endpoint.com/webhook"
          securityToken: "your-security-token"
          alertRecurrence: ONCE
          conditions: {
            tokenAddress:    { eq: "0x6982508145454ce325ddbe47a25d4ec3d2311933" }
            networkId:       { eq: 1 }
            fdvMarketCapUsd: { gte: "5000000000" }
          }
        }
      }
    }
  ) {
    marketCapWebhooks { id name }
  }
}
```

### Message payload (single event)

```json theme={null}
{
  "type": "MARKET_CAP_EVENT",
  "webhookId": "ac1d4b2e-7a3c-4f1e-9b2d-5e6f7a8b9c0d",
  "webhook": { "id": "ac1d4b2e-7a3c-4f1e-9b2d-5e6f7a8b9c0d", "name": "PEPE FDV > $5B" },
  "groupId": "caps",
  "deduplicationId": "ac1d4b2e-…-0x69825081…:1-0019200000#00000012#00000045",
  "hash": "…",
  "data": {
    "priceModel": {
      "id": "0x6982508145454ce325ddbe47a25d4ec3d2311933:1",
      "priceUsd": "0.0000118",
      "address": "0x6982508145454ce325ddbe47a25d4ec3d2311933",
      "networkId": 1,
      "timestamp": 1714742400,
      "blockNumber": 19200000,
      "targetTokenAddress": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      "pairMetadata": {
        "liquidityUsd": "12500000",
        "pairId": "0x11950d141ecb863f01007add7d1a342041227b58:1",
        "volume24Usd": "44000000"
      }
    },
    "tokenModel": {
      "id": "0x6982508145454ce325ddbe47a25d4ec3d2311933:1",
      "address": "0x6982508145454ce325ddbe47a25d4ec3d2311933",
      "name": "Pepe",
      "symbol": "PEPE",
      "decimals": 18,
      "shiftedTotalSupply": "420690000000000",
      "shiftedCirculatingSupply": "420690000000000"
    },
    "fdvMarketCapUsd": "4964142000",
    "circulatingMarketCapUsd": "4964142000"
  }
}
```

`deduplicationId = <webhookId>-<priceModel.id>-<sortKey>`. Note the 3-segment sortKey here: `blockNumber#txIndex#logIndex` (no `supplementalIndex`).

### Message payload (batch version)

```json theme={null}
{
  "type": "MARKET_CAP_EVENT_BATCH",
  "webhookId": "ac1d4b2e-7a3c-4f1e-9b2d-5e6f7a8b9c0d",
  "groupId": "caps",
  "deduplicationId": "ac1d4b2e-…-batch-0019200000",
  "hash": "…",
  "data": [
    { "priceModel": { /* … */ }, "tokenModel": { /* … */ }, "fdvMarketCapUsd": "…", "circulatingMarketCapUsd": "…" }
  ]
}
```

`deduplicationId = <webhookId>-batch-<paddedBlockNumber>`.

***

## TOKEN\_TRANSFER\_EVENT

ERC-20 / SPL transfers (and native-token transfers).

**When to use it**

* Monitoring whale wallet movements
* Triggering on-chain alerts for your own wallets
* Watching exchange hot wallets or bridge addresses

**Filter conditions** — see [TokenTransferEventWebhookConditionInput](/api-reference/graphql/types/TokenTransferEventWebhookConditionInput).

* `networkId`, `tokenAddress`, `address`
* `direction` — `TO`, `FROM`, or both

### Creation example

```graphql theme={null}
mutation CreateTokenTransferWebhook {
  createWebhooks(
    input: {
      tokenTransferEventWebhooksInput: {
        webhooks: {
          name: "USDC inflows to Binance hot wallet"
          callbackUrl: "https://your-endpoint.com/webhook"
          securityToken: "your-security-token"
          alertRecurrence: INDEFINITE
          conditions: {
            networkId:    { oneOf: [1] }
            tokenAddress: { eq: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" }
            address:      { eq: "0x28c6c06298d514db089934071355e5743bf21d60" }
            direction:    { oneOf: [TO] }
          }
        }
      }
    }
  ) {
    tokenTransferEventWebhooks { id name }
  }
}
```

### Message payload (single event)

```json theme={null}
{
  "type": "TOKEN_TRANSFER_EVENT",
  "webhookId": "9d2c4b1a-3f6e-4a2b-8c5d-1e7f8a9b0c2d",
  "webhook": { "id": "9d2c4b1a-3f6e-4a2b-8c5d-1e7f8a9b0c2d", "name": "USDC inflows to Binance hot wallet" },
  "groupId": "transfers",
  "deduplicationId": "9d2c4b1a-…-0xabc123def456…-45",
  "hash": "…",
  "data": {
    "tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
    "networkId": 1,
    "fromAddress": "0xfeed1234…",
    "toAddress": "0x28c6c06298d514db089934071355e5743bf21d60",
    "amount": "5000000000",
    "direction": "TO",
    "timestamp": 1714742400,
    "blockNumber": 19200000,
    "transactionHash": "0xabc123def456…",
    "transactionIndex": 12,
    "logIndex": 45
  }
}
```

`deduplicationId`:

* ERC-20 / SPL (with `logIndex`): `<webhookId>-<txHash>-<logIndex>`
* Native transfers (no `logIndex`): `<webhookId>-<txHash>-n<sha256(from|to|amount)[:16]>`

### Message payload (batch version)

```json theme={null}
{
  "type": "TOKEN_TRANSFER_EVENT_BATCH",
  "webhookId": "9d2c4b1a-3f6e-4a2b-8c5d-1e7f8a9b0c2d",
  "webhook": { "id": "9d2c4b1a-3f6e-4a2b-8c5d-1e7f8a9b0c2d", "name": "USDC inflows to Binance hot wallet" },
  "groupId": "transfers",
  "deduplicationId": "9d2c4b1a-…-batch-0019200000",
  "hash": "…",
  "data": [
    {
      "tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      "networkId": 1,
      "fromAddress": "0xfeed1234…",
      "toAddress": "0x28c6c06298d514db089934071355e5743bf21d60",
      "amount": "5000000000",
      "direction": "TO",
      "timestamp": 1714742400,
      "blockNumber": 19200000,
      "transactionHash": "0xabc123…",
      "transactionIndex": 12,
      "logIndex": 45
    }
  ]
}
```

`deduplicationId = <webhookId>-batch-<paddedBlockNumber>`.
