Webhooks

Push event updates to your servers in real time. Each delivery is a POST carrying the full event payload + a CMC-Signature header. Verify the signature using the endpoint's signing secret before trusting the body.

Event types

TypeTriggers when
events.createdEditorial team adds a new event to the catalog.
events.updatedMaterial change to an existing event (date, impact score, description, source).
events.cancelledEvent is removed from the catalog (cancelled, retracted, or merged into another).
events.high-impactAn event reaches impact >= 7.5 for the first time. Fires once per event lifecycle.

Delivery payload

Every delivery is a JSON body with the same outer shape regardless of type. The data field carries the full event resource (same shape as GET /v2/events/{id}).

{
  "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 2xx within 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 degraded state 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…). Dedupe on it; retries reuse the same id, so the same delivery never reaches your handler twice.