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 Mailjet Laravel Package

mailjet/laravel-mailjet

Official Mailjet integration for Laravel: use Mailjet as your mail transport driver and access the full Mailjet API (contacts, lists, campaigns, templates, stats) through a simple, expressive wrapper with Laravel-style configuration and facades.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require mailjet/laravel-mailjet   # 3.2.0
php artisan vendor:publish --tag=config    # -> config/mailjet.php

Set MAILJET_APIKEY / MAILJET_APISECRET in .env. Note these are the HTTP API key pair from Mailjet's dashboard, not the SMTP credentials — they are different values and mixing them up produces 401s that look like a config problem.

First use case: keep using Laravel's Mail API. The main value of this package day-to-day is that it registers a mailjet transport, so you don't rewrite your Mailables, Notifications, or Mail::to() calls. Add a mailer in config/mail.php:

'mailers' => [
    'mailjet' => ['transport' => 'mailjet'],
],

then set MAIL_MAILER=mailjet. Everything your app already sends now goes out through Mailjet, and Mail::fake() still works in tests.

Second use case: the facade for things Laravel Mail doesn't model — contact lists, template management, message stats, campaign-style sends. The Mailjet facade proxies to Mailjet\LaravelMailjet\Services\MailjetService, which wraps the underlying \Mailjet\Client.

Where to look first

  1. The README usage section — it was rewritten in 3.2.0 (PR #99), so read the current one rather than a blog post written against 2.x.
  2. MailjetServiceProvider — shows how the transport is registered and what is bound in the container.
  3. MailjetService — the actual surface area behind the facade.
  4. config/mailjet.php — confirm which keys the provider reads in your installed version before assuming services.mailjet is the source of truth.

Implementation Patterns

Route user-facing mail through Mailables, not the facade. Mailables/Notifications give you queueing, localization, and Mail::fake() for free. Reserve direct facade calls for contact sync, list management, and stats.

Wrap the facade behind your own contract. The facade resolves a concrete service; injecting it directly makes tests brittle. Define interface TransactionalMailer { public function send(array $payload): Result; }, implement it with MailjetService, and bind a fake in tests. This also lets you swap providers later without touching call sites.

Template-driven email. Keep Mailjet TemplateID and Variables out of your domain code — build them in a dedicated MailjetTemplate value object or mailable method, and validate the variable names against the template. Typo'd variables don't error; they render blank in production.

Contact list sync as a queued side effect. On registration (or a UserRegistered event), dispatch a job that creates/updates the contact and adds them to the right list. Make it idempotent on email address — Mailjet will happily create duplicates on retries.

Per-tenant credentials. Don't rely on the singleton config if you're multi-tenant. Either construct the service/client with explicit credentials per tenant, or set config and Mail::purge('mailjet') before use so the mailer doesn't keep stale credentials. Never let a long-lived queue worker cache one tenant's key.

Webhooks are yours to build. The package does not handle Mailjet's event callbacks (bounce, blocked, spam, open). Add a route, verify the payload, and persist events keyed by MessageID/MessageUUID so you can reconcile with your own mail_log table. This is where most of the real integration work lives.

Local/staging. Use log or array mailer in tests, and Mailjet's sandbox option when you want to validate a payload's shape without delivering. Don't burn reputation on test sends.

Config caching. php artisan config:cache is mandatory in deploy; env vars are only read through config files, so any runtime config([...]) juggling has to happen after boot, not in .env.

Gotchas and Tips

The SDK returns responses, it doesn't throw. MailjetService calls return a response object; a bad payload comes back as a 400 with a Messages array, not an exception. Check the status and log the body — otherwise failed sends look like successes. Same discipline applies to queued mailables: catch and log around Mail::send() if delivery failures matter to your domain.

Mailjet's ErrorMessage is frequently useless ("Missing mandatory property"). Log your full outgoing payload (redacting PII) alongside the response; you'll find the missing From/To/TemplateID in seconds instead of guessing.

Sender validation causes silent-ish failures. Unverified From addresses or domains (missing SPF/DKIM) get rejected at the API layer, not by Laravel. If mail "isn't arriving" but no exception fired, check sender validation in the Mailjet dashboard before touching code.

Attachments. Laravel attachments are translated by the transport, but Mailjet enforces a total message size (~15 MB). Large PDFs fail at the API, not at validation — pre-check size in a Mailable or move to a link.

Rate limits and retries. Mailjet throttles per-second and per-minute. Queue your sends and add RateLimited job middleware or a retryUntil() with backoff; synchronous loops over hundreds of recipients will get you 429s mid-batch.

Facade is not a contract. Every extension point worth using — swapping the transport via Mail::extend, subclassing the transport to inject X-MJ-* custom headers/variables, binding your own service implementation — is easier if you own an interface. Also useful: for transport-level bugs, replay the exact Mailable payload through Mailjet::send() directly to see the raw response.

Upgrading to 3.2.0. This is additive: Laravel 13 support plus widened Composer constraints, no breaking changes and no deprecations. Treat it as a patch bump, but do run your mail test suite — the transport leans on Illuminate\Mail\Transport and Symfony Mime internals that shift between framework majors, and the widened constraints mean Composer may now resolve combinations that weren't previously tested. If you're pinned below 3.1.3, read the refreshed docs first; the config key names and facade method surface have drifted across the 3.x line, and the reworked README is the authoritative source. Clear config:cache after upgrading.

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