We’ve got a Node.js Lambda function set up to receive real-time event notifications from Genesys Cloud via an AWS EventBridge integration. The webhook endpoint is public-facing, and we’re trying to verify the request signature to ensure it’s actually coming from Genesys before processing the payload.
The issue is that our signature validation logic keeps failing. We’re following the standard HMAC-SHA256 approach, but the calculated signature never matches the x-genesys-signature header.
Here’s the relevant snippet from our Lambda handler:
const crypto = require('crypto');
exports.handler = async (event) => {
const body = JSON.stringify(event.body);
const timestamp = event.headers['x-genesys-timestamp'];
const signature = event.headers['x-genesys-signature'];
const secret = process.env.GENESYS_WEBHOOK_SECRET;
// Construct the string to sign
const stringToSign = `${timestamp}.${body}`;
// Calculate HMAC
const hmac = crypto.createHmac('sha256', secret);
hmac.update(stringToSign);
const calculatedSignature = hmac.digest('hex');
if (calculatedSignature !== signature) {
console.log('Signature mismatch');
console.log('Expected:', calculatedSignature);
console.log('Received:', signature);
return {
statusCode: 403,
body: 'Invalid signature'
};
}
return {
statusCode: 200,
body: 'OK'
};
};
We’ve double-checked the secret in the Genesys Admin console under Integrations > Webhooks. It matches exactly what’s in our AWS Parameter Store. The timestamp header is present and looks valid.
Is there something specific about how Genesys constructs the payload string for EventBridge integrations? Or maybe the body needs to be normalized in a specific way before hashing?
Any pointers would be appreciated.