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
- Simiz computes the signature using your webhook secret and the signed payload
${timestamp}.${raw_body}
- The signature is sent in the
X-Simiz-Signature header as t=<unix_timestamp>,v1=<hmac_sha256_hex>
- 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,
header: string,
secret: string
): boolean {
const parts = header.split(',');
let timestamp = '';
let signature = '';
for (const part of parts) {
const [key, value] = part.split('=');
if (key === 't') timestamp = value;
if (key === 'v1') signature = value;
}
// Reject if timestamp is older than 5 minutes
const fiveMinutes = 5 * 60 * 1000;
if (Date.now() - parseInt(timestamp) * 1000 > fiveMinutes) {
return false;
}
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post('/webhooks/simiz',
express.raw({ type: 'application/json' }),
(req, res) => {
const header = req.headers['x-simiz-signature'] as string;
if (!verifyWebhook(req.body.toString(), header, 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