We’ve set up an internal service to ingest .queue.member.assigned events from Genesys Cloud via a custom webhook endpoint. The goal is to trigger a downstream notification, but we need to ensure the requests are legit. I’m trying to implement signature verification to stop replay attacks, following the docs on signing requests. The problem is the signature check is always failing on the first request, even though the payload looks correct.
Here’s the Node.js snippet handling the verification. We’re using the crypto module to generate the HMAC SHA256 hash. The secret comes from the environment variable set during the webhook creation in the admin UI.
const crypto = require('crypto');
app.post('/webhook/genesys', (req, res) => {
const signature = req.headers['x-genesys-signature'];
const timestamp = req.headers['x-genesys-timestamp'];
const body = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', process.env.GENESYS_WEBHOOK_SECRET)
.update(`${timestamp}${body}`)
.digest('hex');
if (signature !== expectedSignature) {
console.log('Signature mismatch');
res.status(401).send('Unauthorized');
return;
}
res.status(200).send('OK');
});
The x-genesys-signature header arrives, but the hash never matches. I’ve tried changing the update string to just the body, or body plus timestamp, but nothing works. The timestamp header is there, formatted as a Unix epoch string. Am I missing a step in the concatenation? The docs are vague on the exact input string format for the HMAC. Also, the timestamp seems to drift a bit when I log it, which might be causing issues if there’s a time window check I’m unaware of. No error codes from Genesys, just my code rejecting it.