Connect Domain
Webhooks

Verifying signatures

HMAC-SHA256 signature verification and replay protection.

Each request carries two headers:

  • X-CD-Timestamp: <unix seconds> — when we signed the delivery.
  • X-CD-Signature: sha256=<hex> — an HMAC-SHA256, using your endpoint's signing secret, over the string ${timestamp}.${rawBody} (the timestamp, a dot, then the exact bytes of the request body).

Binding the timestamp into the signature lets you reject replays: check that the timestamp is recent (we recommend a 5-minute window) before trusting the payload. Each attempt — including retries and replays — is re-signed with a fresh timestamp.

const crypto = require('crypto');

function verify(rawBody, tsHeader, sigHeader, secret, toleranceSec = 300) {
  const ts = parseInt(tsHeader, 10);
  if (!Number.isFinite(ts)) return false;
  if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false; // replay window
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(`${ts}.`)
    .update(rawBody)
    .digest('hex');
  const a = Buffer.from(sigHeader || ''), b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Compute the HMAC over the exact bytes received (don't re-serialize the JSON) and prepend ${timestamp}. before hashing.