moukail/mailgun-mail-status-bundle
Symfony bundle for tracking Mailgun email delivery statuses. Integrates Mailgun events/webhooks to monitor delivered, opened, clicked, bounced, and failed messages, helping applications manage transactional email feedback and status updates.
This is a Symfony bundle, not a Laravel package. That is the single most important thing to establish before anything else — a Laravel developer cannot composer require this and expect it to boot in app/Providers. There is no auto-discovery, no service provider, no publishable config. Everything is wired through Symfony's config/bundles.php, config/services.yaml, and the DependencyInjection compiler pass. Before committing to it, open composer.json and confirm the symfony/* constraints (likely ^4.0|^5.0 given the December 2021 release), then check whether your project is Symfony at all. If you are in a Laravel codebase, read the source as a reference implementation for Mailgun webhook handling rather than as a dependency.
For the Symfony context it was built for, the minimal path is:
composer require moukail/mailgun-mail-status-bundleconfig/bundles.php:
Moukail\MailgunMailStatusBundle\MailgunMailStatusBundle::class => ['all' => true],
config/packages/mailgun_mail_status.yaml) — expect to supply at minimum your Mailgun API key and/or the HTTP webhook signing key. Both live in the Mailgun dashboard under Account → API Keys / Webhooks.The first useful use case is the obvious one: stop polling Mailgun and start receiving delivery events. You get delivered, opened, clicked, bounced, dropped, spam_complaint, unsubscribed pushed to you, and the bundle gives you an event layer on top so you can react (mark a DB row, dispatch a Laravel/Symfony job, suppress a contact).
The bundle's value is the webhook → event → listener pipeline. The pattern you will actually write day-to-day is a listener or subscriber on the events it dispatches (namespaced around Mailgun status changes, typically one event per event-type or a single event carrying the event-data payload). Confirm the exact event class names by reading Event/ and EventDispatcher/Controller/ in the repo — with zero stars and no docs beyond the README, the source is the documentation.
Realistic integration patterns:
event-data JSON alongside normalized columns (message_id, recipient, event, timestamp). Webhook replays happen; dedupe on Mailgun's id / signature timestamp so retries don't produce duplicate state transitions.status = bounced blindly — use "last event by timestamp wins" or a monotonic ranking (queued < accepted < delivered < opened < clicked).bounced (permanent) and spam_complaint should feed a suppression table immediately; temporary_fail should not. This is the highest-value pattern and the one the bundle's event model is designed to enable.If you are adapting this into Laravel (the likely intent for a Laravel developer evaluating it), do not attempt to boot the bundle. Instead:
mailgun/mailgun-php for sending, and write a POST /webhooks/mailgun route.HMAC-SHA256(timestamp + token, signing_key) and compare to the signature field, plus a timestamp skew check (~15 min). This is the part the bundle saves you, and it is ~20 lines.MailgunWebhookReceived, or typed events) and queue listeners. Mirror the bundle's event granularity so any future migration is mechanical.The bundle is also worth borrowing wholesale for its route + controller wiring if you are on Symfony: copy the controller shape rather than re-inventing content negotiation, signature rejection (401), and the 200-always-or-Mailgun-will-retry semantics.
timestamp, token, and signature at the top level of every webhook. If the bundle's config lets you disable verification for local dev, make absolutely sure it is re-enabled in production — an unverified webhook endpoint is an open door for anyone to mark arbitrary addresses as delivered or bounce them.2xx fast, process async.mailgun_mail_status.api_key vs mailgun_mail_status.mailgun.key) has to be read off the Configuration.php (TreeBuilder) or the DependencyInjection/Configuration node. Don't guess from the README alone; it may be stale relative to source.kernel.event_subscriber), and check bin/console debug:event-dispatcher for the Mailgun event name to see registered listeners.services.yaml. There is unlikely to be a clean strategy interface for swapping the storage layer — expect to write your own persistence in a listener rather than relying on whatever (if anything) the bundle ships.debug:event-dispatcher and the profiler, you can triangulate quickly.mailgun/mailgun-php and a queued listener. You get the same capability, supported, and without a 2021 Symfony dependency you cannot upgrade.How can I help you explore Laravel packages today?