Skip to content

API reference

Use the Chainhook API to create and manage webhooks, look up the deliveries we send to your endpoints, and read usage for your account.

The API follows REST conventions: URLs are organized around resources, requests and responses are JSON, errors use standard HTTP status codes, and every request is authenticated with an API key. Anything you can do with webhooks and deliveries in the dashboard, you can also do here.

AI agents can use the same operations through the MCP server, and this reference is also available as plain text at llms-full.txt.

Base URL

https://api.chainhook.io/v1

Authentication

Send an API key as a bearer token in the Authorization header of every request. Requests without a valid key fail with 401.

Create an account, then add a key on the API keys page of your dashboard. A key is shown only once, when you create it, so store it somewhere safe.

When you create a key, give it full access or only the permissions it needs. Each endpoint lists the permission it requires, and a key without it gets 403. Full access also covers permissions added later.

Each key expires on the date you choose, or never if you pick no expiration. Keep them on your servers, out of client-side code and source control, and revoke any key you think has leaked.

Permissions

webhooks:readscope
List and retrieve webhooks.
webhooks:writescope
Create, update, and delete webhooks.
deliveries:readscope
List and retrieve deliveries.
deliveries:replayscope
Replay deliveries.
account:readscope
Read account stats and plan usage.
mcp:usescope
Connect to the MCP server. The tools it lists still depend on the other permissions.

Authenticated request

curl https://api.chainhook.io/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Errors

Chainhook uses conventional HTTP status codes. Codes in the 2xx range mean the request worked. Codes in the 4xx range mean something in the request needs fixing, and the body's message says what. Validation failures also include an errors object keyed by field.

Status codes

202Accepted
The request was accepted for processing, such as a replay.
204No Content
The request succeeded and returns no body, such as a delete.
401Unauthorized
The API key is missing or invalid.
403Forbidden
The API key doesn't have the permission this endpoint needs.
404Not Found
The resource doesn't exist or belongs to another account.
409Conflict
The delivery is still in progress and can't be replayed yet.
422Unprocessable Content
The request failed validation; see the errors object. Also returned when creating or resuming a webhook would go over your plan's active webhook limit, or when you resume a webhook while detection is paused.
429Too Many Requests
You exceeded the rate limit. Wait a moment and try again.

Error responses

// 422 Unprocessable Content
{
  "message": "The type field is required.",
  "errors": { "type": ["The type field is required."] }
}

// 422 — creating a webhook at your plan's active webhook limit
{
  "message": "Your Free plan allows 3 active webhooks. Pause a webhook or upgrade your plan.",
  "errors": { "network": ["..."] }
}

// 422 — resuming a webhook after the free allowance is used
{
  "message": "Free allowance used. Subscribe to a plan to resume detection.",
  "errors": { "status": ["..."] }
}

// 401 Unauthorized
{ "message": "Unauthenticated." }

// 403 Forbidden
{ "message": "This API key is missing the webhooks:write permission." }

// 404 Not Found
{ "message": "The requested resource was not found." }

Pagination

List endpoints return results newest first, one page at a time. Set the page size with per_page and move between pages with cursor, using the values in meta.next_cursor and meta.prev_cursor. A null cursor means there's no page in that direction.

Query parameters

per_pagenumber
Items per page, from 1 to 100. Defaults to 25.
cursorstring
The cursor for the next or previous page, from meta.next_cursor or meta.prev_cursor.

Paginated response

{
  "data": [
    { "id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72", "network": "bsc", "status": "active", ... }
  ],
  "links": { "first": null, "last": null, "prev": null, "next": "https://api.chainhook.io/v1/webhooks?cursor=eyJpZCI6Li4ufQ" },
  "meta": { "path": "https://api.chainhook.io/v1/webhooks", "per_page": 25, "next_cursor": "eyJpZCI6Li4ufQ", "prev_cursor": null }
}

Rate limits

Each account can make up to 120 requests per minute, shared across its API keys. Every response includes X-RateLimit-Limit and X-RateLimit-Remaining headers so you can pace your requests.

Requests over the limit fail with 429. Wait the number of seconds in the Retry-After header before you try again.

Rate-limited response

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 37
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0

{ "message": "Too many requests. Wait a moment and try again." }

Networks

