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

# Webhooks

> Receive token price alerts and pair event notifications at your own HTTP endpoints with Codex webhooks.

Webhooks deliver real-time updates from the Codex API directly to an HTTP endpoint you control. Unlike [subscriptions](/concepts/subscriptions), which require a persistent connection, webhooks fit naturally into event-driven architectures, alerting systems, and background services.

<Note>
  Webhooks require a Growth or Enterprise plan.
</Note>

## Delivery and retries

Codex sends webhook messages via HTTP POST and expects a 2xx response within 3 seconds. If your endpoint responds slowly or fails, the delivery will be retried up to four times, about 5 seconds apart. If your service is down for long enough, some messages will be lost.

<Note>
  Webhook usage breakdown:

  * **Processed** means an event matched at a basic level (for example, maker, pair, or token address)
  * **Triggered** means all conditions passed and Codex will try to publish
  * **Success** means publishing was attempted and succeeded
  * **Failed** means publishing was attempted and failed (usually a bad URL)
</Note>

### Batch publishing

With `publishingType: BATCH`, Codex groups events that are ready at the same time into one POST of up to 5 events. There is no batching window, so latency is the same as `SINGLE`, and a batch of one event is normal. Each event in a batch counts as one request. Order is not guaranteed across POSTs, so sort on the event's block number and log index if order matters and dedupe per event.

## Creating a webhook

Create webhooks with the [`createWebhooks`](/api-reference/mutations/createwebhooks) mutation, which you can run from the [Explorer](/explore). View and manage them in the dashboard.

