smtpfast / smtpfast-php
The official PHP SDK for the SMTPfast API.
Requires
- php: ^8.2
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5 || ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-22 20:16:45 UTC
README
The official PHP SDK for the SMTPfast API. It covers every endpoint in the API, with a PHPStan array shape for every request and response. It needs PHP 8.2+ with the curl extension and has no other dependencies.
composer require smtpfast/smtpfast-php
For Laravel, smtpfast/smtpfast-laravel adds a mail driver on top of this SDK.
Send an email
use SMTPfast\Client; $client = new Client(); // reads SMTPFAST_API_KEY $email = $client->emails->send([ 'from' => 'Acme <hello@yourdomain.com>', 'to' => ['customer@example.org'], 'subject' => 'Your receipt', 'html' => '<p>Thanks for your order.</p>', 'category' => 'transactional', ]); echo $email['id'];
Email is marketing unless you set category to transactional. Marketing email gets a one-click unsubscribe header and is not sent to people who unsubscribed. Set transactional on receipts, password resets and account alerts so a marketing opt-out does not block them.
Requests and responses are plain PHP arrays. SMTPfast\Types describes each one as a PHPStan array shape, so PHPStan, Psalm and your editor know every field. A new field or value from the API never breaks your code, because the SDK does not validate responses at runtime.
Every API operation is a method, named after its path: $client->domains->verify($id), $client->contacts->segments->add($contactId, $segmentId), $client->inboxes->threads->emails->reply($inboxId, $threadId, $emailId, $params). Path parameters come first, then the request body, then the query parameters, which read best as named arguments: $client->emails->list(limit: 20, status: 'bounced').
An empty PHP array is sent as {} where the spec says the value is an object, and as [] where it is a list.
Options
$client = new Client( apiKey: 'sf_...', // default: SMTPFAST_API_KEY baseUrl: 'https://smtpfa.st/api', maxRetries: 2, // after 429 (with Retry-After), 408, 5xx and dropped connections timeout: 60.0, // per attempt, in seconds ); $client->emails->send($params, idempotencyKey: 'order-1234', options: ['timeout' => 10]);
options also takes maxRetries and headers for one call. The client sends requests with curl. To use another HTTP client, implement SMTPfast\Http\Transport and pass it as transport:.
Retries and idempotency
Reads, PUT and DELETE are retried after a 5xx answer or a dropped connection. A send, reply or forward is retried with the same Idempotency-Key, so the API returns the first answer instead of sending twice. When you do not give a key, the SDK makes one per call. Other POST and PATCH calls are retried only after a per-second rate limit, which the API rejects before doing any work.
Errors
Every exception is an SMTPfast\Exception\SMTPfastException. An answer outside 2xx throws an ApiException with status, body and headers, as one of BadRequestException, AuthenticationException, PermissionDeniedException, NotFoundException, ConflictException, UnprocessableEntityException, RateLimitException or InternalServerException. No answer at all throws ConnectionException or TimeoutException.
use SMTPfast\Exception\NotFoundException; try { $client->emails->get('em_missing'); } catch (NotFoundException $e) { echo $e->status, ' ', json_encode($e->body); }
Pagination
Lists that page with a cursor also have an ...All method that fetches the next pages as you iterate:
foreach ($client->emails->listAll(limit: 100) as $email) { echo $email['id'], ' ', $email['status'], "\n"; }
Webhooks
use SMTPfast\Webhooks; // The raw body, before any JSON parsing. $event = Webhooks::parse(file_get_contents('php://input'), getallheaders(), $secret); if ($event['type'] === 'email.bounced') { // ... }
Webhooks::verifySignature($payload, $signature, $secret) returns a bool if you only need the check. parse throws a WebhookVerificationException when the signature does not match.
How this SDK is made
The SDK is generated from the OpenAPI document, so it follows the API without hand edits. scripts/generate.py (Python 3 standard library only) writes src/Types.php, src/BodyShapes.php and src/Resources/, naming every method from the x-sdk-group and x-sdk-method the spec gives each operation; the SDKs for other languages use the same names. Client.php, Core.php, Webhooks.php, Http/ and Exception/ are written by hand.
python3 scripts/pull_spec.py # download the published spec into spec/openapi.json python3 scripts/generate.py # regenerate src/Types.php and src/Resources/ composer install vendor/bin/phpstan # static analysis, including the generated array shapes vendor/bin/phpunit # unit tests, including one call per operation SMTPFAST_API_KEY=sf_... vendor/bin/phpunit tests/LiveTest.php # read-only calls, checked against the spec
A daily workflow pulls the spec and opens a pull request when it changed. The live job of the CI workflow runs the read-only tests when the workflow is started by hand, with the SMTPFAST_API_KEY secret.
Releases
Packagist publishes a version for every git tag, so there is no release workflow. To release, update Client::VERSION, then tag the commit, for example git tag v0.1.0 && git push origin v0.1.0.
License
MIT