Use one of these values as network when you create a webhook. Native coin transfers are detected where a coin is listed, and token transfers for the tokens listed. A transfer is delivered once its block has the number of confirmations shown.

NetworkValueNative coinTokensConfirmations
EthereumethereumETHUSDT, USDC, BUSD, DAI, WETH, WBTC, LINK, UNI, SHIB, PEPE12
BNB Smart ChainbscNot detectedUSDT, USDC, BUSD, DAI, WBNB, Cake, ETH, BTCB15
PolygonpolygonNot detectedUSDT, USDC, DAI, WETH, WPOL, WBTC64
Arbitrum OnearbitrumNot detectedUSDT, USDC, DAI, WETH, ARB, WBTC20
OP MainnetoptimismNot detectedUSDT, USDC, DAI, WETH, OP, WBTC20
BasebaseNot detectedUSDC, DAI, WETH, cbBTC20
Avalanche C-ChainavalancheNot detectedUSDT, USDC, DAI.e, WAVAX, WETH.e, WBTC.e20
TRONtronNot detectedUSDT, USDC, WTRX, JST19
BitcoinbitcoinBTCNot detected6

Receiving webhooks

When a confirmed transfer matches one of your webhooks, Chainhook sends a POST request to its webhook URL. The body is JSON and has the same shape on every network.

Headers

X-Webhook-Idstring
The delivery ID (UUID). It stays the same on retries and replays, so use it to skip deliveries you have already processed.
X-Webhook-Signaturestring
Hex-encoded HMAC-SHA256 of the raw request body, signed with the webhook's signing secret.
Content-Typestring
Always application/json.
User-Agentstring
Always chainhook/1.x.

Body

idstring
The delivery ID (UUID), identical to X-Webhook-Id. It stays the same on retries and replays.
eventstring
The event type. Currently always "transfer.detected".
created_atstring
When the transfer was detected (UTC, ISO 8601).
webhook_idstring
The ID of the webhook that matched the transfer.
data.networkstring
The network, e.g. "bsc".
data.directionstring
"in" or "out", relative to the watched address.
data.typestring
"native" for the network's native coin, "token" for token transfers.
data.transaction_hashstring
The transaction hash.
data.transfer_indexnumber
Distinguishes transfers within one transaction: the log index for tokens, or the output index for Bitcoin (negative for spends).
data.block_numbernumber
The block height.
data.block_hashstring
The block hash. Compare it with the chain to detect reorganizations.
data.block_timestampstring | null
The block time (UTC, ISO 8601), or null when the network doesn't report it. Null is common for token transfers.
data.confirmationsnumber
Confirmations at the time of detection.
data.fromstring | null
The sender address. Null for Bitcoin receipts, which have no sender.
data.tostring | null
The recipient address. Null for Bitcoin spends, which have no recipient.
data.currencystring
The asset symbol, e.g. "USDT" or "BNB". Symbols aren't unique, so identify a token by data.network and data.contract_address.
data.amountstring
The amount as a decimal string, scaled by data.decimals.
data.amount_rawstring
The raw on-chain integer amount, in the smallest unit (e.g. wei).
data.decimalsnumber
The number of decimals of the coin or token.
data.contract_addressstring | null
The token contract address. Null for native coins.

Example request

POST /webhooks/chainhook HTTP/1.1
Host: api.example.com
Content-Type: application/json
User-Agent: chainhook/1.x
X-Webhook-Id: 01a0fbfd-4fe4-7a3c-8f21-6b9d0e4c7a15
X-Webhook-Signature: 7a12d02673420517279805cf8188a1a62c6f69706161ba5cf0a8ec91ea66b8e3

{
  "id": "01a0fbfd-4fe4-7a3c-8f21-6b9d0e4c7a15",
  "event": "transfer.detected",
  "created_at": "2026-10-02T09:41:17Z",
  "webhook_id": "01a0fbf2-fadc-7c3e-9b1a-4d6f2e8a5c71",
  "data": {
    "network": "bsc",
    "direction": "in",
    "type": "token",
    "transaction_hash": "0x7c1e9f4a2b6d8035e4f1a9c27d3b5e8061f4a2c9d7b3e5f8a1c4d6e9b2f7a3c5",
    "transfer_index": 214,
    "block_number": 71206438,
    "block_hash": "0x3a8f2c6e1d9b4f7a0c5e8d2b6f1a9c4e7d0b3f6a2c8e5d1b9f4a7c0e3d6b8f2a",
    "block_timestamp": "2026-10-02T09:41:05Z",
    "confirmations": 16,
    "from": "0xe3b1c5a9f27d4e8b06c1a3f5d9e2b7c4a8f6d0e1",
    "to": "0x9d2a7e41c8b05f3e6a1d4c7b28e0f95a3b6c1d47",
    "currency": "USDT",
    "amount": "1250.75",
    "amount_raw": "1250750000000000000000",
    "decimals": 18,
    "contract_address": "0x55d398326f99059ff775485246999027b3197955"
  }
}

