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.