Ghost + Mailgun Webhooks (or: Why the Endpoint Simply Doesn’t Exist)
“Let’s quickly enable webhooks for bounces and complaints and call it a day.”
I just wanted a clean, fully working Mailgun integration in Ghost.
Newsletters were already sending fine — so I figured: “Let’s quickly enable webhooks for bounces and complaints and call it a day.”
Spoiler: it was anything but quick.
Initial Setup
Environment:
- Ghost (self-hosted, Docker)
- Mailgun (EU region)
- Newsletters sending successfully ✅
- Members enabled ✅
Environment variables looked correct:
bulkEmail__mailgun__domain=blog.michael-bohn.net
bulkEmail__mailgun__apiKey=***
bulkEmail__mailgun__webhookSigningKey=***
bulkEmail__mailgun__baseUrl=https://api.eu.mailgun.net/v3
Everything seemed in place.
And actually, it was.
There's no webhook for Mailgun anymore. Somehow, the "create webhook" option in the Mailgun configuration misled me.
What used to be done via the webhook is now handled by Ghost's active polling. That's the analytics part of Ghost.

The data is retrieved from Mailgun by Ghost like this, for example:
curl -u "api:YOUR_API_KEY"
"https://api.eu.mailgun.net/v3/your.blog.com/events?event=failed&event=complained&event=delivered&event=opened&event=clicked&limit=10"Some old web posts and AI-hallucination led me astray. And so I ran into hours of debugging and reading forum posts until I finally got it.