Skip to content
Main Project

Verify webhook signatures + retries

Article

The signature header

Every webhook includes:

X-Releaseo-Signature: t=1719501234,v1=abc123…
X-Releaseo-Event-Id: evt_…

t is the Unix timestamp; v1 is the HMAC-SHA256 of ${t}.${body} using your signing secret.

Verify in Node.js

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyReleaseo(req, secret) {
  const header = req.headers['x-releaseo-signature'];
  const [tPart, sigPart] = header.split(',');
  const t = tPart.replace('t=', '');
  const v1 = sigPart.replace('v1=', '');

  const payload = `${t}.${req.rawBody}`;
  const expected = createHmac('sha256', secret).update(payload).digest('hex');
  return timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Reject replays

Compare the timestamp against Date.now() — reject anything older than 5 minutes.

Retry behavior

  • 6 attempts over 24 hours

  • Backoff: 30s, 2m, 10m, 1h, 6h, 24h

  • Stop after a 2xx response

  • Audit log in Settings → Integrations → Webhooks → Deliveries

Make endpoints idempotent

Use X-Releaseo-Event-Id as your idempotency key — same id = same event, even on retry.