Webhooks
Push event updates to your servers in real time. Each delivery is a POST carrying an event payload and a CMC-Signature header. Verify the signature using the endpoint's signing secret before trusting the body.
◆ Event types
| Type | Triggers when |
|---|---|
| events.created | An event is published for the first time. |
| events.updated | A published event is edited, temporarily unpublished, or republished with the same id. |
| events.cancelled | An event is permanently removed from the catalog, whether cancelled by a project or deleted by the editorial team. |
| events.high-impact | An event reaches impact >= 7.5 for the first time. Fires once per event lifecycle. |
◆ Delivery payload
Every delivery uses the same outer envelope. Its evt_dlv_* id identifies the delivery and is distinct from data.id, which is the exact numeric API V2 event id serialized as a string. Created, updated, and high-impact deliveries carry current event data. Cancelled deliveries contain only the event's id, slug, title, and cancelled: true. Use data.id to remove it from your local catalog.
{ "id": "evt_dlv_01HW8K2D5N", "type": "events.created", "apiVersion": "v2", "deliveredAt": "2026-04-29T14:22:08Z", "data": { "id": "48291", "slug": "ethereum-pectra-upgrade", "title": "Ethereum Pectra Upgrade", "date": "2026-05-06T12:00:00Z", "isEstimated": false, "displayedDate": "06 May 2026", "coins": [ { "slug": "ethereum", "symbol": "ETH", "name": "Ethereum" } ], "impact": 8.5 } }
◆ Verify the signature
Each POST carries a CMC-Signature header containing sha256=followed by the HMAC-SHA256 hex digest of the raw request body, signed with your endpoint's signing secret. Reject any request whose signature doesn't match.
import crypto from "node:crypto"; import express from "express"; const app = express(); const SECRET = process.env.CMC_WEBHOOK_SECRET; app.post( "/cmc/webhooks", express.raw({ type: "application/json" }), (req, res) => { const sigHeader = req.header("CMC-Signature") ?? ""; const expected = "sha256=" + crypto .createHmac("sha256", SECRET) .update(req.body) .digest("hex"); const sigBuf = Buffer.from(sigHeader); const expBuf = Buffer.from(expected); if ( sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf) ) { return res.status(401).end(); } const event = JSON.parse(req.body.toString("utf8")); // ... handle event res.status(200).end(); }, );
◆ Retry policy
- Success window: your endpoint must respond
2xxwithin 10 s. Anything else (4xx, 5xx, timeout, connection error) is treated as a failed delivery. - Backoff: failed deliveries are retried with exponential backoff for up to 24 hours. Schedule: 1m → 5m → 30m → 2h → 6h.
- Endpoint health: if 3+ consecutive deliveries fail in a 24h window, the endpoint enters
degradedstate and surfaces in your dashboard. After 24h of continuous failure, it auto-pauses; resume manually once your receiver is fixed. - Idempotency: every delivery carries a unique
id(e.g.evt_dlv_01HW…). Retries reuse the same id and may reach your handler more than once, so persist that id and ignore duplicates.