Screen recording webhook duplicates causing 500 on Express middleware

Problem

Screen recording webhooks are firing duplicate events for a single interaction. Second payload is missing the recordingUrl. Express middleware is configured to catch interaction.wrapped events. When the screen recording finishes, the webhook endpoint gets hit twice within 200ms. GC version sits at 2024-6.712.0.

First payload comes through clean with the media URL. Second payload shows up empty on the media object. Middleware tries to download the blob, hits a 404 on the second request. S3 bucket fills up with empty JSON files. This broke the transcription job for the whole team this morning. Dedup logic isn’t catching this. Doing jack all to prevent the duplicate write. Express middleware uses express.json() and validates the signature before processing. First event passes validation. Second event passes validation too. Lambda handler downstream gets confused when recordingUrl is null. Returns a 500 back to Genesys. Queue backs up immediately. Mic stays hot on the alert channel.

{
 "event": "interaction.wrapped",
 "data": {
 "id": "a1b2c3d4",
 "media": null,
 "recordingUrl": null
 }
}

WebSocket Notification API might be batching these incorrectly. Architect flow triggers the recording wrap event twice when the agent clicks “End Interaction”. Logs show the JWT is valid on both hits.

Looks like a race condition in your middleware. The first payload is the actual recording event, the second is likely a generic interaction wrap-up that triggers before the media assets are fully indexed.

Here’s how I handle it:

  • Check for the URL existence: Don’t just catch the event. Verify recordingUrl exists in the payload body before cessing. If it’s null or undefined, drop it.
  • Add a deduplication key: Store the interactionId in a short-lived cache (like Redis with a 5-second TTL). If you see the same ID twice in that window, ignore the second hit.
  • Filter by media type: Ensure the event object actually contains media: 'screen' or similar. Generic interaction.wrapped events don’t always carry media specifics.

Try this middleware snippet:

app.post('/webhook/recording', (req, res) => {
 const { interactionId, recordingUrl } = req.body;

 if (!recordingUrl) {
 console.log('Missing URL, skipping duplicate event');
 return res.status(200).send('OK'); // Acknowledge to stop retries
 }

 // Your cessing logic here
 res.status(200).send('OK');
});

The 500 error is bably because your code crashes when trying to access perties on a null URL. Returning 200 immediately prevents Genesys from retrying.