zeevx / laravel-sendbyte-transport
A SendByte mail transport for Laravel - send mail through https://sendbyte.africa with Laravel's native mail system
Package info
github.com/zeevx/laravel-sendbyte-transport
pkg:composer/zeevx/laravel-sendbyte-transport
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.5
- illuminate/http: ^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/mail: ^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^9.0|^10.0|^11.0|^12.0|^13.0
- symfony/mailer: ^6.2|^7.0|^8.0
- symfony/mime: ^6.2|^7.0|^8.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^7.0|^8.0|^9.0|^10.0|^11.0
- pestphp/pest: ^1.23|^2.0|^3.0
README
A SendByte mail transport for Laravel. Set MAIL_MAILER=sendbyte and your existing Mailables, Notifications, and Mail:: calls deliver through SendByte's transactional email API, no code changes required.
Full SendByte documentation: docs.sendbyte.africa
Requirements
- PHP 8.1 through 8.4
- Laravel 9 through 13
Installation
composer require zeevx/laravel-sendbyte-transport
Add a sendbyte mailer to config/mail.php:
'mailers' => [ // ... 'sendbyte' => [ 'transport' => 'sendbyte', 'key' => env('SENDBYTE_API_KEY'), ], ],
Then point your .env at it:
MAIL_MAILER=sendbyte SENDBYTE_API_KEY=sk_live_your_key MAIL_FROM_ADDRESS=receipts@yourdomain.com MAIL_FROM_NAME="Your App"
If you prefer keeping credentials with your other third-party services, omit key from the mailer and set it in config/services.php instead:
'sendbyte' => [ 'key' => env('SENDBYTE_API_KEY'), ],
The mailer accepts two optional settings:
'sendbyte' => [ 'transport' => 'sendbyte', 'key' => env('SENDBYTE_API_KEY'), 'base_url' => env('SENDBYTE_BASE_URL', 'https://api.sendbyte.africa'), 'timeout' => 30, ],
Your sending domain must be verified in your SendByte dashboard before live sends. Use an sk_test_ sandbox key to exercise the full pipeline without delivering to real inboxes.
Sending from a different domain
MAIL_FROM_ADDRESS applies to every mailer. When SendByte handles only part of your mail, or the domain you verified with SendByte is not the one the rest of your app sends from, give the mailer its own from block. Laravel reads a mailer's from in preference to the global one, so your other mailers are unaffected:
'sendbyte' => [ 'transport' => 'sendbyte', 'key' => env('SENDBYTE_API_KEY'), 'from' => [ 'address' => env('SENDBYTE_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), 'name' => env('SENDBYTE_FROM_NAME', env('MAIL_FROM_NAME')), ], ],
Sending from an address whose domain is not verified fails with domain_not_verified, naming the domain it saw. That domain comes from the from address, so check which one applies to the mailer you are sending through.
Usage
Send mail exactly as you always do:
Mail::to($user)->send(new OrderShipped($order)); $user->notify(new InvoicePaid($invoice));
Everything a Mailable carries maps onto SendByte's send API: HTML and plain-text bodies, cc, bcc, reply-to, attachments (base64-encoded, up to 10), and custom headers.
Tags
Laravel's message tags become SendByte tags (up to 10), filterable in the dashboard and the List Emails endpoint:
use Illuminate\Mail\Mailables\Envelope; public function envelope(): Envelope { return new Envelope( subject: 'Your receipt', tags: ['receipt', 'payment'], ); }
Message metadata is delivered as X-Metadata-* headers.
Idempotent sends
SendByte deduplicates sends that share an idempotency key within 24 hours. Set one per message via the X-SendByte-Idempotency-Key header; the transport hoists it into the API's idempotency_key field:
public function headers(): Headers { return new Headers(text: [ 'X-SendByte-Idempotency-Key' => 'order-4421-receipt', ]); }
Message IDs
SendByte assigns the final Message-ID at delivery. The transport records the API's email id (em_...) as the sent message id, so you can correlate sends with dashboard entries and webhook events:
$sent = Mail::to($user)->send(new OrderShipped($order)); $sent->getMessageId(); // "em_01j..."
Failures
A rejected send (unverified domain, suppressed recipient, rate limit, validation error) throws a Symfony\Component\Mailer\Exception\TransportException whose message includes SendByte's error detail.
Limits
Imposed by the SendByte API:
- Maximum 50 recipients per message
- Maximum 10 attachments and 10 tags per message
Message-IDcannot be overridden; SendByte assigns it at delivery
Testing your integration
The transport sends through Laravel's HTTP client, so Http::fake() works, and Mail::fake() behaves as usual since the transport is never invoked.
Run the package test suite (powered by Pest):
composer test
composer lint
Security
If you discover any security-related issues, please email adamsohiani@gmail.com instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see the License File for more information.