Webhook
is a HTTP web callback API, where the app will get notifications regarding some actions. As we are speaking SendGrid & Mailgun, webhook requests inform a web app of the email delivery status or some other actions (eg. email delivered, email opened, link opened, etc.)
Before using the webhooks, we need some unique identification specific for each sending email, like ID. in Laravel the sample code might look like:
$message_id = '';
\Illuminate\Support\Facades\Mail::send([], [], function (\Illuminate\Mail\Message $message) use (&$message_id) {
$message
->to('test@inbox.mailtrap.io')
->from('test@yourdomain', 'Test')
->subject('Test subject')
->setBody('Some html message', 'text/html')
;
$message_id = $message->getId();
});
dd($message_id);
# sample output:
eedf5729ca72367c68e05e635700cf74@yourdomain
When sending the email via Mailgun transport driver, webhook API for any event will receive a payload, which include this $message_id
in the next format:
{
"signature": {
"token": "...",
...
},
"event-data": {
...
"message": {
"headers": {
"message-id": "eedf5729ca72367c68e05e635700cf74@yourdomain"
}
},
...
"event": "opened"
}
}
This message-id
will be provided on any webhook request, despite what kind of event will be fired – email delivery, delivery failure, email opened, etc.
In case of SendGrid transport driver, the webhook callback data for events processed
and delivered
might look like:
{
"email": "test@inbox.mailtrap.io",
"event": "delivered",
"ip": "...",
...
"sg_event_id": "ZGVsaXZjiuVkLTAtMjI3JUY2NzktQTRSdWoyb25ROWkwIUtsenFEbGRMQS0w",
"sg_message_id": "uj273onQ9igtSMzqDldLA.filterdrecv-7489454555-bkwvj-1-611FEB34-1.0",
"smtp-id": "<eedf5729ca72367c68e05e635700cf74@yourdomain>",
...
}
As you can see, the message_id
in SendGrid payload is available under smtp-id
key.
But, unfortunately, this smtp-id
is not available for other events like: open
(mail opened), click
(when the link clicked in email).
Fortunately, there is a solution – we can set custom headers:
\Illuminate\Support\Facades\Mail::send([], [], function (\Illuminate\Mail\Message $message) use (&$message_id) {
$message->getHeaders()->addTextHeader('X-SMTPAPI',
json_encode([
'unique_args' => ['my-message-id' => $message->getId()],
])
);
});
@source https://docs.sendgrid.com/for-developers/sending-email/building-an-x-smtpapi-header
In this case, any webhook callback request payload will include custom arguments in a way of:
{
"email": "test@inbox.mailtrap.io",
"event": "opened",
"ip": "...",
"my-message-id": "<eedf5729ca72367c68e05e635700cf74@yourdomain>",
...
"sg_event_id": "ZGVsaXZjiuVkLTAtMjI3JUY2NzktQTRSdWoyb25ROWkwIUtsenFEbGRMQS0w",
"sg_message_id": "uj273onQ9igtSMzqDldLA.filterdrecv-7489454555-bkwvj-1-611FEB34-1.0",
...
}
In the same way we can set custom headers for Mailgun driver too, like so:
\Illuminate\Support\Facades\Mail::send([], [], function (\Illuminate\Mail\Message $message) use (&$message_id) {
$message->getHeaders()->addTextHeader('X-MAILGUN-VARIABLES',
json_encode(['my-message-id' => $message->getId()])
);
});
In this case, the callback payload will look like:
{
...
"event-data": {
...
"user-variables": {
"my-message-id": "<eedf5729ca72367c68e05e635700cf74@yourdomain>"
},
...
}
For testing the webhook requests and checking the payload data structure, SendGrid recommends a very handy online tool: https://webhook.site/
After all above, its now your turn to handle webhook requests in your controller, as the sent email identification will be presented for sure.
@sources:
https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook
https://docs.sendgrid.com/for-developers/sending-email/building-an-x-smtpapi-header
https://documentation.mailgun.com/en/latest/user_manual.html?highlight=X-MAILGUN-VARIABLES#webhooks