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

Headoo Mailjet Bundle Laravel Package

headoo/headoo-mailjet-bundle

Integrates the Mailjet email service into PHP applications, providing a simple way to send transactional and marketing emails through Mailjet's API with straightforward configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

The framework mismatch comes first, before any code. Despite being filed under Laravel/PHP, headoo/headoo-mailjet-bundle is a Symfony bundle — type: symfony-bundle in composer.json, a DependencyInjection/*Extension.php, and Resources/config/services.yml. There is no ServiceProvider, no Facade, no config/*.php to publish. Composer will happily install it into a Laravel app, but php artisan package discovery will register nothing. You get classes, not wiring. It is also flagged Deprecated, has 3 stars, and 0 dependents — so nothing upstream will ever be fixed for you.

That means your first step is a decision, not an install: are you wrapping the bundle's client class by hand, or skipping the bundle entirely? For new work, skip it. For an inherited Symfony codebase you need to port behavior out of, here is the minimal evaluation path:

  1. composer show headoo/headoo-mailjet-bundle — read the require block. Note the PHP constraint and target Symfony major. If the php cap is below your runtime, stop.
  2. Open the exposed client/wrapper class and identify the Mailjet API version it targets. A deprecated bundle almost certainly points at the retired api.mailjet.com/v3 (now v3.1) send endpoint, or worse, the long-dead v1. This is the single biggest failure you will hit — confirm it before writing a line of application code.
  3. Note the config keys it expects (an API key and a secret). Map these to MAILJET_APIKEY / MAILJET_SECRET in your .env.
  4. Prove the transport with one plain-text send to your own address before touching templates, queues, or Mailable classes.

First real use case: a password-reset or welcome email — one recipient, one sender, an HTML body, no variables. If that round-trips, the client works. If it 400s, you've found the dead-endpoint problem for the cost of one request instead of one sprint.

Where to look in the repo: the bundle's service definition (Resources/config/services.yml) shows how the client is constructed — arguments order, defaults, and which keys are mandatory. That file is the documentation, since there usually is no working Laravel README.

Implementation Patterns

Skip the bundle's DI; wire the client yourself. Laravel will never run the bundle's extension, so treat it as a single library class. In config/services.php:

'mailjet' => [
    'key'     => env('MAILJET_APIKEY'),
    'secret'  => env('MAILJET_SECRET'),
    'sandbox' => env('MAILJET_SANDBOX', false),
],

Bind a singleton in a service provider, constructing the bundle's client from these values. That is the entire integration surface — one class, one binding.

Adapter pattern (the pattern that matters). Never let a bundle class leak into controllers or jobs. Define a narrow interface so a swap to mailjet/laravel-mailjet or the official mailjet/mailjet-apiv3-php SDK is a one-file change:

interface TransactionalMailer {
    public function send(string $to, string $subject, string $html, array $vars = []): void;
}

Implement MailjetMailer against the v3.1 payload shape:

['Messages' => [[
    'From'      => ['Email' => config('mail.from.address'), 'Name' => config('mail.from.name')],
    'To'        => [['Email' => $to]],
    'Subject'   => $subject,
    'HTMLPart'  => $html,
    'Variables' => $vars,
]]]

If the bundle's client only speaks an old endpoint, ignore its send method entirely and make the same HTTPS call the SDK makes — the thin-wrapper value of this package is essentially just auth and a configured HTTP client. Keep the implementation in app/Services/Mail/, not app/Http/.

Queue everything. Mailjet enforces per-second send limits and returns 429. Always dispatch sends through a queued job:

class SendTransactionalEmail implements ShouldQueue
{
    public int $tries = 3;
    public function backoff(): int { return 10; } // seconds
    // ...
}

A synchronous send inside a request will eventually stall a checkout or signup flow under load. This is true for any ESP client, but doubly so when you cannot rely on the library to do retry/backoff for you.

Templating. Two valid approaches: build HTMLPart in Blade and pass variables inline, or use Mailjet's hosted templates and send TemplateID + Variables, keeping your HTML out of the repo. For anything marketing-adjacent, hosted templates are usually better; for transactional, inline Blade gives you full test coverage in your own CI.

Integration tips:

  • Register a Mail transport if you want to keep using Laravel's Mail:: API — but at that point you're reimplementing mailjet/laravel-mailjet, so prefer that package.
  • Wrap the client in a health check that does a sandbox send on deploy; deprecated clients fail silently on endpoint drift.
  • Keep MAILJET_SANDBOX wired so staging never sends to real addresses.

Gotchas and Tips

  • It will not auto-register. The most common first-hour loss is expecting discover to wire it. It won't. There is no provider to publish; every binding is manual. If that surprises you, you've confirmed the framework mismatch — stop and use a Laravel-native option.

  • Dead endpoint is the silent killer. Old Mailjet v1/v3 paths return 400/404 rather than an obvious "upgrade required" message. Symptom: every send fails, logs show a generic HTTP error. Verify the URL the client hits (dump it or set a Guzzle middleware to log the request URI) before assuming auth is wrong.

  • 429 masquerades as an auth failure in naive clients. Distinguish 401 (bad key), 400 (bad payload/endpoint), and 429 (rate limit) in your error handling and log the response body — Mailjet returns a structured {"Messages":[{"Status":"error","Errors":[...]}]} that tells you exactly which field failed.

  • Config key names are bundle-specific. The Extension class defines the exact tree (api_key, secret, maybe sandbox) and constructor argument order. Don't guess — read the extension. A wrong argument order yields a confusing 401 rather than a constructor error.

  • No security patch path. This is deprecated with no maintainer. Pin the exact version in composer.lock, run composer audit in CI, and treat any advisory as unfixable-by-upstream — the mitigation is a migration ticket, not a bump.

  • Don't force-install with --ignore-platform-reqs. If the PHP constraint blocks install on your runtime, that's a real signal about untested code paths, not a hurdle to brute-force. Forcing the install converts a visible error into invisible runtime drift.

  • Extension points are thin — plan to own them. There is no event system, no retry hook, no pluggable transport. Bounce/complaint webhook handling and multi-provider failover are not in this bundle; if a roadmap item mentions deliverability, budget that work in your own mailer layer.

  • Removal risk. Before deleting it from an inherited codebase, grep for its service ID and any webhook route — an "unused" mail bundle sometimes has a listener registered elsewhere. Inventory usage, add an interface at the call sites, put an SDK-backed implementation alongside, cut over by config flag, parallel-run, then remove.

  • Debugging workflow: enable Guzzle request/response logging behind a config flag, confirm the endpoint and payload shape, and validate a single send against Mailjet's dashboard before touching templates. If the dashboard shows nothing, the request never left your app.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings