clario-laravel maintained by agirem
agirem/clario-laravel
Laravel Mail transport and optional webhook consumer for the Clario email API.
Send with familiar Laravel Mailables:
Mail::to($user)->send(new OtpCodeMail($code));
Requirements
- PHP 8.2+
- Laravel 11 or 12
- Clario API access (active trial, Solo, or Pro)
- API key with
emails:send
Installation
composer require agirem/clario-laravel
Publish config (optional):
php artisan vendor:publish --tag=clario-config
Configuration
.env:
MAIL_MAILER=clario
CLARIO_API_KEY=clario_live_...
# Optional when Laravel From matches a Clario address:
# CLARIO_MAIL_ADDRESS_ID=01HX...
CLARIO_BASE_URL=https://clario-mail.com/api/v1
config/mail.php:
'clario' => [
'transport' => 'clario',
],
Sender resolution
The transport sends either mail_address_id or from to the Clario API, in this order:
- Message header
X-Clario-Mail-Address-Id→mail_address_id - Map
config('clario.mail_addresses')[from@email]→mail_address_id - Default
CLARIO_MAIL_ADDRESS_ID→mail_address_id - Laravel / Symfony
Fromaddress →from(API resolves the mailbox)
Display name is configured on the Clario address, not via Laravel from.name.
Idempotency (important for queued mail)
Every send includes an Idempotency-Key. Prefer a stable business key on critical mailables:
public function headers(): Headers
{
return new Headers(
text: [
'X-Clario-Idempotency-Key' => 'otp-'.$this->user->id.'-'.$this->codeId,
],
);
}
If omitted, the transport hashes the rendered message (stable across worker retries of the same content).
Attachments
Attachments are sent as multipart/form-data. Limits match the API: max 10 files, 5 MB each, allowed MIME types (PDF, JPEG/PNG/GIF, Word, Excel, CSV, plain text).
Webhooks (optional)
CLARIO_WEBHOOKS_ENABLED=true
CLARIO_WEBHOOK_SECRET=whsec_...
CLARIO_WEBHOOK_PATH=clario/webhook
Point your Clario Developer webhook to https://your-app.test/clario/webhook.
Events dispatched:
Clario\Laravel\Webhooks\Events\EmailQueuedEmailSent,EmailDelivered,EmailBounced,EmailFailedEmailComplained,EmailOpened,EmailClicked,EmailReceived
Event::listen(EmailDelivered::class, function (EmailDelivered $event) {
// $event->emailId, $event->data['metadata'], $event->eventId
});
Signatures use Clario-Signature: t=…,v1=… (HMAC-SHA256 of timestamp.rawBody). Invalid signatures return 401.
The route uses the api middleware group (no CSRF).
Metadata
public function headers(): Headers
{
return new Headers(text: [
'X-Clario-Metadata-order_id' => (string) $this->order->id,
]);
}
Exceptions
Clario\Laravel\Exceptions\ClarioTransportException exposes:
statusCode,errorCode,requestIdretryable— useful for queue$this->release()vs fail
Non-retryable examples: pro_plan_required, sender_not_allowed, quota exceeded, idempotency_conflict.
Testing
composer install
composer test
License
MIT