Sending transactional emails with a third-party service on Symfony
How to combine the power of the Mailer component with the features offered by an email sending service provider.
Transactional emails are messages sent automatically by an application to inform users of certain events that concern them directly. This can be:
- an order confirmation email
- an e-mail with a magic link to log in
or password reset
- a welcome email after signing up for an application
These messages are important because they are an integral part of the user
experience. Not receiving them has direct consequences for your company:
blocked users, support requests, loss of reputation…
In this context, the notion of message delivery is crucial and there is a ready-made solution: use a third-party service specialized in email delivery.
We will see how to send your transactional emails through a third-party service thanks to the Mailer component of Symfony and how to anticipate a possible unavailability of this service.
That’s not all: the service provider has information on the status of the messages: have they been delivered? Have they been opened by the recipient? Why are some messages undeliverable? Let’s see how to feed this valuable information back to your application using the webhook principle.
Choosing a service provider
Sendinblue offers a free basic plan that allows 300 transactional emails per day and limits 100 emails per hour. This is perfect for a more modest deployment or to test the potential of the solution before considering a move to a paid offering.
Finally, Sendinblue is a French solution that complies with the European Data
Protection Regulation (GDPR).
The example below is designed for Symfony 6+ with autowire and autoconfig enabled, but the code can be adapted to work with earlier versions. Your emails must be sent via the Symfony Mailer component.
It is necessary to create a Sendinblue account. The sender address of your transactional emails (e.g. email@example.com) must exist and be previously declared on your Sendinblue account.
- Configure sending with Sendinblue
- Anticipate an unavailability of the sending service
- Receive the deliverability status of the emails sent (8 steps)
Configure sending with Sendinblue
This step does not require any development. You just need to install a connector with Sendinblue for the Symfony Mailer, and to fill an API key to allow sending emails.
Install the connector with Composer like this:
Next, we need to update the DSN of the mail sender. It is a string that represents the connection to a data source, in this case our email provider. The DSN is defined as an environment variable and is therefore found in the .env files of Symfony.
On your Sendinblue management interface, the API key will be shown in the
“Transactional” section. If you forgot your key, you can generate a new one by
going to your account settings, “SMTP & API” section. You can find more
explanations in the documentation.
It’s already over, your outgoing emails will now go through Sendinblue.
Anticipate an unavailability of the sending service
We will now take advantage of the Mailer component to increase your application’s resilience to unexpected events. To do this, we will define an alternative method to send emails in case the Sendinblue API is temporarily unreachable.
Once again, no additional development will be required, it will be enough to adapt Mailer’s DSN.
The failover system allows for high availability of the mailer functionality. The
Mailer will try to send again with the second routing service if the first one fails (you can also be extremely careful and define more backup services).
Other mechanisms are available natively in the Mailer, such as the possibility to use load balancing (or automatically distribute the mailings to several services).
Receive the deliverability status of the emails sent
Now that the sending of our e-mails is delegated to specialists, let’s take the
opportunity to functionally enrich your application.
It becomes possible to find out whether an email has been delivered, opened and even whether the recipient has clicked on one of the links in the email. This makes it possible to offer a function for confirmations of receipt. It is also possible to find out whether the e-mail could not be delivered. So it is conceivable, for example, to provide for the invalidation of a user account if the email address associated with it does not exist, or to block the further sending of emails from your application to that recipient if an error has occurred.
To do this, we will eventually write some PHP code.
1/ Create a Doctrine entity
First, we will create an entity to represent each email sent and store its unique
identifier. This entity will also contain the name of the last event related to the
message (delivered, opened, …).
The attributes created and updated, used respectively to store the date of sending of the message and the date of the last event about which your application was notified, are managed manually on a voluntary basis.
The isUpdateable method ensures that the device is updated only when legitimate: it handles cases with a small time difference between your application’s server and Sendinblues’ servers and also cases where the
event messages do not arrive in chronological order.
2/ Get a unique identifier for each email sent
Here is a sample code that allows you to automatically retrieve a unique identifier for each of your sent emails.
The important thing is to inject TransportInterface rather than MailerInterface, which is not able to return the unique identifier.
It may be preferable to provide a centralized service for all email deliveries, so that the logic for retrieving and storing unique identifiers is not duplicated in multiple places in your application code. Another alternative is to dispatch an event containing the SentMessage object returned by the Mailer.
3/ Create a controller to receive webhook calls from Sendinblue
Thanks to the webhook principle, Sendinblue will automatically call a URL in your application to provide you with information about each email sent. We therefore create a controller to handle these requests.
Sendinblue sends the tracked email information as a JSON object. However, our controller expects to directly receive our message entity that needs to be updated, as well as the name and date of the event we are notified about. Let’s see how we can solve this with a ParamConverter.
4/ Setting up a ParamConverter
The ParamConverter processes the raw JSON data in the request sent by Sendinblue and adds attributes to the Symfony Request object. This way, our controller -created in the previous step- will only receive the data it
5/ Restrict access to the route dedicated to the webhook
We now want to make sure that the access to this route will be public, but that only Sendinblue servers, or possibly you when you perform local tests, you will be able to access it.
We add an access rule in the Symfony security.yaml file.
The IP ranges 188.8.131.52/24 and 184.108.40.206/20 correspond to the Sendinblue servers. The addresses 127.0.0.1 and :1 correspond to the localhost for your tests.
6/ Testing the webhook locally
Open the console by pressing [F12], then copy and paste and adapt the following code for your test cases:
All possible response formats are detailed in this documentation.
7/ Activate the webhook on the Sendinblue platform
It’s almost done, now you have to activate the webhook.
In your Sendinblue management interface, go to the “Transactional” section and then to “Configuration”. You can choose, case by case, the events for which you want your request to be notified.
For more information on this configuration, please refer to this documentation.
8/ Bonus: translation file of the status of emails
Here is a file with translations of the different events for which you can be notified.
The final word
This little tutorial has allowed you to delegate the sending of your emails to a specialized service provider, and to take advantage of additional features such as the ability to confirm receipt of an email to enrich the
functionality of your application.
All that’s left is for you to focus on your core business!
Feel free to give me your feedback in the comments.
Documentation of the Mailer component of Symfony:
DSN for Data Source Name :
Webhooks or HTTP callback links:
Sendinblue documentation on webhook functionality:
Creating a custom ParamConverter:
The access control rules of Symfony: