Search by

Framework-agnostic PHP SDK for the transactional email API.

Package info

github.com/pufferpost/sdk

pkg:composer/pufferpost/sdk

Statistics

Installs: 34

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.4 2026-09-18 11:41 UTC

This package is auto-updated.

Last update: 2026-09-18 11:44:48 UTC


README

Framework-agnostic PHP SDK for the transactional email API. MIT-licensed.

Full documentation: docs/ — sending, batch, messages, error handling, testing.

Install

composer require pufferpost/sdk

Send a templated email

use PufferPost\Sdk\Client;
use PufferPost\Sdk\Email;

$client = new Client('key_live_…');

$result = $client->send(Email::fromTemplateId(
    from: 'no-reply@acme.com',
    to: 'jane@example.com',
    templateId: 'tpl_…',
    data: ['name' => 'Jane'],
));

echo $result->id;      // msg_…
echo $result->status;  // accepted

Pass an idempotency key to make a retry safe:

$client->send($email, idempotencyKey: 'order-1234-welcome');

Send an inline email (no template)

Deliver a subject + HTML body as-is — the API renders nothing:

$client->send(Email::inline(
    from: 'no-reply@acme.com',
    to: 'jane@example.com',
    subject: 'Your receipt',
    html: '<h1>Thanks!</h1>',
));

Provide a template or inline subject + html, never both.

Attachments, cc/bcc, reply-to

Any send (templated or inline) can carry these via the Email constructor:

use PufferPost\Sdk\Attachment;

$client->send(new Email(
    from: 'no-reply@acme.com',
    to: 'jane@example.com',
    subject: 'Your receipt',
    html: '<h1>Thanks!</h1>',
    replyTo: 'support@acme.com',
    cc: ['ops@acme.com'],
    bcc: ['audit@acme.com'],
    attachments: [Attachment::fromContents('receipt.pdf', 'application/pdf', $pdfBytes)],
));

Send a batch

Up to 100 messages in one call; each is accepted or rejected independently:

$result = $client->sendBatch([$email1, $email2]);

foreach ($result->results as $item) {
    echo $item->accepted ? $item->id : $item->errorCode;
}

Retrieve a message

$message = $client->getMessage('msg_…');
echo $message->status; // delivered

Error handling

Every non-2xx response raises a typed exception carrying the stable error code, the HTTP status, and the request_id:

use PufferPost\Sdk\Exception\ApiException;
use PufferPost\Sdk\Exception\RateLimitException;
use PufferPost\Sdk\Exception\ValidationException;

try {
    $client->send($email);
} catch (RateLimitException $e) {
    // back off and retry
} catch (ValidationException $e) {
    echo $e->errorCode;   // e.g. recipient_suppressed
} catch (ApiException $e) {
    error_log($e->getMessage().' ('.$e->requestId.')');
}

Network failures (DNS/TLS/timeout) raise PufferPost\Sdk\Exception\TransportException.

Framework integration

Symfony and Laravel users get a drop-in Mailer transport so $mailer->send($email) flows through this API — see the companion mailer-symfony package.

Testing

Inject a Symfony\Component\HttpClient\MockHttpClient to test without network calls:

$client = new Client('key_test_…', 'https://pufferpost.com', $mockHttpClient);