Verify signatures

Every request carries an X-Webhook-Signature header: the hex-encoded HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret. Check it before you act on a request.

  1. Read the raw body exactly as it arrived. Parsing and re-encoding the JSON changes the bytes and breaks the signature.
  2. Compute the HMAC-SHA256 of that body with your signing secret, and hex-encode it.
  3. Compare the result with the header using a constant-time comparison. If they don't match, respond with 401 and stop.

The signing secret is returned only when you create the webhook, so store it with your other credentials.

import crypto from 'node:crypto';
import express from 'express';

const app = express();

app.post(
  '/webhooks/chainhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.get('X-Webhook-Signature') ?? '';
    const expected = crypto
      .createHmac('sha256', process.env.CHAINHOOK_SIGNING_SECRET)
      .update(req.body)
      .digest('hex');

    const valid =
      signature.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

    if (!valid) {
      return res.sendStatus(401);
    }

    const event = JSON.parse(req.body);
    // Skip event.id if you've already processed it.

    res.sendStatus(200);
  },
);

Retries and replays

Respond with any 2xx status within 10 seconds to acknowledge a delivery. Any other status, a timeout, or a connection error counts as a failed attempt. Failed attempts are retried automatically, up to 3 attempts in total, waiting 1 minute, then 5 minutes between attempts.

Redirects aren't followed, so a 3xx response counts as a failure. Requests to private or reserved IP addresses are blocked and aren't retried.

When every attempt fails, the delivery is marked failed. You can send it again from the Deliveries page of your dashboard or with the replay endpoint. Replays are free.

Deliveries are sent at least once, so the same event can reach you more than once. The X-Webhook-Id header and the body's id stay the same across retries and replays: record the IDs you've handled and skip repeats. If your handler does slow work, queue it and respond right away.

Webhooks

A webhook watches one address on one network and sends matching transfers to your webhook URL. Use type and direction to choose which transfers match.

Attributes

idstring
Unique identifier for the webhook (UUID).
networkstring
The network being watched, e.g. "bsc".
typestring
"address", "native", or "token".
directionstring
"in", "out", or "any".
addressstring
The watched address.
webhook_urlstring
The URL that receives deliveries.
statusstring
"active", "paused", or "suspended". Webhooks are suspended while detection is paused, and resume on their own when it resumes.
suspension_reasonstring | null
Why the webhook is suspended, e.g. "Free allowance used. Subscribe to a plan to resume detection." Null when it isn't suspended.
secretstring
The signing secret. Only included in the response that creates the webhook.
created_atstring
When the webhook was created (UTC, ISO 8601).
updated_atstring
When the webhook last changed (UTC, ISO 8601).

The webhook object

{
  "data": {
    "id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72",
    "network": "bsc",
    "type": "token",
    "direction": "in",
    "address": "0xaaaa000000000000000000000000000000000001",
    "webhook_url": "https://api.example.com/webhooks",
    "status": "active",
    "suspension_reason": null,
    "created_at": "2026-06-14T12:00:00Z",
    "updated_at": "2026-06-14T12:00:00Z"
  }
}

List webhooks

GET/v1/webhooks

List your webhooks.

Requires the webhooks:read permission.

Query parameters

searchstring
Only return webhooks whose ID, address, webhook URL, network, transfer type, direction, or status starts with this term.
statusstring
"active", "paused", or "suspended".
networkstring
A network, e.g. "ethereum" or "bsc".
typestring
"address", "native", or "token".
directionstring
"in", "out", or "any".

Request

