Signature format updated (2026-04) — the X-Simiz-Signature header now uses the Stripe-like format t=<unix_timestamp>,v1=<hmac_sha256_hex> (signed payload = ${timestamp}.${body}) with a 5 minute replay tolerance. For current verification snippets (Node.js, Python, PHP with timing-safe compare), see the Node.js SDK webhooks guide. The legacy sha256=<hex> snippets below are kept for historical reference only — do not use them.
Every webhook Simiz sends includes a HMAC SHA-256 signature in the X-Simiz-Signature header. You should always verify this signature before processing the event.

How it works

  1. Simiz computes the signature using your webhook secret and the signed payload ${timestamp}.${raw_body}
  2. The signature is sent in the X-Simiz-Signature header as t=<unix_timestamp>,v1=<hmac_sha256_hex>
  3. On your side : parse the header, verify timestamp is within 5 min, recompute HMAC on ${timestamp}.${raw_body}, timing-safe compare

Verification examples

import crypto from 'crypto';

function verifyWebhook(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expectedSignature}`)
  );
}

// Express example
app.post('/webhooks/simiz',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-simiz-signature'] as string;

    if (!verifyWebhook(req.body.toString(), signature, process.env.WEBHOOK_SECRET)) {
      return res.status(401).send('Invalid signature');
    }

    const event = JSON.parse(req.body.toString());
    // Process the event...

    res.status(200).json({ received: true });
  }
);

Security best practices

Always use timingSafeEqual (or equivalent) for signature comparison. Standard string comparison (===) is vulnerable to timing attacks.
  • Use the raw body — Don’t parse JSON before verifying. Middleware like express.json() can alter the payload
  • Rotate secrets — Regenerate your webhook secret periodically via the Dashboard
  • Use HTTPS — Never expose webhook endpoints over plain HTTP
  • Validate event types — Only process events you expect