sendio-laravel maintained by sendio
send.io for Laravel
The official Laravel integration for the send.io email API: a sendio mail
transport, a Sendio facade for the full API, and a webhook handler that dispatches Laravel events.
Built on sendio/sendio-php.
Requires PHP 8.1+ and Laravel 10, 11, 12 or 13
Full API reference: https://developer.send.io/docs/api
Install
composer require sendio/sendio-laravel
The service provider is registered automatically. Add your API key (keys start with mx_) to .env:
SENDIO_API_KEY=mx_your_api_key
Send mail with the sendio mailer
Add a mailer to the mailers array in config/mail.php:
'sendio' => [
'transport' => 'sendio',
],
Then select it in .env:
MAIL_MAILER=sendio
MAIL_FROM_ADDRESS=onboarding@yourdomain.com
MAIL_FROM_NAME=Acme
Every Laravel mail API now goes through send.io:
use Illuminate\Support\Facades\Mail;
Mail::raw('It works!', function ($message) {
$message->to('delivered@send.io')->subject('Hello from send.io');
});
Mail::to('delivered@send.io')->send(new App\Mail\OrderShipped($order));
The send.io email id is added to the sent message as the X-Sendio-Email-ID header. To make a send
idempotent, add a Sendio-Idempotency-Key header to the message; it is passed to send.io as the
idempotency key and not sent as an email header. Mailable metadata becomes send.io tags.
Use the API with the facade
use Sendio\Laravel\Facades\Sendio;
$email = Sendio::emails()->send([
'from' => 'Acme <onboarding@yourdomain.com>',
'to' => ['delivered@send.io'],
'subject' => 'Hello from send.io',
'html' => '<p>It works!</p>',
]);
// Batch
Sendio::batch()->send([
['from' => 'Acme <onboarding@yourdomain.com>', 'to' => ['delivered@send.io'], 'subject' => 'One', 'text' => '1'],
['from' => 'Acme <onboarding@yourdomain.com>', 'to' => ['delivered@send.io'], 'subject' => 'Two', 'text' => '2'],
]);
// Retrieve
$email = Sendio::emails()->get($email->id);
// Domains
$domains = Sendio::domains()->list();
// Add a domain that also receives email, then turn receiving off later
// (send.io stops accepting its email; remove the MX record pointing at send.io).
$domain = Sendio::domains()->create([
'name' => 'yourdomain.com',
'capabilities' => ['receiving' => 'enabled'],
]);
Sendio::domains()->update($domain->id, ['capabilities' => ['receiving' => 'disabled']]);
// Mailboxes (IMAP/SMTP email accounts)
$mailbox = Sendio::mailboxes()->create(['address' => 'ada@yourdomain.com']);
Sendio::mailboxes()->update($mailbox->id, ['warmup_enabled' => false]);
The Sendio\Contracts\Client (and Sendio\Client) binding is also available for dependency injection.
Configuration
Publish the config file to change the defaults:
php artisan vendor:publish --tag=sendio-config
Key in config/sendio.php |
Env | Default |
|---|---|---|
api_key |
SENDIO_API_KEY |
none (required) |
base_url |
SENDIO_BASE_URL |
https://api.send.io |
routes |
SENDIO_ROUTES |
true |
domain |
SENDIO_DOMAIN |
null |
path |
SENDIO_PATH |
sendio |
webhook.secret |
SENDIO_WEBHOOK_SECRET |
none |
webhook.tolerance |
SENDIO_WEBHOOK_TOLERANCE |
300 |
The API key and base URL are read from config, never directly from the environment, so
php artisan config:cache works. Use SENDIO_BASE_URL to point at another endpoint, for example
a test environment:
SENDIO_BASE_URL=https://api.send.io
services.sendio.key and services.sendio.base_url in config/services.php are used as fallbacks.
Webhooks
The package registers POST /sendio/webhook (change the prefix with SENDIO_PATH). Point a send.io
webhook at it and set the signing secret so requests are verified:
SENDIO_WEBHOOK_SECRET=whsec_...
Exclude the route from CSRF protection (for example sendio/*). Each webhook dispatches a Laravel
event you can listen for, such as Sendio\Laravel\Events\EmailDelivered, EmailBounced,
EmailOpened, EmailClicked, EmailComplained, EmailReceived, ContactCreated or
DomainUpdated (see src/Events).
use Illuminate\Support\Facades\Event;
use Sendio\Laravel\Events\EmailBounced;
Event::listen(function (EmailBounced $event) {
logger()->warning('Bounce', $event->payload);
});
Error handling
The mail transport throws Symfony\Component\Mailer\Exception\TransportException with the send.io
error message when a send fails. The facade throws Sendio\Exceptions\ErrorException for API errors
(getErrorCode(), getErrorType(), getErrorMessage()) and
Sendio\Exceptions\TransporterException for network failures.
Development
composer install # resolves sendio/sendio-php from ../php through a path repository
vendor/bin/pest
# Live test in a fresh Laravel app in Docker (sends one email):
SENDIO_API_KEY=mx_... SENDIO_BASE_URL=https://api.send.io examples/smoke.sh