curl "https://api.chainhook.io/v1/webhooks?per_page=25" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    { "id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72", "network": "bsc", "status": "active", ... }
  ],
  "links": { "first": null, "last": null, "prev": null, "next": "https://api.chainhook.io/v1/webhooks?cursor=eyJpZCI6Li4ufQ" },
  "meta": { "path": "https://api.chainhook.io/v1/webhooks", "per_page": 25, "next_cursor": "eyJpZCI6Li4ufQ", "prev_cursor": null }
}

Create a webhook

POST/v1/webhooks

Create a webhook. Returns 201 Created. The response includes the signing secret, which isn't returned again. While detection is paused, the webhook is created with the status "suspended".

Requires the webhooks:write permission.

Request body

networkstringRequired
An enabled network, e.g. "ethereum", "bsc", or "bitcoin".
typestringRequired
"address" (all transfers), "native" (native coin transfers), or "token" (token transfers). Supported coins and tokens vary by network.
directionstringRequired
"in" (incoming), "out" (outgoing), or "any" (both).
addressstringRequired
The address to watch. Up to 128 characters.
webhook_urlstringRequired
The public HTTPS URL that receives deliveries. Up to 2,048 characters.
secretstring
Signing secret, 32–128 characters. Generated for you if omitted, and returned only in the create response.

Request

curl -X POST https://api.chainhook.io/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "network": "bsc",
    "type": "token",
    "direction": "in",
    "address": "0xAaaa000000000000000000000000000000000001",
    "webhook_url": "https://api.example.com/webhooks"
  }'

Response

{
  "data": {
    "id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72",
    "network": "bsc",
    "type": "token",
    "direction": "in",
    "address": "0xaaaa000000000000000000000000000000000001",
    "webhook_url": "https://api.example.com/webhooks",
    "status": "active",
    "suspension_reason": null,
    "created_at": "2026-06-14T12:00:00Z",
    "secret": "N6x0cS2rUe4b8Wm1tQy9Zp3Lk5Hd7Fg0",
    "updated_at": "2026-06-14T12:00:00Z"
  }
}

Get a webhook

GET/v1/webhooks/{webhook}

Retrieve a webhook.

Requires the webhooks:read permission.

Path parameters

webhookstringRequired
The ID of the webhook.

Request

curl https://api.chainhook.io/v1/webhooks/WEBHOOK_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72",
    "network": "bsc",
    "type": "token",
    "direction": "in",
    "address": "0xaaaa000000000000000000000000000000000001",
    "webhook_url": "https://api.example.com/webhooks",
    "status": "active",
    "suspension_reason": null,
    "created_at": "2026-06-14T12:00:00Z",
    "updated_at": "2026-06-14T12:00:00Z"
  }
}

Update a webhook

PATCH/v1/webhooks/{webhook}

Pause or resume a webhook, or change its webhook URL.

Requires the webhooks:write permission.

Path parameters

webhookstringRequired
The ID of the webhook.

Request body

statusstring
"active" to resume the webhook, or "paused" to pause it. Resuming returns 422 at your plan's active webhook limit, or while detection is paused.
webhook_urlstring
A new public HTTPS URL.

Request

curl -X PATCH https://api.chainhook.io/v1/webhooks/WEBHOOK_ID \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "paused" }'

Response

{
  "data": {
    "id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72",
    "network": "bsc",
    "type": "token",
    "direction": "in",
    "address": "0xaaaa000000000000000000000000000000000001",
    "webhook_url": "https://api.example.com/webhooks",
    "status": "active",
    "suspension_reason": null,
    "created_at": "2026-06-14T12:00:00Z",
    "updated_at": "2026-06-14T12:00:00Z"
  }
}

Delete a webhook

DELETE/v1/webhooks/{webhook}

Delete a webhook and its delivery history. Returns 204 No Content.

Requires the webhooks:write permission.

Path parameters

webhookstringRequired
The ID of the webhook.

Request

curl -X DELETE https://api.chainhook.io/v1/webhooks/WEBHOOK_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

HTTP/1.1 204 No Content

Delete webhooks in bulk

POST/v1/webhooks/bulk-delete

Delete up to 1,000 webhooks at once. Returns the number of webhooks deleted.

Requires the webhooks:write permission.

Request body

idsstring[]Required
The IDs of the webhooks to delete.

Request

