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

Stampie Bundle Laravel Package

stampie/stampie-bundle

Symfony bundle that integrates the Stampie email library, enabling transactional email delivery through multiple API-based mail providers. It provides framework-native configuration and services for quickly connecting your app to providers such as Mailgun, SendGrid, and Postmark.

View on GitHub
Deep Wiki
Context7

Getting Started

Stampie is an API-first mailer library: it talks to transactional email providers (SendGrid, Mailgun, Postmark, Mandrill, SparkPost) over HTTP instead of SMTP. The v2.1.1 release only touches stampie/stampie-bundle, which is a Symfony bundle — in a Laravel app you almost never install the bundle. Install the library instead:

composer require stampie/stampie

You also need an HTTP adapter dependency. Stampie ships with a Guzzle adapter, so:

composer require guzzlehttp/guzzle

First working use case — send one message from a queued job or a service class:

use Stampie\Adapter\Guzzle;
use Stampie\Mailer\Postmark;
use Stampie\Message;

$mailer = new Postmark(new Guzzle(new \GuzzleHttp\Client()), config('services.postmark.token'));

$message = new Message();
$message->setFrom('[email protected]', 'Example');
$message->setTo('[email protected]');
$message->setSubject('Welcome');
$message->setText('Plain text body');
$message->setHtml('<p>HTML body</p>');

$ok = $mailer->send($message); // bool; failures throw, they don't return false silently

Where to look first:

  1. Stampie\Mailer\* — pick the class matching your provider; each has a getEndpoint() constant you can override for EU/sandbox regions.
  2. Stampie\MessageInterface / Stampie\Message — the payload object. Subclass or implement the interface if you need custom headers or per-provider fields.
  3. Stampie\Adapter\* — the transport. Guzzle adapter is the default; swap it for a fake adapter in tests.
  4. Stampie\Exception\* — HttpException (transport-level) vs ApiException (provider rejected the payload, with the decoded error body).

If you are on an older Laravel (5.8–6.x) that pins Symfony 4 components, v2.1.1 is the release that widens the bundle's Symfony constraint to include 5.x — but again, only relevant if you actually pull in the bundle.

Implementation Patterns

Bind the mailer in a service provider, not inline. Resolve it from the container so tests can swap it:

// AppServiceProvider::register()
$this->app->singleton(\Stampie\Mailer::class, function ($app) {
    $adapter = new \Stampie\Adapter\Guzzle(new \GuzzleHttp\Client([
        'timeout' => 10,
        'connect_timeout' => 5,
    ]));

    return new \Stampie\Mailer\SendGrid($adapter, config('services.sendgrid.token'));
});

Then type-hint Stampie\Mailer $mailer in jobs, listeners, and controllers. Because Mailer is an abstract base, the container binding lets you flip providers per environment (Mailgun in staging, Postmark in production) without touching call sites.

Wrap it in a Laravel Mailable-shaped class. Stampie's Message is a mutable value object with a different API than Laravel's Mailable; don't leak it into your domain code. Create an adapter:

class StampieTransport
{
    public function __construct(private \Stampie\Mailer $mailer) {}

    public function send(Mailable $mailable): void
    {
        $message = new \Stampie\Message();
        $message->setFrom(config('mail.from.address'), config('mail.from.name'));
        $message->setTo($mailable->to[0]['address']);
        $message->setSubject($mailable->subject);
        $message->setHtml($mailable->render());
        $message->setText(/* strip tags fallback */);

        $this->mailer->send($message);
    }
}

This keeps the eventual "go back to SMTP" migration cheap.

Always send from a queue job. API mailers are fast but not free-form-safe — a provider 429 or 500 should be retried, not surfaced to the request. Implement ShouldQueue, set $tries = 3 and $backoff = [10, 60, 300], and let Stampie\Exception\HttpException bubble so the queue marks it failed. Catch ApiException separately: a 422 (bad address, unverified sender) will never succeed on retry, so $this->fail($e) immediately rather than burning attempts.

Use tags/metadata for observability. Most Stampie mailer classes accept provider-specific headers via $message->addHeader(). SendGrid X-SMTPAPI, Mailgun o:tag, Postmark Tag — map these from a single addTag() helper on your wrapper so provider webhooks can be correlated back to a NotificationLog model.

Test with a fake adapter. Implement Stampie\Adapter\AdapterInterface (just send()), assert on the raw payload your mailer produced, and bind it in a testing service provider. This is faster and more deterministic than hitting a sandbox endpoint, and it lets you assert on the exact JSON shape per provider.

Multi-provider fallback. Because the mailer is just an object, you can build a small FailoverMailer implementing Stampie\Mailer that loops a list of inner mailers and re-throws only if all fail. Useful when one provider has a regional outage. Keep the list short — a two-provider fallback covers most incidents.

Gotchas and Tips

  • Don't install stampie/stampie-bundle in Laravel. v2.1.1 is exclusively a bundle release (Symfony 5 constraint fix). Installing it drags in Symfony bundle/framework expectations, registers nothing useful for Laravel, and its symfony/* constraints can conflict with Laravel's pinned components during composer update. Use stampie/stampie only.
  • send() throws, it does not return false on failure. Every call needs a try/catch or a queue. HttpException means the HTTP layer failed (timeout, DNS, 5xx); ApiException means the provider answered with an error — its getResponse() carries the decoded body, which is where the real reason lives (unverified sender, suppressed recipient, bad template variables).
  • Sender verification is per-provider and per-account. A message that works in Mailgun will 422 in Postmark if the domain isn't verified there. Keep mail.from.address aligned with each provider's verified domain, and expect this to bite when you switch providers in staging.
  • Endpoints default to US regions. Most mailers hardcode a US endpoint; EU/UK data-residency accounts need an override ($mailer->setEndpoint(...) or a subclass). Sandbox endpoints likewise. There is no Laravel config for this — put it in config/services.php yourself.
  • The Message object is mutable and reused state is a classic bug. Construct a fresh Message per send; don't cache one on a singleton and mutate setTo() between calls, or you'll leak recipients between jobs under a long-running worker (Octane, Horizon).
  • Attachments and provider-specific features vary. Stampie's core Message covers from/to/cc/bcc/subject/text/html/headers. Anything beyond that (attachments, templates, scheduled send, dynamic templates) needs either a custom MessageInterface implementation per provider or a drop to the provider SDK. Check the specific mailer class before promising a feature to a product owner.
  • Adapter timeouts are your problem, not Stampie's. Inject a Guzzle client with explicit timeout and connect_timeout; the default has no timeout, which is how you get a wedged queue worker.
  • Extension point: if you need retry/backoff, logging, or metrics across all sends, decorate the Mailer binding rather than editing call sites — implement Stampie\Mailer, wrap a real one, and swap the container binding. That's also the cleanest place to plug in Laravel's Log and Event facades.
  • Version pinning: stampie/stampie 2.x is stable and quiet; there is no Laravel-specific release cadence to track. If you need the Symfony bundle for a legacy Symfony service in the same monorepo, 2.1.1 is the minimum for Symfony 5 compat.
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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel