Search by

symfony / puffer-post-mailer

Symfony PufferPost Mailer Bridge

Maintainers

Package info

github.com/symfony/puffer-post-mailer

Homepage

Type:symfony-mailer-bridge

pkg:composer/symfony/puffer-post-mailer

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

8.2.x-dev 2026-09-03 19:56 UTC

This package is auto-updated.

Last update: 2026-09-04 09:01:09 UTC


README

Provides PufferPost integration for Symfony Mailer.

Configuration example:

# API
MAILER_DSN=pufferpost+api://API_KEY@default

# same thing, shorter
MAILER_DSN=pufferpost://API_KEY@default

where API_KEY is your PufferPost API key.

PufferPost has no SMTP relay, so the bridge is API-only and pufferpost is an alias for pufferpost+api. Sends go over HTTPS and report the error message returned by the API on a failed send.

The transport carries what the send endpoint accepts: sender, recipients, subject, text and HTML bodies, a single reply-to address, attachments and custom X- headers. Inline (cid:) attachments are rejected, because the API has no Content-ID support and the reference would silently break.

The API addresses one recipient per message, so an email with several recipients is submitted as a batch: one request, one message per envelope recipient. Cc and Bcc are attached to the first message only, because each item is delivered as an independent email.

Templates

A template stored in PufferPost is rendered server side, through the component's RemoteTemplateEmail:

use Symfony\Component\Mailer\RemoteTemplateEmail;

$email = (new RemoteTemplateEmail())
    ->from('shop@example.com')
    ->to('jane@example.com')
    ->template('tpl_welcome', ['name' => 'Jane']);

Such an email carries no body of its own, and the subject belongs to the template, so setting one is refused rather than silently dropped.

Provider options

The remaining options are set as headers, so a plain Email carries them and $mailer->send() keeps working: X-PufferPost-Metadata (JSON), X-PufferPost-Unsubscribe-Group, X-PufferPost-Locale and X-PufferPost-Timezone. The transport reads them into their own payload fields and never sends them on as headers.

Only other X- headers are forwarded; the API allow-lists the headers map to those names. An email with several reply-to addresses keeps the first, since the others cannot be passed as a raw header.

A batch is one request, so if some messages are accepted and others refused the transport reports the failure while the accepted ones are already queued. A Messenger retry of that send would deliver those again; pufferpost/sdk sends per recipient with idempotency keys where that matters.

Resources