curl -X POST https://api.chainhook.io/v1/webhooks/bulk-delete \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ids": ["WEBHOOK_ID_1", "WEBHOOK_ID_2"] }'

Response

{ "deleted": 2 }

Deliveries

A delivery is one detected transfer sent to one webhook URL. It keeps the payload, the number of attempts, and your endpoint's latest response, so you can see what happened and replay it.

Attributes

idstring
Unique identifier for the delivery (UUID). Sent to your endpoint as X-Webhook-Id.
webhook_idstring
The webhook that matched the transfer.
tx_hashstring
The transaction hash.
transfer_indexnumber
The position of the transfer within its transaction.
statusstring
"pending", "delivered", or "failed".
attemptsnumber
How many times the delivery has been sent.
response_codenumber | null
The HTTP status of your endpoint's latest response, or null if there isn't one yet.
response_bodystring | null
The body of your endpoint's latest response, or null.
delivered_atstring | null
When your endpoint accepted the delivery (UTC, ISO 8601), or null.
payloadobject
The event body sent to your endpoint.
created_atstring
When the transfer was detected (UTC, ISO 8601).
updated_atstring
When the delivery last changed (UTC, ISO 8601).

The delivery object

{
  "data": {
    "id": "019ebf94-1111-7000-aaaa-222233334444",
    "webhook_id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72",
    "tx_hash": "0xabc...",
    "transfer_index": 26,
    "status": "delivered",
    "attempts": 1,
    "response_code": 200,
    "response_body": "ok",
    "delivered_at": "2026-06-14T12:00:05Z",
    "payload": { "id": "019ebf94-1111-7000-aaaa-222233334444", "event": "transfer.detected", "data": { ... } },
    "created_at": "2026-06-14T12:00:00Z",
    "updated_at": "2026-06-14T12:00:05Z"
  }
}

List deliveries

GET/v1/deliveries

List deliveries.

Requires the deliveries:read permission.

Query parameters

searchstring
A status, network, direction, transfer type, currency, or HTTP status code, or the first 6 or more characters of a delivery ID, transaction hash, address, or webhook ID.
webhook_idstring
Only return deliveries for this webhook ID.
statusstring
"pending", "delivered", or "failed".
networkstring
A network, e.g. "ethereum" or "bsc".
directionstring
"in" or "out".
typestring
"native" or "token".
currencystring
A currency symbol, e.g. "USDT".

Request

curl "https://api.chainhook.io/v1/deliveries?status=failed" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    { "id": "019ebf94-1111-7000-aaaa-222233334444", "status": "failed", "attempts": 3, ... }
  ],
  "links": { "first": null, "last": null, "prev": null, "next": null },
  "meta": { "path": "https://api.chainhook.io/v1/deliveries", "per_page": 25, "next_cursor": null, "prev_cursor": null }
}

Get a delivery

GET/v1/deliveries/{delivery}

Retrieve a delivery, including its payload and your endpoint's latest response.

Requires the deliveries:read permission.

Path parameters

deliverystringRequired
The ID of the delivery.

Request

curl https://api.chainhook.io/v1/deliveries/DELIVERY_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": {
    "id": "019ebf94-1111-7000-aaaa-222233334444",
    "webhook_id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72",
    "tx_hash": "0xabc...",
    "transfer_index": 26,
    "status": "delivered",
    "attempts": 1,
    "response_code": 200,
    "response_body": "ok",
    "delivered_at": "2026-06-14T12:00:05Z",
    "payload": { "id": "019ebf94-1111-7000-aaaa-222233334444", "event": "transfer.detected", "data": { ... } },
    "created_at": "2026-06-14T12:00:00Z",
    "updated_at": "2026-06-14T12:00:05Z"
  }
}

Replay a delivery

POST/v1/deliveries/{delivery}/replay

Send a delivered or failed delivery again. Returns 202 Accepted, or 409 Conflict while the delivery is still in progress. Replays are free.

Requires the deliveries:replay permission.

Path parameters

deliverystringRequired
The ID of the delivery.

Request

curl -X POST https://api.chainhook.io/v1/deliveries/DELIVERY_ID/replay \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

HTTP/1.1 202 Accepted

Get account stats

GET/v1/stats

Webhook and delivery counts, the delivery success rate, and your most recent deliveries.

Requires the account:read permission.

Request

