Webhooks
Webhook integration

Mailgun Webhooks: Send Email Events to Sazabi

By Sazabi Team

Webhook facts checked on the platform’s docs on Nov 8, 2026.

Mailgun webhooks tell your systems what happened to each email after you send it: queued, delivered, bounced, opened, clicked, unsubscribed or reported as spam. Pointing them at Sazabi gives you a searchable history of those events without writing a receiver.

What Mailgun sends

When an event occurs, Mailgun sends an HTTP or HTTPS POST with a JSON payload to the URL you registered. The event types in the docs are accepted, delivered, temporary_fail, permanent_fail, opened, clicked, unsubscribed and complained. Open, click and unsubscribe events only fire if the matching tracking option is enabled for the domain.

You can register webhooks per domain or for the whole account, including subaccounts. Each event type takes up to 3 URLs. The US and EU regions are isolated, so a webhook created in one region does not exist in the other. HTTPS endpoints need a certificate from a trusted CA, not a self-signed one.

Why send Mailgun events to Sazabi

Email failures rarely show up in your application logs. Your code calls the Mailgun API, gets a 200 and moves on. The bounce arrives later, as a webhook.

Sazabi's Webhook Events source stores each JSON payload as a log record, with the payload under webhook.* attributes. A bounce then sits in the same project as the log lines your service wrote when it queued the message. Start a thread and search for the recipient address or the message ID to see both sides.

An optional path suffix helps you keep sources apart. If you append /mailgun to the intake URL, Sazabi stores it as webhook.path. A JSON array becomes one record per element.

Set up Mailgun webhooks

Copy your intake URL from the Webhook Events source in Sazabi. It encodes your project key, so treat it as a secret. Then:

  1. In the Mailgun control panel, open Sending and then Webhooks.
  2. Choose the event types you want to store, such as permanent_fail, temporary_fail and complained. Selecting fewer types keeps volume down.
  3. Paste the intake URL, with a path suffix if you want one, and save.

You can also create webhooks through the API with POST /v3/domains/{domain}/webhooks for one domain or POST /v1/webhooks for the account. Mailgun notes that account-level changes can take up to 10 minutes to become effective because of caching.

To send a test, use the test webhook endpoint. It posts dummy data to a URL you give it:

curl -i -X POST \
  -u '<username>:<password>' \
  https://api.mailgun.net/v1/alerts/webhooks/test \
  -H 'Content-Type: application/json' \
  -d '{"event_type": "delivered", "url": "<your intake URL>/mailgun"}'

Then search the Webhook Events source for the test record. Sazabi accepts JSON only, so Inbound Routes posts from forward() or store(), which arrive as form data, will not be accepted.

Example Mailgun payload

This abbreviated example shows the shape of a bounce. The signing docs place a signature object next to event-data, and the payload docs list the event fields. Treat the values as illustrative.

{
  "signature": {
    "token": "e0b5477167110d68991efc6b9f89f0a11066af27834600e123",
    "timestamp": "1770920772",
    "signature": "12d99f5a15355c180971bed7494d578b093c958f57766f3fe750761baed12345"
  },
  "event-data": {
    "event": "failed",
    "severity": "permanent",
    "reason": "bounce",
    "log-level": "error",
    "recipient": "badrecipient@sample.mailgun.com",
    "delivery-status": { "attempt-no": 1, "code": 550 },
    "tags": ["webhook_payload"]
  }
}

Mailgun warns that it may add fields over time and recommends against hardcoding the ones in its examples. Stored as records, new fields simply appear next to the old ones.

What to watch for

  • Bounce and complaint volume. A jump in permanent_fail or complained events after a campaign or a list import usually points to a list quality or sender reputation problem.
  • Gaps in delivered events. If sends continue but delivered records stop, either the webhook broke or Mailgun is not delivering. Compare record counts with your own send logs.
  • Delivery attempts on the Mailgun side. Mailgun treats a 200 as success and a 406 as a rejection it will not retry. Any other response is retried for 8 hours at 5, 10 and 15 minutes, then at 1, 2 and 4 hours. The docs say the delivery notification is excluded from that schedule.
  • Signing. Mailgun sends its signature inside the request body, in a JSON object named signature, not in a header.

If you also handle support or payments events, see the Zendesk and PayPal guides, or browse the full integrations list. Plans and allowances are on the pricing page.

Mailgun webhook facts

Signatures: Sazabi doesn’t verify Mailgun requests and doesn’t reject unsigned ones. Mailgun sends its signature inside the request body, not in a header, so none of Sazabi’s signature options fits it. The Webhook Events source accepts any JSON POST to your intake URL, so the intake URL is the only credential. It’s hard to guess, so keep it private, and rotate the project’s public key if it leaks.

  • Events: Mailgun supports accepted, delivered, temporary_fail, permanent_fail, opened, clicked, unsubscribed and complained events. (Mailgun docs, accessed Nov 8, 2026)
  • Payload: When something happens to your email, Mailgun calls your URL with an application/JSON payload. (Mailgun docs, accessed Nov 8, 2026)
  • Signing: Mailgun signs every POST with your account-wide Webhook Signing Key and sends token, timestamp and signature values in a JSON object named signature. (Mailgun docs, accessed Nov 8, 2026)
  • Retries: Mailgun retries a failed webhook over 8 hours with exponential backoff, except for 406 responses, which it treats as rejected. (Mailgun docs, accessed Nov 8, 2026)

Frequently Asked Questions

Does Mailgun send webhooks as JSON?

Yes for event webhooks. Mailgun calls your URL with an application/JSON payload when an event happens. Inbound Routes posts are different and use multipart or URL-encoded form bodies.

How many URLs can I register for one Mailgun event?

Up to 3 URLs per event type, at the domain level or the account level. If the same URL is set at both levels, Mailgun sends the event to it once.

Does Mailgun retry failed webhooks?

Yes. For any response other than 200 or 406, Mailgun retries for 8 hours at exponential backoff intervals, and the delivery notification is excluded from the schedule.

Does Sazabi verify Mailgun signatures?

No. Mailgun sends its signature inside the request body, not in a header, so none of Sazabi’s signature options fits it. Sazabi doesn’t reject unsigned requests, so keep the intake URL private: it’s the only credential.

Sources

  1. Event types, POST with a JSON payload, account-level and domain-level webhooks (accessed Nov 8, 2026)
  2. Up to 3 URLs per event type, US and EU regions are isolated, deduplication of the same URL across levels, trusted CA requirement for HTTPS (accessed Nov 8, 2026)
  3. Application/JSON payloads, example event fields, additive fields that can change (accessed Nov 8, 2026)
  4. Webhook Signing Key, signature object next to event-data, Routes posts use form encoding (accessed Nov 8, 2026)
  5. Response codes Mailgun reacts to and the 8 hour retry schedule (accessed Nov 8, 2026)
  6. Test webhook endpoint that sends dummy data to a URL (accessed Nov 8, 2026)
  7. Creating account-level webhooks through the API, up to 10 minutes for changes to take effect (accessed Nov 8, 2026)
  8. Webhooks are added under Sending and then Webhooks in the Mailgun control panel (accessed Nov 8, 2026)
  9. Webhook Events log source: JSON only, payload under webhook.* attributes, path suffix stored as webhook.path, arrays become one event per element; signature verification with the Standard Webhooks and Custom HMAC options, and rejected deliveries (accessed Nov 8, 2026)