Mailgun Webhooks in Laravel
Mailgun Webhooks in Laravel
This guide explains how to receive Mailgun webhooks in Laravel, tolerate form-encoded and JSON payloads, enqueue processing, and forward normalized events to SendPromptly. You’ll get copy‑paste routes and controllers and a suggested idempotency approach.
The primary keyword mailgun webhooks laravel is used throughout the examples and test steps.
Mailgun webhook types you’ll actually use
Not all Mailgun events matter for every app — choose the set that matches your product needs.
Delivery-related events vs engagement
Delivery-related: delivered, bounced, dropped. Engagement: opened, clicked. Handle delivery events for billing/blacklist logic; handle engagement for analytics.
Endpoint strategy (one endpoint vs per-event endpoints)
You can use a single endpoint and dispatch on event or provide separate endpoints for higher isolation.
Micro checklist:
- Pick either single endpoint or per-event endpoints deliberately.
- Persist raw payload for auditing.
- Queue everything and return
204.
Configure Mailgun
Add webhook URLs in the Mailgun dashboard and use a dedicated signing key for verification.
Add webhook URLs in Mailgun dashboard
Point Mailgun webhooks to your HTTPS endpoint(s) and enable the events you intend to consume.
Use a dedicated “webhooks signing key” (don’t reuse secrets randomly)
Store the signing key separately from your API keys and rotate it regularly.
Micro checklist:
- Confirm webhook URLs in staging before switching production.
- Use the signing key shown in Mailgun dashboard, not the global API key.
Laravel endpoint
Accept both form-encoded and JSON payloads, record the raw body, and enqueue work.
Accept form-encoded or JSON payloads
Mailgun may send application/x-www-form-urlencoded; normalize to an array in your controller.
Store raw payload for debugging
Persist raw body to webhook_inbox to reproduce issues later.
ACK fast + queue work
Enqueue a job and return 204 immediately to stop Mailgun retries.
Required code snippets
A) Route
| |
B) Controller (payload-tolerant)
| |
Normalize + forward into SendPromptly
Normalize Mailgun fields into your canonical event schema and forward with an Idempotency-Key.
Canonical schema fields
Include provider, provider_event, occurred_at, recipient, message_id, and meta.
Idempotency key strategy (provider message id + event + timestamp)
Combine Mailgun message id, event type, and timestamp into a stable idempotency key for ingestion.
Common gotcha: Treating Mailgun webhooks as JSON-only will break form-encoded deliveries.
Test steps (curl + expected response)
| |
Expected: HTTP/1.1 204 No Content
Common failure modes
- Treating every webhook as JSON-only (but receiving form-encoded).
- Doing heavy work inline → timeouts → re-delivery.
- No dedupe → double counting opens/clicks.
- Not persisting raw payload → painful debugging.
- Shipping without signature verification (fix via the next guide).
Related
- /docs/guides/mailgun-webhook-signature-verification — Verify Mailgun webhook signatures
- /docs/guides/email-provider-event-webhooks — Normalize provider events into one stream
- /docs/event-ingestion/ — SendPromptly ingestion API
Prove your pipeline works: trigger one Mailgun-style event, forward it, and inspect it. Open Sample Project · Open Message Log
Conclusion
- Accept form-encoded and JSON payloads and normalize early.
- ACK fast and queue processing to avoid retries.
- Use provider message id + event + timestamp for idempotency.
- Persist raw payloads for reliable debugging.