curl https://api.chainhook.io/v1/stats \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "webhooks": { "total": 5, "active": 3, "paused": 1, "suspended": 1 },
  "deliveries": { "total": 5, "pending": 1, "delivered": 3, "failed": 1 },
  "success_rate": 75,
  "recent_deliveries": [
    {
      "id": "019ebf94-1111-7000-aaaa-222233334444",
      "webhook_id": "019ebf93-9c6a-7164-ba16-444f2e3aeb72",
      "tx_hash": "0xabc...",
      "status": "delivered",
      "response_code": 200,
      "created_at": "2026-06-14T12:00:00Z"
    }
  ]
}

Billing

Each account is on a plan with a monthly allowance of detections and a limit on active webhooks (see pricing). Each transfer that matches a webhook is one detection, counted once against your allowance when it's detected, even if your endpoint fails. Retries and replays are free.

Only active webhooks count toward your webhook limit; paused and suspended webhooks don't. At the limit, creating a webhook or setting a webhook's status to "active" fails with 422. If your plan changes to one with a lower limit, active webhooks over the new limit are suspended.

Pay as you go has no monthly fee and includes 100 trial detections for 1 month after sign-up, then each detection is paid from prepaid credit at $0.001. Paid plans keep detecting past the allowance and bill extra detections at the plan's overage rate after the period ends. If you set a monthly overage cap, detection pauses at the cap until the next period.

While detection is paused, your active webhooks are suspended and new transfers aren't detected. Detections recorded before the pause are still delivered. New webhooks are created with the status "suspended" and a suspension_reason, and setting a webhook's status to "active" fails with 422. Suspended webhooks resume automatically when detection resumes; paused webhooks stay paused.

AI agents can read the current plan and usage with get-usage-tool on the MCP server.

Plans

free$0/month
100 trial detections for 1 month. 3 active webhooks. $0.001 per detection after the trial.
starter$9/month or $90/year
25,000 detections a month. 25 active webhooks. $0.0006 per extra detection.
growth$29/month or $290/year
100,000 detections a month. 100 active webhooks. $0.0003 per extra detection.
scale$99/month or $990/year
400,000 detections a month. 200 active webhooks. $0.0002 per extra detection.

get-usage-tool result

{
  "plan": "free",
  "plan_label": "Pay as you go",
  "detections": 64,
  "included": 100,
  "remaining": 36,
  "resets_at": "2026-11-01T00:00:00Z"
}

MCP server

The MCP server lets AI agents and other tools that speak the Model Context Protocol manage your webhooks and deliveries. It runs at https://mcp.chainhook.io/mcp over streamable HTTP.

Clients that support OAuth only need that URL. The first time you connect, they open a browser window where you sign in to Chainhook and allow access. Other clients can send an API key in the Authorization header instead.

An API key needs the mcp:use permission to connect, and the server lists only the tools its other permissions allow. Apps you connect by signing in can use every tool.

Every tool acts only on your account. Connected apps are listed on the API keys page of your dashboard, where you can revoke their access.

Tools and required permissions

list-webhooks-toolwebhooks:read
List your webhooks, with the same filters and cursor pagination as the REST API.
get-webhook-toolwebhooks:read
Retrieve a webhook by ID.
create-webhook-toolwebhooks:write
Create a webhook. The signing secret is returned only in this result.
update-webhook-toolwebhooks:write
Pause or resume a webhook, or change its webhook URL.
delete-webhook-toolwebhooks:write
Delete a webhook and its delivery history.
bulk-delete-webhooks-toolwebhooks:write
Delete up to 1,000 webhooks at once.
list-deliveries-tooldeliveries:read
List deliveries, with the same filters and cursor pagination as the REST API.
get-delivery-tooldeliveries:read
Retrieve a delivery, including its payload.
replay-delivery-tooldeliveries:replay
Send a delivered or failed delivery again.
get-stats-toolaccount:read
Webhook and delivery counts, the success rate, and recent deliveries.
get-usage-toolaccount:read
Your plan and usage this period: plan, plan_label, detections used, detections included, detections remaining before detection pauses (null when overage is unlimited), and resets_at.
  1. Run the command. Add --scope user to use it in every project.
  2. In Claude Code, run /mcp, choose chainhook, and sign in.

Terminal

claude mcp add --transport http chainhook https://mcp.chainhook.io/mcp