Send emails asynchronously with Symfony Messenger, full PHP 8.3 strict types, webhook support, and zero-bloat architecture. Built for professionals and beginners alike.
Built from scratch in 2026 to use modern PHP and Symfony features properly.
Send emails without slowing down your HTTP responses. Symfony Messenger queues them in Redis or any transport for background processing.
Webhook signature verification via HMAC-SHA256. Timing-safe comparison. No credentials ever logged. Secrets via environment variables only.
Unit tests with MockHttpClient β no real API calls needed. PHPUnit 11, PHPStan Level 9. CI runs on every push with GitHub Actions.
Chain method calls to build emails naturally. Immutable DTOs prevent bugs. Type-safe from creation to delivery.
Receive real-time delivery events from Mailjet (bounces, opens, clicks, spam complaints) and react with Symfony Events.
Validate emails without actually sending them. Enable automatically in dev environments. Zero risk of spamming during development.
Send up to 50 emails in a single API call. Dramatically reduces API overhead for bulk operations like newsletters or notifications.
Attach files from disk or from memory. Automatic Base64 encoding. File size validation. Support for inline images in HTML emails.
Every file is heavily commented. Code explains itself. Perfect for students learning PHP, Symfony, and professional software practices.
From basic transactional emails to async batch sending β see how it works.
// In your Symfony Controller or Service use Fabconejo\MailjetBundle\Contract\EmailSenderInterface; use Fabconejo\MailjetBundle\DTO\EmailMessage; class RegistrationController { public function __construct( // Symfony injects this automatically β no new() needed! private readonly EmailSenderInterface $emailSender ) {} public function register(): Response { $email = EmailMessage::create() ->from('noreply@myapp.com', 'My App') ->to('newuser@example.com', 'New User') ->subject('Welcome to My App! π') ->htmlBody('<h1>Hello!</h1><p>Thanks for joining.</p>') ->textBody('Hello! Thanks for joining.'); // Sends immediately β user receives email in seconds $this->emailSender->sendNow($email); return new Response('Registration successful!'); } }
// Async: returns instantly, email sent in background // Your HTTP response is fast β email queued for worker! $email = EmailMessage::create() ->from('newsletter@myapp.com', 'My Newsletter') ->to('subscriber@example.com', 'John') ->subject('This week in My App') ->htmlBody($html); // β‘ This returns in <1ms β email queued in Redis/DB $this->emailSender->sendAsync($email); // Start the background worker (run in a terminal): // $ php bin/console messenger:consume async --time-limit=3600 // Or in production with Supervisor (keeps the worker running): // [program:messenger] // command=php /var/www/html/bin/console messenger:consume async
// Attach a PDF invoice to the email $email = EmailMessage::create() ->from('billing@myapp.com', 'Billing') ->to('customer@example.com') ->subject('Your Invoice #2026-042') ->htmlBody('<p>Please find your invoice attached.</p>') // Attach from a file path (most common) ->attachFile('/var/invoices/invoice-042.pdf') // Attach a file with a custom display name ->attachFile('/tmp/report.xlsx', 'Monthly Report.xlsx') // Attach raw binary content (e.g. generated in memory) ->attach('receipt.pdf', $pdfBinaryContent, 'application/pdf'); $this->emailSender->sendNow($email); // β Files are automatically Base64-encoded for the API
// Inject the low-level client for batch sending use Fabconejo\MailjetBundle\Contract\MailjetClientInterface; class NewsletterService { public function __construct( private readonly MailjetClientInterface $mailjet ) {} public function sendNewsletter(array $subscribers): void { // Build one email per subscriber $messages = []; foreach ($subscribers as $subscriber) { $messages[] = EmailMessage::create() ->from('newsletter@app.com') ->to($subscriber['email'], $subscriber['name']) ->subject('Weekly Newsletter') ->htmlBody($html); } // Send up to 50 per call β dramatically more efficient! foreach (array_chunk($messages, 50) as $batch) { $this->mailjet->sendBatch($batch); } } }
No complicated setup. Just install, configure, and send.
Requires PHP 8.3+ and Symfony 7+. Symfony Flex will auto-register the bundle.
composer require fabconejo/symfony-mailjet-bundle
.envGet your API keys at app.mailjet.com/account/apikeys (free account required)
MAILJET_API_KEY=your_api_key_here
MAILJET_SECRET_KEY=your_secret_key_here
MAILJET_SANDBOX_MODE=true # Set to false in production!
MAILJET_WEBHOOK_SECRET= # Optional: random string for webhook security
config/packages/mailjet.yamlmailjet:
api_key: '%env(MAILJET_API_KEY)%'
secret_key: '%env(MAILJET_SECRET_KEY)%'
sandbox_mode: '%env(bool:MAILJET_SANDBOX_MODE)%'
The EmailSenderInterface is now available anywhere in your Symfony app via dependency injection.
// In any controller or service:
public function __construct(
private readonly EmailSenderInterface $emailSender
) {}
// Send!
$this->emailSender->sendNow(
EmailMessage::create()
->from('you@example.com', 'Your App')
->to('user@example.com')
->subject('Hello!')
->htmlBody('<h1>It works!</h1>')
);