Trying to understand why my Lambda function is failing to parse the payload from a Genesys Cloud webhook. I’ve set up a simple Node.js handler that logs the incoming event, but the data structure isn’t what I expected. The webhook is configured to send a single event for routing.queue.conversation.added. Here is the handler code:
exports.handler = async (event) => {
console.log("Received event:", JSON.stringify(event));
const body = event.body ? JSON.parse(event.body) : event;
console.log("Parsed body:", JSON.stringify(body));
if (!body.data) {
return {
statusCode: 400,
body: JSON.stringify({ error: "Missing data field" })
};
}
return {
statusCode: 200,
body: JSON.stringify({ message: "Success" })
};
};
The logs show the event object has a headers field with Content-Type: application/json, but the body field is undefined when I check it. The event itself contains the JSON data directly, not nested under body. I’m used to API Gateway proxies wrapping the payload, but this is a direct webhook invocation. Is there a specific configuration in Genesys Cloud or the Lambda trigger that changes this behavior? I’ve checked the managed identity permissions and they look fine. What am I missing?
If I recall correctly, the Lambda event structure wraps the actual webhook body inside event.body as a string, not as a parsed object. You’ll need to JSON.parse(event.body) first. Also, Genesys Cloud webhooks send an array of events in the events field, even if it’s just one.
Here’s how I handle it in my Electron app’s IPC bridge when receiving similar payloads:
const payload = JSON.parse(event.body);
const events = payload.events || [];
events.forEach(evt => {
if (evt.type === 'routing.queue.conversation.added') {
console.log('Queue ID:', evt.data.queueId);
}
});
Make sure your webhook configuration has Content Type set to application/json. If it’s set to text/plain, you’ll get raw text. Also, check that the Lambda IAM role has lambda:InvokeFunction permissions if you’re triggering other functions. The 403 errors I mentioned in my previous post often stem from scope issues, but here it’s likely a parsing error.
It depends, but generally… you’re missing the parse step.
- docs state “the payload is a JSON string.”
- const data = JSON.parse(event.body);
- const evt = data.events[0];
check your logs. if it’s still broken, you’re probably hitting a timeout.
you need to handle the string parsing first, but also watch out for the events array wrapper. the suggestions above are spot on about JSON.parse(event.body), but you’ll likely hit a null pointer if you don’t check if the array exists before accessing index 0. in my express middleware i always do a quick guard clause.
exports.handler = async (event) => {
const body = typeof event.body === 'string' ? JSON.parse(event.body) : event.body;
if (!body.events || !Array.isArray(body.events)) {
return { statusCode: 400, body: 'invalid payload' };
}
const evt = body.events[0];
console.log('event type:', evt.eventType);
// process routing.queue.conversation.added logic here
return { statusCode: 200 };
};
also make sure your lambda has application/json in the integration request templates if you’re using apigateway directly, otherwise it might not parse automatically. i’ve seen that trip people up when switching from test invoke to real traffic.