Verifying webhook signatures
5 min read Updated February 14, 2026
Why Verify Signatures?
Webhook signatures ensure that events are genuinely from ComplianceGrid and haven't been tampered with. Always verify signatures in production — without verification, an attacker could send forged events to your endpoint.
How It Works
Every webhook request includes a CG-Signature header containing an HMAC-SHA256 signature of the request body, computed using your webhook secret.
Verification Steps
1. Extract the CG-Signature header from the request
2. Compute HMAC-SHA256 of the raw request body using your webhook secret
3. Compare the computed signature with the header value
4. Reject the request if they don't match
Node.js Example
javascript
import crypto from 'crypto';
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf-8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your Express handler:
app.post('/webhooks/compliancegrid', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['cg-signature'];
if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process verified event...
});Python Example
python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Important Notes
- Use timing-safe comparison (not
==) to prevent timing attacks - Parse the body after verification, not before — use the raw bytes for HMAC
- Your webhook secret is shown once when you create the endpoint — store it securely
- Rotate secrets periodically via the Developer Portal
Was this article helpful?