Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Driver Laravel Package

mailersend/laravel-driver

Official MailerSend mail driver for Laravel. Send transactional email through MailerSend’s API using Laravel’s native Mail facade, with a drop-in transport that supports templates, attachments, tags, and webhooks. Simple configuration and reliable delivery for Laravel apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up in four steps.

composer require mailersend/laravel-driver
MAIL_MAILER=mailersend
MAILERSEND_API_KEY=mlsn.xxxxxxxxxxxxxxxx
[email protected]
MAIL_FROM_NAME="Your App"
// config/mail.php
'mailersend' => [
    'transport' => 'mailersend',
    'api_key'   => env('MAILERSEND_API_KEY'), // driver reads the key from the mailer config
],
php artisan config:clear   # or config:cache in deploy

The service provider is auto-discovered; the transport is registered under the mailersend key, so anything that resolves a mailer (Mail::, Mailable, Notification, MailMessage) can use it.

Where to look first

  1. The package README — the only doc that matters for the config array shape.
  2. src/MailerSendServiceProvider.php and src/MailerSendTransport.php — the transport is thin; read it once and you know exactly which Mailable features survive the conversion to an API payload (cc/bcc/reply-to/attachments/headers).
  3. The MailerSend dashboard's Activity log — the fastest way to distinguish "Laravel never sent it" from "MailerSend rejected it."
  4. MailerSend's API error reference — the driver surfaces API errors verbatim, so you'll be reading MSxxxxx codes.

First use case: smoke test through the real transport, not tinker.

Mail::mailer('mailersend')->raw('Test', function ($m) {
    $m->to('[email protected]')->subject('Driver smoke test');
});

Confirm it appears in Activity before building Mailables on top. If the from-address domain isn't verified (SPF/DKIM in MailerSend), you'll get a 4xx here and nowhere else — better to find that on line one than in a queued job at 3am.

Implementation Patterns

Default vs. explicit mailer. Set MAIL_MAILER=mailersend for the common path, then opt specific mailables/notifications in or out. Transactional (receipts, password resets) → mailersend. Internal notification noise → log or SMTP, so you don't burn API quota.

// Mailable
public function __construct(public Order $order) {}

public function build()
{
    return $this->mailer('mailersend')
        ->subject("Order #{$this->order->id}")
        ->markdown('emails.order');
}

// Notification — define a mailer property on the class
class InvoicePaid extends Notification
{
    public $mailer = 'mailersend';
}

Failover / routing. Laravel 9+ lets you stack mailers. Useful when you want SMTP as a safety net:

'default' => env('MAIL_MAILER', 'failover'),
'failover' => [
    'transport' => 'failover',
    'mailers'   => ['mailersend', 'smtp'],
],

Verify this actually catches API errors — see the Gotchas section, because failover keys off Symfony's TransportExceptionInterface and an SDK exception may not implement it.

Queue everything. The driver is a synchronous HTTP call to MailerSend; a slow API turns a 200ms request into a 2s one. Combine ShouldQueue with real retry semantics:

class SendReceipt extends Mailable implements ShouldQueue
{
    public $tries = 5;
    public $backoff = [10, 60, 300, 900];   // 429s and 5xx deserve patience
    public $maxExceptions = 3;
}

Attachments. attach(), attachFromStorage(), and attachData() all work — they're streamed/encoded at send time, so files are read inside the queue worker, not when the job is dispatched. Pass storage paths and IDs into the job, not raw file contents.

Multi-tenant / multi-domain. Two clean options:

// A) named mailers, one per sending domain/API key
'amazon' => ['transport' => 'mailersend', 'api_key' => env('MS_KEY_A')],
'apple'  => ['transport' => 'mailersend', 'api_key' => env('MS_KEY_B')],
// then ->mailer($tenant->mailer_name)

Option B — mutating config() at runtime — works but is a footgun under Octane and long-lived queue workers (see Gotchas). Prefer named mailers.

Local dev and tests. Keep MAIL_MAILER=log or Mailpit locally; point staging at MailerSend's trial domain (which only delivers to your own account email — do not try to demo to a client with it). In feature tests, Mail::fake() intercepts before the transport, so no API key or network is needed — assert on Mail::assertSent() as usual.

Beyond mail. Delivery events (delivered, opened, bounced, spam complaint) come via MailerSend webhooks, not this driver. Add a webhook route and verify the signature with the signing secret; store events against your message/recipient records. For templates, lists, inbound routing, or the bulk endpoint, use mailersend/mailersend directly alongside the driver — they coexist fine.

Gotchas and Tips

  • A 202 is success. MailerSend accepts the message asynchronously. Mail::send() returning without an exception means "queued at MailerSend," not "delivered." Don't build retry logic around the return value; build it around webhooks.
  • Failover may not save you. Laravel's failover transport only switches on TransportExceptionInterface. If the driver lets a MailerSend*Exception escape unwrapped, a hard API failure won't trigger failover. Test it deliberately (bad API key) before trusting it in production; otherwise catch and rethrow in your own job layer.
  • Domain verification is the #1 day-one blocker. Sender domain must be verified in MailerSend with DNS records, and the from address must match. Mismatched MAIL_FROM_ADDRESS between .env and MailerSend produces a 4xx that reads like an auth problem.
  • API key scope and caching. The key needs send permission. After changing env vars: php artisan config:clear locally, config:cache in deploy, and restart queue workers and Octane — long-running processes keep the old config in memory.
  • Rate limits are per-plan and real. Bursts produce 429s. Backoff on the job, and for genuine volume consider chunking with MailerSend's bulk API rather than Mail::queueing 10k individual sends.
  • Arbitrary headers get filtered. Setting X-Custom-Thing on a Mailable may be dropped or rejected by the API. Stick to what the transport maps (reply-to, cc/bcc, tags via the documented mechanism) and verify in Activity.
  • Attachment size ceiling. MailerSend caps total attachment size (base64 inflates it ~33%). Large PDFs will fail at the API, not in Laravel — check the current limit before shipping invoice PDFs.
  • Extension point: register your own transport after the package provider loads via Mail::extend('mailersend', fn ($config) => new YourTransport(...)) if you need a custom HTTP client, timeouts, or structured logging around the SDK. Useful for injecting request IDs into logs so MailerSend Activity entries are traceable back to a Laravel job.
  • Debugging order: (1) config('mail.mailers.mailersend'), (2) dashboard Activity for the recipient + timestamp, (3) API error code, (4) failed_jobs payload. Ninety percent of issues are resolved at step 2.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource