New in Symfony 6.3: Webhook Integration with Mailer and Notifier

Contributed by
Fabien Potencier

in #48542.

In Symfony 6.3 we’ve introduced two new components called Webhook and RemoteEvent.
A webhook is a notification from one system (e.g. a payment processor) to another
system (e.g. your application) of some state change (e.g. some order was paid).

Many third-party mailing services provide webhook support to notify you about
the different events related to emails (sent, opened, bounced, etc.) Same for
notification services like SMS, which provide webhooks to notify events like
message sent, sending failed, etc.

Most webhooks use standard HTTP and JSON to send their information. However, they
are not standardized: security is provider-dependent and payload is free-form.
That’s why in Symfony 6.3, we’re standardizing the webhooks of the most common
mailer/notification services
so your application doesn’t have to deal with those
internal details.

The rest of the article shows an example focused on the Mailer integration, but
the same applies to the Notifier integration. Imagine that you need to log when
your emails „bounce“ (they haven’t reached their destination) and when people
unsubscribe from your emails.

If you use for example Mailgun, first you configure a webhook in that service
pointing to a URL in your site (e.g.
Then, you add the following configuration in your Symfony project:

service: ‚…‘

Finally, create the consumer of this webhook:

use SymfonyComponentRemoteEventAttributeAsRemoteEventConsumer;
// …

#[AsRemoteEventConsumer(name: ‚emails‘)]
class MailerEventConsumer implements ConsumerInterface
public function consume(Event $event): void
$email = $event->getRecipientEmail();

error_log(match ($event->getName()) {
MailerDeliveryEvent::BOUNCE => sprintf(‚Email to %s bounced (%s)‘, $email, $event->getReason()),
MailerEngagementEvent::UNSUBSCRIBE => sprintf(‚Unsubscribe from %s‘, $email),
default => sprintf(‚Receive unhandled email event %s‘, $event->getName()),

And that’s all. If you change your mailer provider (in this or another project)
you can reuse the exact same code for the consumer; you’ll only need to update the
configuration. This is possible because Symfony does the following:

It runs some „request parsers“ that check that the incoming payload is not
malformed, contains all the needed data, verifies the signatures, etc.
It runs some „payload converters“ so the payload of each service is mapped
into a standard payload format.

The key here is the standardization. Symfony maps the incoming payloads and
events into common structures that you can use in your application to abstract
from the provider details.

For example, no matter how each provider names their events. When using this
feature, you only have to deal with the following common event names:

namespace SymfonyComponentRemoteEventEventMailer;

final class MailerDeliveryEvent extends AbstractMailerEvent
public const RECEIVED = ‚received‘;
public const DROPPED = ‚dropped‘;
public const DELIVERED = ‚delivered‘;
public const DEFERRED = ‚deferred‘;
public const BOUNCE = ‚bounce‘;

final class MailerEngagementEvent extends AbstractMailerEvent
public const OPEN = ‚open‘;
public const CLICK = ‚click‘;
public const SPAM = ’spam‘;
public const UNSUBSCRIBE = ‚unsubscribe‘;

Symfony 6.3 provides out-of-the-box webhook support for Mailgun, Postmark and
Twilio. Now we need you, the Symfony community, to help us provide integration
for the rest of mailing/notification services. Also, consider talking with your
company about sponsoring Symfony components and sponsoring Symfony third-party integrations.

Sponsor the Symfony project.

Symfony Blog

Read More

Latest News


PHP 8.2.10 released!

PHP 8.1.23 released!

PHP 8.0.30 released!

Generated by Feedzy