symfony/sendgrid-mailer
Symfony Mailer bridge for SendGrid. Configure SMTP or API transport via DSN, choose region, handle event webhooks with optional signature validation, set suppression group headers, and schedule sends (API) using a Send-At date header.
Install the package:
composer require symfony/sendgrid-mailer
Configure .env with your SendGrid API key and region:
# SMTP Transport (recommended for most use cases)
MAILER_DSN=sendgrid+smtp://api_key@default?region=us
# OR API Transport (for advanced features like scheduling)
MAILER_DSN=sendgrid+api://api_key@default?region=us
api_key with your SendGrid API key (from SendGrid Dashboard).us with your SendGrid region (e.g., eu, ap1, or global).First email send:
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
public function sendWelcomeEmail(MailerInterface $mailer, string $to)
{
$email = (new Email())
->from('noreply@example.com')
->to($to)
->subject('Welcome!')
->text('Hello! Thanks for signing up.');
$mailer->send($email);
}
MailerInterface via Laravel’s service container (automatically registered by Symfony Mailer).Replace Laravel’s default Mail facade with Symfony’s MailerInterface:
// In a Laravel controller/service
public function __construct(
private MailerInterface $mailer
) {}
public function sendPasswordReset(string $email, string $token)
{
$email = (new Email())
->from(config('mail.from.address'))
->to($email)
->subject('Reset Your Password')
->html(view('emails.password_reset', ['token' => $token]));
$this->mailer->send($email);
}
| Use Case | Transport | Example DSN | Notes |
|---|---|---|---|
| High-volume transactional | sendgrid+smtp |
sendgrid+smtp://key@default?region=us |
Lower latency, simpler setup. |
| Scheduled emails | sendgrid+api |
sendgrid+api://key@default?region=us |
Supports Send-At header for delays. |
| Advanced analytics/webhooks | Either | Same as above | API transport exposes more SendGrid features. |
Pattern: Use SMTP for 90% of cases. Switch to API only if you need:
Send-At header).Workflow:
config/packages/framework.yaml:
framework:
webhook:
routing:
sendgrid:
service: mailer.webhook.request_parser.sendgrid
secret: '%env(SENDGRID_WEBHOOK_SECRET)%' # Optional: Validate signatures
#[AsRemoteEventConsumer(name: 'sendgrid')]
class SendGridDeliveryConsumer implements ConsumerInterface
{
public function consume(RemoteEvent|MailerDeliveryEvent $event): void
{
if ($event instanceof MailerDeliveryEvent) {
$this->logDeliveryEvent($event);
$this->triggerUserNotification($event);
}
}
private function logDeliveryEvent(MailerDeliveryEvent $event): void
{
// Log to DB or analytics service
\Log::info('Email delivered', [
'status' => $event->getStatus(),
'message_id' => $event->getMessageId(),
]);
}
}
config/services.yaml:
services:
App\Event\SendGridDeliveryConsumer:
tags: ['messenger.consumer']
Pattern: Use webhooks to:
Use Case: Allow users to opt out of specific email categories (e.g., marketing vs. transactional).
use Symfony\Component\Mailer\Bridge\Sendgrid\Header\SuppressionGroupHeader;
$email = (new Email())
->from('noreply@example.com')
->to('user@example.com')
->subject('Monthly Newsletter')
->text('Your monthly updates...')
->getHeaders()
->add(new SuppressionGroupHeader('marketing_newsletter', ['marketing']));
GROUP_ID: Your SendGrid suppression group ID (e.g., marketing_newsletter).GROUPS_TO_DISPLAY: Array of group IDs shown to users in their preferences (e.g., ['marketing', 'promotions']).Pattern: Create suppression groups in SendGrid UI and map them to Laravel user preferences:
// In a User model
public function toggleEmailPreference(string $groupId, bool $enabled)
{
$this->email_preferences[$groupId] = $enabled;
$this->save();
// Update SendGrid suppression list via API (optional)
SendGrid::suppression()->update($this->id, $groupId, !$enabled);
}
Send-AtUse Case: Delay emails (e.g., "Send this reminder in 2 hours").
$email = (new Email())
->from('noreply@example.com')
->to('user@example.com')
->subject('Your Reminder')
->text('Don’t forget!')
->getHeaders()
->addDateHeader('Send-At', new \DateTimeImmutable('+2 hours'));
Requirements:
sendgrid+api transport.Send-At must be a DateTimeImmutable object (not a string).Pattern: Combine with Laravel’s job queue for reliability:
// Dispatch a delayed job
SendGridEmailJob::dispatch($email)->delay(now()->addHours(2));
Use Case: Reuse SendGrid templates with dynamic content.
$email = (new Email())
->from('noreply@example.com')
->to('user@example.com')
->subject('Your Order Confirmation')
->html(view('emails.order_confirmation', ['order' => $order]))
->getHeaders()
->addTextHeader('X-SMTPAPI', json_encode([
'template_id' => '1234', // Your SendGrid template ID
'dynamic_template_data' => [
'order_id' => $order->id,
'items' => $order->items,
],
]));
Pattern:
X-SMTPAPI header to inject dynamic data.config/sendgrid.php).Issue: Emails fail silently with no errors. Fix: Verify the DSN format:
# Correct (SMTP)
MAILER_DSN=sendgrid+smtp://api_key:password@default?region=us
# Correct (API)
MAILER_DSN=sendgrid+api://api_key@default?region=us
region parameter defaults to us, but may cause routing delays for EU/APAC users.global region for multi-region redundancy (requires SendGrid Business plan).Debugging: Enable Symfony Mailer debug mode:
$mailer = new Mailer(new TransportFactory(), [
'debug' => true, // Logs raw SendGrid API responses
]);
secret in framework.webhook.routing.sendgrid:
secret: '%env(SENDGRID_WEBHOOK_SECRET)%' # Must match SendGrid webhook settings
How can I help you explore Laravel packages today?