Empty Request Body When Handling Mailgun Bounce Webhooks in Meteor

I’m working with Mailgun webhooks in my Meteor app and running into an issue. When I try to process bounce notifications, the request body comes back empty. Other webhook events seem to work fine, but specifically for bounced emails I can’t access any data from this.request.body. Has anyone dealt with this before?

Router.route('/webhooks/mailgun/events', { where: 'server' })
.post(function() {
    Router.onBeforeAction(Iron.Router.bodyParser.urlencoded({
        extended: true
    }));

    this.response.setHeader('Access-Control-Allow-Origin', '*');
    var request = this.request;
    var response = this.response;
    var payload = request.body;

    response.end('Webhook processed');

    var emailData = {
        eventType: '',
        emailAddress: '',
        messageId: '',
        eventTime: '',
        mailDomain: '',
        location: '',
        ipAddr: '',
        browser: '',
        platform: '',
        browserType: '',
        nation: '',
        deviceCategory: '',
        userAgent: '',
        clickedUrl: ''
    };

    if (payload.event === 'delivered') {
        emailData.mailDomain = payload['domain'];
        emailData.eventType = payload['event'];
        var tagData = payload['X-Mailgun-Tag'];
        var trackingId = tagData.split(":")[1];
        emailData.messageId = payload['Message-Id'];
        emailData.emailAddress = payload['recipient'];
        emailData.eventTime = payload['timestamp'];
    }

    if (payload.event === 'opened' || payload.event === 'clicked') {
        emailData.mailDomain = payload['domain'];
        emailData.eventType = payload['event'];
        var tagInfo = payload['tag'];
        var campaignRef = tagInfo.split(":")[1];
        emailData.messageId = payload['message-id'];
        emailData.emailAddress = payload['recipient'];
        emailData.eventTime = payload['timestamp'];

        emailData.location = payload['city'];
        emailData.ipAddr = payload['ip'];
        emailData.browser = payload['client-name'];
        emailData.platform = payload['client-os'];
        emailData.browserType = payload['client-type'];
        emailData.nation = payload['country'];
        emailData.deviceCategory = payload['device-type'];
        emailData.userAgent = payload['user-agent'];
    }

    if (payload.event === 'clicked') {
        emailData.clickedUrl = payload['url'];
    }

    if (payload.event === 'bounced' || payload.event === 'dropped') {
        emailData.mailDomain = payload['domain'];
        emailData.eventType = payload['event'];
        var tagValue = payload['tag'];
        var refId = tagValue ? tagValue.split(":")[1] : '';
        emailData.messageId = payload['message-id'];
        emailData.emailAddress = payload['recipient'];
        emailData.eventTime = payload['timestamp'];
    }

    console.log('Processed webhook data:', emailData);
});

I ran into this exact issue with Mailgun bounce webhooks in Meteor about six months back. The problem is that bounce webhooks come through as multipart/form-data instead of application/x-www-form-urlencoded like other events. Your current bodyParser setup can’t handle that.

What fixed it for me was setting up a raw body parser just for the webhook endpoint. You’ve got to grab the raw request data first, then parse it yourself based on the content-type header. I used WebApp.rawConnectHandlers.use() to catch the request before Iron Router gets to it, then stored the raw body in a temp variable that the route handler could grab later. This handles both bounce events and regular webhooks without breaking anything else.

Check your webhook signature verification before parsing the body. When I debugged similar Mailgun bounce issues, empty request bodies usually meant the webhook got rejected earlier in the pipeline. Mailgun’s bounce webhooks use different auth - if your server isn’t validating signatures correctly, some frameworks will dump the request body. Check your server logs for parsing errors before your route handler runs. I had to set my webhook endpoint to accept both urlencoded and multipart data, then check the content-type header to pick the right parser.

had the same mailgun bounce issue in meteor. you need to set up bodyparser middleware before the route handler, not inside it. move your bodyparser config to the router setup or use webapp.connecthandlers.use() instead. also, bounce events can have different content-type headers.