jeffersongoncalves/laravel-resend
A Laravel integration for Resend, the transactional email API. Send mail through Resend using Laravel’s mailer, configure your API key, and access a simple service wrapper for Resend endpoints. A lightweight way to add Resend email delivery to Laravel apps.
This is the initial 1.0.0 release, so treat it as the first stable baseline rather than an upgrade — there is no migration path to worry about, but equally no battle-tested history to lean on. Pin it explicitly ("jeffersongoncalves/laravel-resend": "^1.0") and read the changelog link in the release notes alongside the README before adopting.
Where to look first, in order:
config/resend.php after publishing — this is where the API key, default from address, and any transport/sandbox settings live. Almost every "why isn't it working" question resolves here.register()/boot() — confirms whether the package registers a mail transport (so Mail::to(...)->send(...) routes through Resend) and/or binds a facade for the raw Resend API.composer.json requirements — check the Laravel and PHP version constraints before assuming it drops into an older app.Typical first use case — swap the mailer, keep your existing Mailables:
MAIL_MAILER=resend
RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
[email protected]
MAIL_FROM_NAME="Your App"
php artisan vendor:publish --tag=resend-config
php artisan config:clear # config caching will bite you otherwise
Mail::to($user)->send(new WelcomeEmail($user));
If that works, the transport is wired. Then verify the raw-API path (e.g. sending a non-Mailable payload or listing domains) so you know both surfaces are live.
Transport-first (recommended default). Keep using Laravel's Mail facade and Mailables. You inherit queues, ShouldQueue, markdown templates, attachments, and events (MessageSending/MessageSent) with zero changes. Resend becomes an infrastructure detail. This is the pattern to reach for 90% of the time.
Per-mailer configuration. Rather than overriding the global mailer, define a dedicated mailer in config/mail.php so you can run Resend alongside SMTP or SES:
'mailers' => [
'resend' => [
'transport' => 'resend',
],
// ...
],
Then opt in per-send: Mail::mailer('resend')->to($user)->send(...). Useful for staged rollouts or sending transactional mail through Resend while bulk mail stays elsewhere.
Facade / client for resource operations. Use the package facade for things Laravel's mail layer has no concept of — creating domains, verifying DNS, managing audiences and contacts, retrieving delivery status. Wrap these in your own small service class rather than calling the facade from controllers; it keeps the vendor surface isolated and makes swapping providers a one-file change.
Queue everything. Resend is an HTTP API, so a slow or failing call is a network round-trip inside your request. Implement ShouldQueue on Mailables, or dispatch via a job. Pair with ->afterCommit() on the send if you're sending from inside a DB transaction — you don't want a confirmation email for a record that gets rolled back.
Testing integration. For unit tests, Mail::fake() works unchanged — assert on Mail::assertSent(WelcomeEmail::class) and never hit the API. Only the facade/resource calls need stubbing (typically Http::fake() against Resend's host, or a bound fake of the client interface if the package exposes one). Keep a .env.testing with a dummy RESEND_API_KEY so config validation doesn't throw at boot.
Webhooks (if present in this release). Register the route behind api middleware without CSRF, verify the signing secret from the Resend dashboard, and be idempotent — delivery events repeat.
config/resend.php or MAIL_MAILER in production, always php artisan config:clear (or re-run config:cache). This is the single most common cause of "it still uses the old key.".env key naming. This package will use either RESEND_API_KEY or a package-prefixed key like RESEND_RESEND_API_KEY. Check the published config file's env() calls rather than assuming — a silently-null key produces authentication errors only at send time, not at boot.[email protected] is the sandbox address and only delivers to your own account email. If you get 403s locally but not in prod, check which domain is set in MAIL_FROM_ADDRESS.1.0.x patch releases changing behaviour around the transport registration or config keys. Lock the version in composer.json and read the diff before bumping minor versions.failed() method or a global Queue::failing handler logs the response body, not just the exception message. You will otherwise chase generic "failed to send" errors.tries/backoff settings, which can amplify the problem.MAIL_MAILER=resend is set globally but you have mailables built against SendGrid-specific headers or tags, they may be dropped or rejected. Audit any custom withSymfonyMessage() calls when migrating.TestCase::setUp().audiences/contacts mirroring is not a normal pattern — if migrations exist, inspect them before running migrate on a production database).How can I help you explore Laravel packages today?