Each event type has its own input field on the mutation (for example, `tokenPairEventWebhooksInput` or `predictionTradeWebhooksInput`). See the [event type sections](#event-types) below for a ready-to-use creation example for each.

### Organizing webhooks with `bucketKey`

You can optionally group and query your webhooks using a `bucketKey`. This is useful when you are managing many webhooks per user or per token, such as a price alert system.

```graphql theme={null} theme={null}
bucketKey: {
  bucketId: "price-alert-${user.id}"
  bucketSortKey: "price-alert-${token.id}"
}
```

Both `bucketId` and `bucketSortKey` must be provided together. If you don't need to query webhooks by bucket, you can omit `bucketKey` entirely. Using both fields lets you independently query all alerts for a specific user and all alerts for a specific token.

<Note>
  The individual `bucketId` and `bucketSortKey` fields on `createWebhook` are deprecated. Use the `bucketKey` object instead.
</Note>

## Message structure

Every webhook message shares a common envelope. The `type` field tells you which event fired, and the `data` field contains the event-specific payload described in the [event type sections](#event-types).

<ResponseField name="type" type="WebhookPublisherMessageType">
  The event type. One of `TOKEN_PAIR_EVENT`, `TOKEN_PAIR_EVENT_BATCH`, `TOKEN_PRICE_EVENT`, `TOKEN_PRICE_EVENT_BATCH`, `TOKEN_TRANSFER_EVENT`, `TOKEN_TRANSFER_EVENT_BATCH`, `MARKET_CAP_EVENT`, `MARKET_CAP_EVENT_BATCH`, `TOKEN_LAUNCH_EVENT`, `TOKEN_LAUNCH_EVENT_BATCH`, `PREDICTION_TRADE_EVENT`, `PREDICTION_TRADE_EVENT_BATCH`, `PREDICTION_MARKET_METRICS_EVENT`, or `PREDICTION_MARKET_METRICS_EVENT_BATCH`.
</ResponseField>

<ResponseField name="deduplicationId" type="String">
  A unique identifier for this message. Use it to deduplicate retries on your side.
</ResponseField>

<ResponseField name="webhookId" type="String">
  The ID of the webhook that triggered this message.
</ResponseField>

<ResponseField name="groupId" type="String">
  The ID used to group related messages for ordered delivery.
</ResponseField>

<ResponseField name="hash" type="String" deprecated>
  Deprecated. SHA256 hash of the `securityToken` and `deduplicationId`. Prefer the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers described in [Verifying webhooks](#verifying-webhooks). The legacy `hash` does not cover the request body and is still emitted only for backwards compatibility.
</ResponseField>

<ResponseField name="data" type="WebhookPublisherDataModels">
  The event-specific payload. The shape depends on `type`. See the [event type sections](#event-types) for each payload structure.
</ResponseField>

## Verifying webhooks

Every webhook delivery includes two signature headers that authenticate the exact request body bytes and provide a freshness signal for replay protection:

* `X-Webhook-Timestamp`: Unix timestamp in seconds.
* `X-Webhook-Signature`: lowercase hex HMAC-SHA256 over `{timestamp}.{rawBody}`, using your webhook's `securityToken` as the key.

To verify a delivery:

1. Read the raw request body bytes as received, before any JSON parsing.
2. Build the signed payload string `{timestamp}.{rawBody}`.
3. Compute `hex(hmac_sha256(securityToken, signedPayload))` and compare it to `X-Webhook-Signature` using a constant-time comparison.
4. Reject the request if `abs(now - X-Webhook-Timestamp) > 300` seconds, or if either header is missing or malformed.

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

function verifyWebhook ({ rawBody, timestamp, signature, securityToken }) {
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) {
    return false;
  }

  const expected = crypto
    .createHmac("sha256", securityToken)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  const expectedBuffer = Buffer.from(expected, "hex");
  const signatureBuffer = Buffer.from(signature, "hex");

  return (
    expectedBuffer.length === signatureBuffer.length &&
    crypto.timingSafeEqual(expectedBuffer, signatureBuffer)
  );
}
```

<Note>
  Verify the **original raw request body bytes**, then parse JSON only after the signature check passes. Re-serializing the parsed body will change whitespace and key order and produce a different signature.
</Note>

**Capturing the raw body**

* Next.js App Router: call `await request.text()` once and use that exact string.
* Next.js API routes: set `export const config = { api: { bodyParser: false } }`, then read the request stream into a buffer.
* Express: mount `express.raw({ type: "application/json" })` on the webhook route, or capture the buffer in `express.json({ verify })`.
* AWS Lambda + API Gateway: use `event.body` directly, base64-decoding when `event.isBase64Encoded` is true.
* Rails/Rack: use `request.raw_post`. Flask/Django: `request.get_data()` / `request.body`.

**Common mistakes**

* Parsing and re-serializing JSON before verification.
* Verifying only `data` or the legacy `hash` field instead of `{timestamp}.{rawBody}`.
* Comparing signatures with `==` instead of a constant-time helper.
* Treating the timestamp as milliseconds. `X-Webhook-Timestamp` is Unix seconds.
* Skipping the freshness check, which removes replay protection.

## Hash verification (deprecated)

<Warning>
  The body `hash` field is deprecated. Use [Verifying webhooks](#verifying-webhooks) with `X-Webhook-Timestamp` and `X-Webhook-Signature`. The legacy `hash` is computed from `sha256(securityToken + deduplicationId)`, so it only identifies the delivery id pair and does not authenticate the delivered body. Both the headers and the legacy `hash` are emitted during the migration period.
</Warning>

The legacy `hash` is a SHA256 digest of your webhook's `securityToken` concatenated with the message's `deduplicationId`:

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

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

## Source IP addresses

Codex sends webhook deliveries from a fixed set of IP addresses. If you want to gate inbound webhook traffic at the network layer in addition to verifying the signature on each payload, allowlist these IPs at your firewall or load balancer:

```
35.155.50.173
52.25.29.13
44.235.164.143
52.32.112.191
```

<Note>
  Network-level allowlisting complements signature verification, it doesn't replace it. Always verify the signature on incoming payloads to confirm authenticity.
</Note>

## Event types

Each event type fires on a different kind of on-chain or market activity. Pick the one that matches what you want to react to, then jump to its section below for filter conditions, the input field, a copy-paste mutation, and a sample payload.

| Event | Fires when |
| - | - |
| [Token Pair Event](#token_pair_event) | Swaps, mints, burns, and other liquidity events on a trading pair. The most common type. |
| [Token Price Event](#token_price_event) | A token's price crosses a threshold you set. |
| [Token Transfer Event](#token_transfer_event) | Tokens move to or from a wallet you're watching. |
| [Market Cap Event](#market_cap_event) | A token's market cap — fully diluted or circulating — crosses a threshold. |
| [Token Launch Event](#token_launch_event) | A new token launches on a launchpad, filterable by launchpad or creator wallet. |
| [Prediction Trade Event](#prediction_trade_event) | Individual Polymarket or Kalshi trades, filterable by trader, market, event, side, or size. |
| [Prediction Market Metrics Event](#prediction_market_metrics_event) | A prediction market's rolling windowed stats — volume, price, or trade count — cross a threshold, at the market or per-outcome level. |

### `TOKEN_PAIR_EVENT`

The `TOKEN_PAIR_EVENT` webhook fires when a swap, mint, burn, or other liquidity event occurs on a trading pair. It delivers the full `Pair` and `Event` objects so you can reconstruct exactly what happened on-chain. This is the most commonly used webhook.

**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) for a pair

**Filter conditions**

* `tokenAddress`: fire for events involving this token
* `networkId`: one or more network IDs to listen on
* `swapValue`: filter by USD value of the swap
* `maker`: fire for events made by a specific wallet
* `pairAddress`: fire for events on a specific pair
* `exchangeAddress`: fire for events on a specific exchange
* `eventType`: filter to specific event types (`SWAP`, `MINT`, `BURN`, `SYNC`, `BUY`, `SELL`, `COLLECT`, `COLLECT_PROTOCOL`)

See the full input type at [`tokenPairEventWebhookConditionInput`](/api-reference/input-objects/tokenpaireventwebhookconditioninput).

<AccordionGroup>
  <Accordion title="Creation example">
    ```graphql theme={null} 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
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Message payload">
    ```json expandable theme={null} theme={null}
    {
      "deduplicationId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f-DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149-0000000256717997#00000000#00000002#00000009",
      "groupId": "bc9345e6-b0da-4935-9b31-47b34cc5f628",
      "hash": "e39fa0bfcef1bfcd12d6707c29ac752899228d026338e594e04a398255962022",
      "type": "TOKEN_PAIR_EVENT",
      "webhook": {
        "bucketId": "bc9345e6-b0da-4935-9b31-47b34cc5f628",
        "bucketSortkey": "GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe",
        "id": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f",
        "name": "traderpow:GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe:TOKEN_PAIR_EVENT"
      },
      "webhookId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f",
      "data": {
        "event": {
          "address": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo",
          "baseTokenPrice": "18651403264.31996",
          "blockHash": "DGXgkMSxovvjdA5iKgrsFL7duNyXRPsW86Ztvg2Mc1hL",
          "blockNumber": 256717997,
          "data": {
            "amount0": "7020307392",
            "amount1": "-2899270904078",
            "liquidity": "51478999661589",
            "liquidity0": "1140722809229",
            "liquidity1": "457845387792910",
            "protocol": "Orca",
            "sqrtPriceX64": "376244087403309877755",
            "tick": "60310",
            "type": "Swap"
          },
          "eventDisplayType": "Buy",
          "eventType": "Swap",
          "eventType2": "Token1Buy",
          "id": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149",
          "labels": {},
          "liquidityToken": "So11111111111111111111111111111111111111112",
          "logIndex": 2,
          "maker": "GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe",
          "makerHashKey": "GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe:DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149",
          "networkId": 1399811149,
          "quoteToken": "token1",
          "sortKey": "0000000256717997#00000000#00000002#00000009",
          "supplementalIndex": 9,
          "timestamp": 1711526246,
          "token0PoolValueUsd": "186.5140326431996",
          "token0SwapValueUsd": "186.51403264319955960744460600963952776157867455426636901017",
          "token0ValueBase": "1",
          "token0ValueUsd": "186.5140326431996",
          "token1PoolValueUsd": "0.4483442346979745",
          "token1SwapValueUsd": "0.45162590368325119780671230665069180443756818839541768",
          "token1ValueBase": "0.0024038096669951625071971972244735217782",
          "token1ValueUsd": "0.4483442346979745",
          "transactionHash": "2dd5jDHbVYZpXgYbEQzpv47Z7jwimmHeDxKytiM9FB4GzBA5QeYh5xHTzkQvCScQksvTrHqgq9k7q84MBcGbaFfJ",
          "transactionIndex": 0,
          "ttl": 1716710246
        },
        "pair": {
          "address": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo",
          "exchangeHash": "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc",
          "fee": null,
          "id": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo:1399811149",
          "networkId": 1399811149,
          "tickSpacing": null,
          "token0": "So11111111111111111111111111111111111111112",
          "token1": "FU1q8vJpZNUrmqsciSjp8bAKKidGsLmouB8CBdf8TKQv"
        }
      }
    }
    ```

    Batch version (webhook created with `publishingType: BATCH`):

    ```json expandable theme={null} theme={null}
    {
      "deduplicationId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f-batch-0000000256717997",
      "groupId": "bc9345e6-b0da-4935-9b31-47b34cc5f628",
      "hash": "e39fa0bfcef1bfcd12d6707c29ac752899228d026338e594e04a398255962022",
      "type": "TOKEN_PAIR_EVENT_BATCH",
      "webhookId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f",
      "data": [
        {
          "event": { "...": "same shape as single event above" },
          "pair": { "...": "same shape as single event above" }
        },
        {
          "event": { "...": "second event in batch" },
          "pair": { "...": "second pair in batch" }
        }
      ]
    }
    ```
  </Accordion>
</AccordionGroup>

### `TOKEN_PRICE_EVENT`

The `TOKEN_PRICE_EVENT` webhook fires when the price of a specific token crosses a threshold you define. Use it for price alerts on tokens you care about.

**When to use it**

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

**Filter conditions**

* `address`: the token contract address (required)
* `networkId`: the network ID (required)
* `priceUsd`: price condition that must be met (required). Supports `gt`, `gte`, `lt`, `lte`, `eq`

See the full input type at [`tokenPriceEventWebhookConditionInput`](/api-reference/input-objects/tokenpriceeventwebhookconditioninput).

<AccordionGroup>
  <Accordion title="Creation example">
    ```graphql theme={null} 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
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Message payload">
    ```json expandable theme={null} theme={null}
    {
      "type": "TOKEN_PRICE_EVENT",
      "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:1-0000000024000071#00000117#00000353",
      "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b",
      "groupId": "5e0fe797-c795-451f-af87-0257847b8c3b",
      "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd",
      "webhook": {
        "id": "5e0fe797-c795-451f-af87-0257847b8c3b",
        "name": "token-price-webhook"
      },
      "data": {
        "id": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:1",
        "priceUsd": "3.0898058248076381750556910457327e+3",
        "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
        "networkId": 1,
        "timestamp": 1765585223,
        "blockNumber": 24000071
      }
    }
    ```

    Batch version (webhook created with `publishingType: BATCH`):

    ```json expandable theme={null} theme={null}
    {
      "type": "TOKEN_PRICE_EVENT_BATCH",
      "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-batch-0000000024000071",
      "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b",
      "groupId": "5e0fe797-c795-451f-af87-0257847b8c3b",
      "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd",
      "data": [
        { "...": "same shape as the single message above" },
        { "...": "additional messages in the batch" }
      ]
    }
    ```
  </Accordion>
</AccordionGroup>

### `TOKEN_TRANSFER_EVENT`

The `TOKEN_TRANSFER_EVENT` webhook fires when a token is transferred to or from a wallet you are monitoring. Use it to track wallet inflows and outflows in real time.

**When to use it**

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

**Filter conditions**

* `tokenAddress`: the token contract to track
* `networkId`: one or more network IDs
* `address`: the wallet address to monitor
* `direction`: `TO` (receiving), `FROM` (sending), or both

See the full input type at [`tokenTransferEventWebhookConditionInput`](/api-reference/input-objects/tokentransfereventwebhookconditioninput).

<AccordionGroup>
  <Accordion title="Creation example">
    ```graphql theme={null} theme={null}
    mutation CreateTokenTransferWebhook {
      createWebhooks(
        input: {
          tokenTransferEventWebhooksInput: {
            webhooks: {
              name: "Inflows to whale wallet"
              callbackUrl: "https://your-endpoint.com/webhook"
              securityToken: "your-security-token"
              alertRecurrence: INDEFINITE
              conditions: {
                address: { eq: "0x1abde2088657de84ad6239f3d445dd07d6fa1033" }
                networkId: { oneOf: [8453] }
                direction: { oneOf: [TO] }
              }
            }
          }
        }
      ) {
        tokenTransferEventWebhooks {
          id
          name
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Message payload">
    ```json expandable theme={null} theme={null}
    {
      "type": "TOKEN_TRANSFER_EVENT",
      "deduplicationId": "6af3a260-7e2c-4615-babe-c239786ec9fd-0x84df029b0fc5d81ec8d65fa49568bc509e8ce0caa3de05c8de51acea8aafdb6b-740",
      "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd",
      "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55",
      "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b",
      "webhook": {
        "id": "6af3a260-7e2c-4615-babe-c239786ec9fd",
        "name": "Track transfers to wallet"
      },
      "data": {
        "tokenAddress": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
        "networkId": 8453,
        "fromAddress": "0x4767bf3619bb493d129c864a023512fa1dce9da4",
        "toAddress": "0x1abde2088657de84ad6239f3d445dd07d6fa1033",
        "amount": "20000",
        "shiftedAmount": "0.02",
        "direction": "TO",
        "timestamp": 1767657659,
        "blockNumber": 40434156,
        "transactionHash": "0x84df029b0fc5d81ec8d65fa49568bc509e8ce0caa3de05c8de51acea8aafdb6b",
        "transactionIndex": 171,
        "logIndex": 740
      }
    }
    ```

    Batch version (webhook created with `publishingType: BATCH`):

    ```json expandable theme={null} theme={null}
    {
      "type": "TOKEN_TRANSFER_EVENT_BATCH",
      "deduplicationId": "6af3a260-7e2c-4615-babe-c239786ec9fd-batch-40434156",
      "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd",
      "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55",
      "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b",
      "webhook": {
        "id": "6af3a260-7e2c-4615-babe-c239786ec9fd",
        "name": "Track all transfers"
      },
      "data": [
        { "...": "same shape as the single message above" },
        { "...": "additional messages in the batch" }
      ]
    }
    ```
  </Accordion>
</AccordionGroup>

### `MARKET_CAP_EVENT`

The `MARKET_CAP_EVENT` webhook fires when a token's market cap crosses a threshold you specify. Unlike token price webhooks, it factors in supply (both fully diluted and circulating).

Market cap is computed from the token's price, the same price [`getTokenPrices`](/api-reference/queries/gettokenprices) and [`filterTokens`](/api-reference/queries/filtertokens) report, not from any single pair. A swap on a thin pool at an outlier price doesn't fire the webhook unless the token's price also crosses your threshold, and swaps on several pools in the same block produce one event, not one per pool.

**When to use it**

* Market cap milestone alerts (for example, "VIRTUAL crosses \$1B FDV")
* Large-cap filtering logic for automated tools
* Portfolio-level risk dashboards

**Filter conditions**

* `tokenAddress`: the token contract (required)
* `networkId`: the network ID (required)
* `fdvMarketCapUsd`: fully diluted market cap threshold
* `circulatingMarketCapUsd`: circulating market cap threshold
* `liquidityUsd`: optional minimum or maximum for the token's liquidity
* `volumeUsd`: optional minimum or maximum for the token's 24-hour volume

`pairAddress` is deprecated. Creating a market cap webhook with it returns an error, and it is ignored on webhooks created before the change.

See the full input type at [`marketCapEventWebhookConditionInput`](/api-reference/input-objects/marketcapeventwebhookconditioninput).

<AccordionGroup>
  <Accordion title="Creation example">
    ```graphql theme={null} theme={null}
    mutation CreateMarketCapWebhook {
      createWebhooks(
        input: {
          marketCapWebhooksInput: {
            webhooks: {
              name: "VIRTUAL hits $1B FDV"
              callbackUrl: "https://your-endpoint.com/webhook"
              securityToken: "your-security-token"
              alertRecurrence: ONCE
              conditions: {
                tokenAddress: { eq: "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b" }
                networkId: { eq: 8453 }
                fdvMarketCapUsd: { gte: "1000000000" }
              }
            }
          }
        }
      ) {
        marketCapWebhooks {
          id
          name
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Message payload">
    ```json expandable theme={null} theme={null}
    {
      "type": "MARKET_CAP_EVENT",
      "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b:8453-0000000030917440#00000117#00000353",
      "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b",
      "groupId": "test-group",
      "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd",
      "webhook": {
        "id": "5e0fe797-c795-451f-af87-0257847b8c3b",
        "name": "mcap-virt"
      },
      "data": {
        "priceModel": {
          "id": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b:8453",
          "priceUsd": "2.014904011469267",
          "address": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b",
          "networkId": 8453,
          "timestamp": 1748624227,
          "blockNumber": 30917440
        },
        "tokenModel": {
          "id": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b:8453",
          "address": "0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b",
          "name": "Virtual Protocol",
          "symbol": "VIRTUAL",
          "decimals": 18,
          "shiftedTotalSupply": "495620931.12225886540849246",
          "shiftedCirculatingSupply": "495620930.505466750752879695"
        },
        "fdvMarketCapUsd": "998628602.29",
        "circulatingMarketCapUsd": "998628601.04"
      }
    }
    ```

    Batch version (webhook created with `publishingType: BATCH`):

    ```json expandable theme={null} theme={null}
    {
      "type": "MARKET_CAP_EVENT_BATCH",
      "deduplicationId": "5e0fe797-c795-451f-af87-0257847b8c3b-batch-0000000030917440",
      "webhookId": "5e0fe797-c795-451f-af87-0257847b8c3b",
      "groupId": "test-group",
      "hash": "c296a760c563c9a4115146af75945d1b35d0741835309fa44d393ef3c2b044dd",
      "data": [
        {
          "priceModel": { "...": "same shape as single event above" },
          "tokenModel": { "...": "same shape as single event above" },
          "fdvMarketCapUsd": "998628602.29",
          "circulatingMarketCapUsd": "998628601.04"
        }
      ]
    }
    ```
  </Accordion>
</AccordionGroup>

### `TOKEN_LAUNCH_EVENT`

The `TOKEN_LAUNCH_EVENT` webhook fires when a new token launches on a supported launchpad. It delivers the launched token's metadata, creator, and launchpad details so you can react to launches the moment they hit the open market — without polling [`filterTokens`](/api-reference/queries/filtertokens) or holding open a [`onLaunchpadTokenEventBatch`](/api-reference/subscriptions/onlaunchpadtokeneventbatch) subscription.

**When to use it**

* Alerting on every new launch from a specific launchpad (for example, Pump.fun)
* Following a specific creator wallet and getting notified when it deploys a new token
* Feeding a memescope-style new-launch feed into an event-driven backend

**Filter conditions**

Each webhook is **indexed** by exactly one of `creatorAddress` or `launchpadName` — provide one, not both, and not neither. The remaining conditions only narrow an indexed webhook and cannot be used on their own.

* `creatorAddress`: fire for launches from a specific creator wallet (`eq`). *Index — provide this or `launchpadName`.*
* `launchpadName`: fire for launches on a specific launchpad, e.g. `{ oneOf: ["Pump.fun"] }`. Must contain exactly **one** supported launchpad name; matching is case-insensitive and unknown names are rejected at creation. Create one webhook per launchpad. *Index — provide this or `creatorAddress`.*
* `networkId`: one or more network IDs to narrow to. Cannot be used on its own.
* `tokenAddress`: narrow to a specific launched token (`eq`). Cannot be used on its own.

See the full input type at [`tokenLaunchEventWebhookConditionInput`](/api-reference/input-objects/tokenlauncheventwebhookconditioninput).

<Note>
  Today the only launch event that fires is `CREATED` (a brand-new token). Graduation and migration event types are planned; the `tokenAddress` narrowing condition exists mainly for those future events, since a token already has an address by the time it migrates. See the `eventType` field in the payload below.
</Note>

<Accordion title="Supported launchpad names">
  `launchpadName` must match one of the names below (case-insensitive). This list evolves as new launchpads are added — if a name is rejected at creation, the error response lists the currently supported set.

  `AMERICA.fun`, `ArenaTrade`, `BAGS`, `BONAD.fun`, `Bankr`, `Baseapp`, `Baseapp Creator`, `Believe`, `Blowfish`, `Bonk`, `Candle.TV`, `Circus`, `Clanker`, `Clanker V4`, `Coinbarrel`, `ContinuumLaunchpad`, `ContinuumPledge`, `Cooking.City`, `Dealr`, `DubDub`, `EgoTech`, `Eitherway`, `Fairly`, `Flap`, `Flaunch`, `Four.meme`, `Four.meme Fair`, `Heaven`, `Jupiter Studio`, `Kumbaya`, `LaunchLab`, `Liquid`, `Livo`, `MeMoo`, `Metaplex`, `Meteora Alpha Vault`, `MeteoraDBC`, `Moonit`, `Moonshot`, `Nad.Fun`, `Noice`, `OhFuckFun`, `OpenGameProtocol`, `PrintFun`, `Printr`, `Pump Mayhem`, `Pump.fun`, `Rainbow`, `Scale`, `Sendshot`, `SomniaMeme`, `TokenMill V2`, `Trends`, `Virtuals`, `Zora`, `Zora Creator`, `Zora Solana`, `boop`, `cults`, `shout.fun`, `subs.fun`, `time.fun`, `xApple`
</Accordion>

<AccordionGroup>
  <Accordion title="Creation example">
    ```graphql theme={null} theme={null}
    mutation CreateTokenLaunchWebhook {
      createWebhooks(
        input: {
          tokenLaunchEventWebhooksInput: {
            webhooks: {
              name: "Pump.fun launches"
              callbackUrl: "https://your-endpoint.com/webhook"
              securityToken: "your-security-token"
              alertRecurrence: INDEFINITE
              conditions: {
                launchpadName: { oneOf: ["Pump.fun"] }
                networkId: { oneOf: [1399811149] }
              }
            }
          }
        }
      ) {
        tokenLaunchEventWebhooks {
          id
          name
        }
      }
    }
    ```

    To index on a creator wallet instead, swap the index condition (provide one, not both):

    ```graphql theme={null} theme={null}
    conditions: {
      creatorAddress: { eq: "So11111111111111111111111111111111111111112" }
    }
    ```
  </Accordion>

  <Accordion title="Message payload">
    ```json expandable theme={null} theme={null}
    {
      "type": "TOKEN_LAUNCH_EVENT",
      "deduplicationId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f-DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo-1399811149-Created",
      "webhookId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f",
      "groupId": "bc9345e6-b0da-4935-9b31-47b34cc5f628",
      "hash": "e39fa0bfcef1bfcd12d6707c29ac752899228d026338e594e04a398255962022",
      "webhook": {
        "id": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f",
        "name": "Pump.fun launches"
      },
      "data": {
        "eventType": "CREATED",
        "address": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo",
        "networkId": 1399811149,
        "name": "Example Token",
        "symbol": "EXMPL",
        "decimals": 6,
        "totalSupply": "1000000000",
        "creatorAddress": "GSE6vfr6vws493G22jfwCU6Zawh3dfvSYXYQqKhFsBwe",
        "description": "A brand-new launch",
        "imageUrl": "https://crypto-token-images-production.s3.amazonaws.com/example.png",
        "socialLinks": {
          "twitter": "https://x.com/example",
          "telegram": "https://t.me/example",
          "website": "https://example.com"
        },
        "launchpad": {
          "name": "Pump.fun",
          "protocol": "Pump",
          "poolAddress": "DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo",
          "graduationPercent": 0
        },
        "createdAt": 1711526246,
        "createBlockNumber": 256717997,
        "createTransactionHash": "2dd5jDHbVYZpXgYbEQzpv47Z7jwimmHeDxKytiM9FB4GzBA5QeYh5xHTzkQvCScQksvTrHqgq9k7q84MBcGbaFfJ"
      }
    }
    ```

    Batch version (webhook created with `publishingType: BATCH`):

    ```json expandable theme={null} theme={null}
    {
      "type": "TOKEN_LAUNCH_EVENT_BATCH",
      "deduplicationId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f-DbyEjKTHE76qgb8niQ4zDCoaU4CK7Si9PJkD6Ckjtugo-1399811149-Created",
      "webhookId": "aa4cd403-b54e-4e2d-825c-f70a69f6fd9f",
      "groupId": "bc9345e6-b0da-4935-9b31-47b34cc5f628",
      "hash": "e39fa0bfcef1bfcd12d6707c29ac752899228d026338e594e04a398255962022",
      "data": [
        { "...": "same shape as the single message above" },
        { "...": "additional launches in the batch" }
      ]
    }
    ```
  </Accordion>
</AccordionGroup>

### `PREDICTION_TRADE_EVENT`

The `PREDICTION_TRADE_EVENT` webhook fires when a prediction market trade occurs. Use it to track trades by a specific trader, on a specific market or event, or matching conditions like trade value or volume.

**When to use it**

* Tracking copy-traded wallets on Polymarket or Kalshi
* Notifying on large trades in a specific market or event
* Feeding a live trade tape for a prediction market dashboard

**Filter conditions**

* `traderId`: fire for a specific trader
* `marketId`: fire for a specific market
* `eventId`: fire for a specific event
* `eventType`: filter by trade event type (`TRADE`, `BUY`, `SELL`, `BUY_COUNTERPARTY`, `SELL_COUNTERPARTY`, `POSITION_REDEEMED`). `PAYOUT_REDEMPTION` is deprecated; use `POSITION_REDEEMED` instead.
* `tradeValueUsd`: filter by trade value in USD
* `amountToken`: filter by number of tokens or shares traded

See the full input type at [`predictionTradeWebhookConditionInput`](/api-reference/input-objects/predictiontradewebhookconditioninput).

<AccordionGroup>
  <Accordion title="Creation example">
    ```graphql theme={null} theme={null}
    mutation CreatePredictionTradeWebhook {
      createWebhooks(
        input: {
          predictionTradeWebhooksInput: {
            webhooks: {
              name: "Track trader activity"
              callbackUrl: "https://your-endpoint.com/webhook"
              securityToken: "your-security-token"
              alertRecurrence: INDEFINITE
              conditions: {
                traderId: { eq: "your-trader-id" }
                tradeValueUsd: { gte: "100" }
              }
              bucketKey: {
                bucketId: "trader-alerts-${user.id}"
                bucketSortKey: "trader-${traderId}"
              }
            }
          }
        }
      ) {
        predictionTradeWebhooks {
          id
          name
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Message payload">
    ```json expandable theme={null} theme={null}
    {
      "type": "PREDICTION_TRADE_EVENT",
      "deduplicationId": "abc123-...",
      "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd",
      "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55",
      "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b",
      "webhook": {
        "id": "6af3a260-7e2c-4615-babe-c239786ec9fd",
        "name": "Track trader activity"
      },
      "data": {
        "marketId": "0x1234...abcd",
        "sortKey": "0000000040434156#00000171#00000740",
        "outcomeId": "outcome-1",
        "outcomeLabel": "Yes",
        "protocol": "POLYMARKET",
        "tradeType": "BUY",
        "maker": "0x4767bf3619bb493d129c864a023512fa1dce9da4",
        "timestamp": 1767657659,
        "outcomeIndex": 0,
        "priceUsd": "0.72",
        "priceCollateral": "0.72",
        "amount": "100",
        "amountCollateral": "72",
        "amountUsd": "72.00",
        "transactionHash": "0x84df029b0fc5d81ec8d65fa49568bc509e8ce0caa3de05c8de51acea8aafdb6b",
        "blockNumber": 40434156,
        "networkId": 137,
        "exchangeAddress": "0xe55b5ceba4dc0d4e26261f3dcd468faaf7d0cdb8",
        "transactionId": "txn-abc123",
        "traderId": "trader-xyz",
        "eventId": "event-456"
      }
    }
    ```

    Batch version (webhook created with `publishingType: BATCH`):

    ```json expandable theme={null} theme={null}
    {
      "type": "PREDICTION_TRADE_EVENT_BATCH",
      "deduplicationId": "abc123-batch-40434156",
      "webhookId": "6af3a260-7e2c-4615-babe-c239786ec9fd",
      "groupId": "762acfb9-872d-4fa7-96a0-e45236759c55",
      "hash": "360adec82a811049a7374b087c40789e716c2329cb5dfc9b6dffc182fb77cd2b",
      "data": [
        { "...": "same shape as the single message above" },
        { "...": "additional messages in the batch" }
      ]
    }
    ```
  </Accordion>
</AccordionGroup>

### `PREDICTION_MARKET_METRICS_EVENT`

The `PREDICTION_MARKET_METRICS_EVENT` webhook fires when rolling, windowed statistics for a prediction market cross a threshold you define — at the whole-market level or for an individual outcome. Use it to react to volume spikes, price moves, or bursts of trading activity on a Polymarket or Kalshi market without polling.

**When to use it**

* Alerting on volume or trade-count spikes on a specific market
* Tracking a price move on a single outcome (for example, "Yes" climbing past 60%)
* Driving a live activity feed for a prediction market dashboard

**Filter conditions**

Metrics are evaluated over rolling time windows. Each window is its own field: `min5`, `hour1`, `hour4`, `hour12`, `day1`, and `week1`. Set a condition on one or more windows, and within a window match on:

* **Market-level metrics** (the `market` field): `volumeUsd`, `trades`, `volumeChange`, `tradesChange`
* **Per-outcome metrics** (`outcome0`, `outcome1`, or `anyOutcome`): everything available at the market level plus `price` and `priceChange`

`marketId` is required — each webhook is pinned to a single market. Every metric supports the standard `gt`, `gte`, `lt`, `lte`, and `eq` operators.

See the full input type at [`predictionMarketMetricsEventWebhookConditionInput`](/api-reference/input-objects/predictionmarketmetricseventwebhookconditioninput).

<AccordionGroup>
  <Accordion title="Creation example">
    ```graphql theme={null} theme={null}
    mutation CreatePredictionMarketMetricsWebhook {
      createWebhooks(
        input: {
          predictionMarketMetricsEventWebhooksInput: {
            webhooks: {
              name: "Volume spike on a prediction market"
              callbackUrl: "https://your-endpoint.com/webhook"
              securityToken: "your-security-token"
              alertRecurrence: INDEFINITE
              conditions: {
                marketId: { eq: "your-market-id" }
                market: {
                  hour1: {
                    volumeUsd: { gte: "50000" }
                    tradesChange: { gte: "2" }
                  }
                }
                anyOutcome: {
                  hour1: { priceChange: { gte: "0.1" } }
                }
              }
            }
          }
        }
      ) {
        predictionMarketMetricsEventWebhooks {
          id
          name
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Message payload">
    Deliveries use the standard [message envelope](#message-structure). The `data` object carries the market, its `lifecycle`, one block per rolling window (`statsMin5`, `statsHour1`, `statsHour4`, `statsHour12`, `statsDay1`, `statsWeek1`), and `trending` / `relevance` / `competitive` score series. Each window holds market-level `core` totals, `outcome0Stats` / `outcome1Stats` (price, volume, orderbook), and a `statsChange` block whose `volumeChange`, `tradesChange`, and `priceChange` ratios are what your filter conditions compare against. The example shows the `min5` window in full; the other windows share the same shape.

    ```json expandable theme={null} theme={null}
    {
      "type": "PREDICTION_MARKET_METRICS_EVENT",
      "deduplicationId": "103cb449-9165-442c-a94e-4e2394191096:KXATPMATCH-26JUN01TIAARN-ARN:Kalshi:1780339453",
      "webhookId": "103cb449-9165-442c-a94e-4e2394191096",
      "groupId": "default",
      "hash": "5f8fe57bd7ed36aa04237112cdaba68b04568b734a4f0cf7dd43ed26a5b934bd",
      "webhook": {
        "id": "103cb449-9165-442c-a94e-4e2394191096",
        "name": "Any"
      },
      "data": {
        "marketId": "KXATPMATCH-26JUN01TIAARN-ARN:Kalshi",
        "lastTransactionAt": 1780339453,
        "lifecycle": {
          "ageSeconds": 158894,
          "expectedLifespanSeconds": 1333440,
          "timeToResolutionSeconds": 1174546,
          "isResolved": false
        },
        "statsMin5": {
          "start": 1780339140,
          "end": 1780339500,
          "lastTransactionAt": 1780339453,
          "core": {
            "volume": { "usd": "214790", "ct": "214790" },
            "trades": 344
          },
          "uniqueTraders": null,
          "liquidity": null,
          "openInterest": {
            "openInterest": {
              "open": { "usd": "2511700", "ct": "2511700" },
              "close": { "usd": "2880725", "ct": "2880725" },
              "low": { "usd": "2511700", "ct": "2511700" },
              "high": { "usd": "2880725", "ct": "2880725" }
            }
          },
          "outcome0Stats": {
            "core": {
              "venueOutcomeId": "yes#KXATPMATCH-26JUN01TIAARN-ARN:Kalshi",
              "trades": 282,
              "volume": { "usd": "205033", "ct": "205033", "shares": "398508360000" },
              "price": {
                "open": { "usd": "0.52", "ct": "0.52" },
                "close": { "usd": "0.6", "ct": "0.6" },
                "low": { "usd": "0.5", "ct": "0.5" },
                "high": { "usd": "0.6", "ct": "0.6" }
              }
            },
            "buySell": null,
            "liquidity": null,
            "orderbook": {
              "bid": {
                "open": { "usd": "0", "ct": "0" },
                "close": { "usd": "0", "ct": "0" },
                "low": { "usd": "0", "ct": "0" },
                "high": { "usd": "0", "ct": "0" }
              },
              "ask": {
                "open": { "usd": "0.56", "ct": "0.56" },
                "close": { "usd": "0.6", "ct": "0.6" },
                "low": { "usd": "0.5", "ct": "0.5" },
                "high": { "usd": "0.6", "ct": "0.6" }
              }
            },
            "depth": null,
            "statsChange": {
              "volumeChange": 1.1353822760552819,
              "volumeSharesChange": 1.1420015657514841,
              "priceChange": 0.17647058823529413,
              "tradesChange": 0.128,
              "priceRange": 0.18181818181818182
            }
          },
          "outcome1Stats": { "...": "same shape as outcome0Stats, for the 'no' outcome" },
          "allTimeStats": {
            "volume": { "usd": "1862439", "ct": "1862439" },
            "venueVolume": { "usd": "3478959", "ct": "3478959" }
          },
          "statsChange": {
            "volumeChange": 0.7787107886978701,
            "tradesChange": -0.13784461152882205,
            "openInterestChange": 0.10838253428676413
          },
          "scores": {
            "trending": 0.5129627015063644,
            "relevance": 0.9802115747563149,
            "competitive": 0.7098313166349453
          }
        },
        "statsHour1": { "...": "same shape as statsMin5, for the 1h window" },
        "statsHour4": { "...": "same shape as statsMin5, for the 4h window" },
        "statsHour12": { "...": "same shape as statsMin5, for the 12h window" },
        "statsDay1": { "...": "same shape as statsMin5, for the 24h window" },
        "statsWeek1": { "...": "same shape as statsMin5, for the 1w window" },
        "trendingScores": {
          "score5m": 0.5129627015063644,
          "score1": 0.5526402920243513,
          "score4": 0.6330336268045679,
          "score12": 0.6056772499786226,
          "score24": 0.7080148356575182,
          "score1w": 0.49798763786971245
        },
        "relevanceScores": {
          "score5m": 0.9802115747563149,
          "score1": 0.9802115747563149,
          "score4": 0.9802115747563149,
          "score12": 0.9802115747563149,
          "score24": 0.9802115747563149,
          "score1w": 0.9506551892542483
        },
        "competitiveScores": {
          "score5m": 0.7098313166349453,
          "score1": 0.8411082705350258,
          "score4": 0.8190846252610525,
          "score12": 0.8288629254817336,
          "score24": 0.8254551774444506,
          "score1w": 0.8253162265343478
        },
        "allTimeStats": {
          "volume": { "usd": "1862439", "ct": "1862439" },
          "venueVolume": { "usd": "3478959", "ct": "3478959" }
        },
        "eventId": "KXATPMATCH-26JUN01TIAARN:Kalshi"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## FAQ

<AccordionGroup>
  <Accordion title="How many tokens can I add to a single webhook?">
    Webhooks monitor one token at a time, but there is no limit on how many webhooks you can create. To watch many tokens, create one webhook per token (the [`bucketKey`](#organizing-webhooks-with-bucketkey) field makes managing large sets straightforward).
  </Accordion>

  <Accordion title="How do I push real-time buys via webhooks?">
    Create a `TOKEN_PAIR_EVENT` webhook for the token and filter `eventType` to `BUY`. The webhook payload includes the full event object, so you don't need a follow-up call to enrich the data. See the [`TOKEN_PAIR_EVENT` section](#token_pair_event) above for the full creation example.
  </Accordion>

  <Accordion title="What is the `securityToken` field for, and how do I verify it?">
    `securityToken` is your secret. Codex uses it as the HMAC key for the `X-Webhook-Signature` header on every delivery, which authenticates the exact raw request body. See [Verifying webhooks](#verifying-webhooks) for the full verification flow. The legacy body `hash` field is also derived from `securityToken` but is deprecated because it doesn't cover the body.
  </Accordion>

  <Accordion title="How do I get notified when a token launches on a launchpad?">
    Create a [`TOKEN_LAUNCH_EVENT`](#token_launch_event) webhook indexed on `launchpadName` (for example `{ oneOf: ["Pump.fun"] }`) to fire on every new launch from that launchpad, or index on `creatorAddress` to follow a specific deployer wallet. Each webhook is keyed on exactly one launchpad, so create one webhook per launchpad you want to watch. Unlike the [`onLaunchpadTokenEventBatch`](/api-reference/subscriptions/onlaunchpadtokeneventbatch) subscription, the webhook needs no persistent connection.
  </Accordion>

  <Accordion title="How is webhook usage measured against my plan's monthly request limit?">
    Each webhook delivery counts as 1 request. If you also fire a follow-up API call to enrich the event (e.g. fetching token metadata), that's a second request. So if you're processing 10k buys per day with one enrichment call each, that's 20k requests/day or roughly 600k/month.
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.