stephenfarrell/postmark-bundle
A simple integration for sending transactional email through Postmark in PHP applications, with configurable service wiring for Postmark’s API. Useful for adding reliable email delivery to Symfony or Laravel-style projects with minimal setup.
The first thing to understand: this is a Symfony 3 bundle, and Laravel does not load Symfony bundles. There is no config/bundles.php, no Extension registration, and no services.yaml auto-wiring in a Laravel app. You cannot composer require stephenfarrell/postmark-bundle and expect it to "just work."
So "getting started" means deciding why you looked at this package and picking the right layer:
MAIL_MAILER=smtp, MAIL_HOST=smtp.postmarkapp.com, username = server token, password = server token). For the HTTP API (templates, batch, inbound, message streams), use wildbit/postmark-php directly.composer.json first. It pins an older Postmark HTTP client and Symfony 3 DI components. Note the exact wildbit/postmark-php (or equivalent) version — that's the only piece you'll actually reuse..env.A minimal Laravel-native replacement for what the bundle does:
composer require wildbit/postmark-php
// config/services.php
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
'message_stream' => env('POSTMARK_MESSAGE_STREAM', 'outbound'),
],
Bind a singleton in AppServiceProvider::register():
$this->app->singleton(\Postmark\PostmarkClient::class, fn () => new \Postmark\PostmarkClient(
config('services.postmark.token')
));
First send:
$client = app(\Postmark\PostmarkClient::class);
$client->sendEmail(
from: '[email protected]',
to: $user->email,
subject: 'Welcome',
htmlBody: view('emails.welcome', compact('user'))->render(),
messageStream: config('services.postmark.message_stream'),
);
That is functionally what this bundle gives a Symfony 3 app — nothing more.
Once you accept the bundle itself is inert in Laravel, the useful patterns are the ones it intended to provide, reimplemented as Laravel idioms.
Service provider + singleton. The bundle registers a postmark DI service keyed off config; do the same via AppServiceProvider. Inject PostmarkClient anywhere (controllers, jobs, notifications) rather than resolving from the container inline.
Facade optional. If your team prefers Postmark::sendEmail(...), add a thin facade over the singleton. Keep the real client injectable underneath so it stays testable.
Queued sending. Postmark is an external HTTP call — never send inline in a request path. Wrap sends in a ShouldQueue job or use Laravel's Mail::to()->queue() with a Postmark-backed transport. This is the single biggest day-to-day win:
Mail::to($user)->queue(new WelcomeMail($user));
Transactional vs. broadcast separation. The bundle's config exposes a server token; in practice you need two tokens (transactional stream vs. broadcast stream) plus a message stream name. Model this explicitly in config/services.php and pick the stream per mail class rather than hardcoding it.
Templates. Use Postmark's server-side templates for HTML the marketing team edits, and Laravel Blade for developer-owned mail. Route template sends through sendEmailWithTemplate() and Blade sends through sendEmail(). Don't mix them within one mail class.
Webhooks (bounces, opens, inbound). Postmark POSTs to your app. Register routes in routes/api.php, validate the Postmark signature header, and dispatch to jobs. The bundle has no meaningful webhook support — build this fresh.
Testing. Bind a fake PostmarkClient (or use Mail::fake()) in tests. Never hit the real API in CI.
composer audit. Do not install this into a production Laravel app; cherry-pick the ideas instead.ContainerInterface, Bundle classes, and compiler passes have no Laravel equivalent. Copying the bundle's service definitions will not work; rewrite them as AppServiceProvider bindings.config('services.*') conventions. Don't try to mirror them — adopt Laravel conventions so future developers can find the config.401 that looks like an auth bug but is actually a wrong-token-type bug. Log the Postmark ErrorCode and Message verbatim.MessageStream in API calls causes 422s. If you port any old send code, add the stream parameter./message-streams and /server API endpoints confirm your token and stream config.PostmarkClient binding (swap for a mock in tests), a Mail transport if you prefer the mailer abstraction, and queued jobs for retries. If you need custom headers or metadata, wildbit/postmark-php exposes them directly — no bundle wrapper needed.wildbit/postmark-php + a service provider.How can I help you explore Laravel packages today?