The email API built for Laravel.
Keep your Mailables and Mail::to(). Lettr adds templates your team
edits without a deploy, generated types for every merge tag, and queued sends that never go out twice.
Free for 3,000 emails a month. No credit card. Laravel 10 to 13, PHP 8.4+.
From install to a
typed, queued send.
Four steps. Two of them are artisan commands, and the last one is the code you already write.
-
composer require lettr/lettr-laravelLaravel 10 to 13 on PHP 8.4+. Adds the lettr mail transport.
-
php artisan lettr:initWrites your API key, sets MAIL_MAILER=lettr and checks your domain's DNS.
-
php artisan lettr:generate-dtosTyped DTOs from your template merge tags.
-
Mail::to($customer) ->queue(new OrderShipped(...))Queued and retry-safe by default. A retry replays the original send.
The template lives in Lettr. The types live in your app.
Behind this text: the enum and DTO that lettr:generate-dtos writes for
you. In front: the Mailable that uses them. Your team edits the email in Lettr, your IDE and PHPStan check every variable.
<?php
namespace App\Mail;
use App\Dto\Lettr\OrderShippedData;
use Illuminate\Mail\Mailables\Envelope;
use Lettr\Laravel\Mail\LettrMailable;
class OrderShipped extends LettrMailable
{
// The design lives in Lettr. Edit it in the dashboard, no deploy needed.
protected ?string $templateSlug = 'order-shipped';
public function __construct(
public OrderShippedData $data,
) {}
// No subject here, so the subject set on the template in Lettr is used.
public function envelope(): Envelope
{
return new Envelope;
}
/**
* Merge tags sent to the Lettr template.
*
* @return array<string, mixed>
*/
public function withMergeTags(): array
{
return $this->data->toArray();
}
} You own the code. Your team owns the words.
Copy changes stop being tickets. Your team edits the email in Lettr, and lettr:pull brings it into your repo as Blade.
The order-shipped email in the Lettr editor uses three merge tags. lettr:generate-dtos types each one as a string in OrderShippedData, and lettr:pull writes the Blade view, Mailable and DTO into the app.
A retried job never sends twice.
Queued sends carry an idempotency key built from the job's uuid. When the API accepted an email but the response timed out, the retry replays the original result instead of emailing your customer again. Nothing to configure.
- Applies to
- queued sends, by default
- Key kept for
- 24 hours
- Replay reported as
- $response->replayed
// Sending synchronously? Pass your own key.
Mail::lettr()
->to($order->customer->email)
->idempotencyKey("order-{$order->id}-shipped")
->send(new OrderShipped($data));A queue worker runs SendQueuedMailable, times out after 30 seconds and retries. With the idempotency key, the Lettr API answers the retry with the first result, marked replayed, and the customer's inbox holds one email. Without it, the retry sends a second email.
Eleven minutes, one real app.
Laravel Daily
Templates, DTOs, enums · 11:00
Prefer YouTube? Watch the walkthrough there.
Everything else
stays Laravel.
Lettr is a mail transport and a facade. Everything else in your app stays where it is.
-
Mailables and notifications
MAIL_MAILER=lettrDelivered over the HTTP API. No SMTP.
-
One email at a time
Mail::mailer('lettr')Keep your default mailer while you move over.
-
Templates as Blade
lettr:pull / lettr:pushMove templates between the editor and your repo.
-
Tests
Mail::fake()Assertions you already write keep passing.
-
Scheduled sends
->scheduleAt(now()->addDay())Five minutes to thirty days ahead, cancellable.
-
Typed errors
RateLimitExceptionOne exception class per failure, with retryAfter.
-
Marketing, same account
Lettr::campaigns()Audiences, segments and campaigns too.
Start free. Pay per email after.
Transactional email is billed per email, campaigns per contact, both from the same account.
50,000
Pro $15 /mo
Transactional.
Per emailPay per email3,000 emails every month, free.
Scale up from $15/mo.
- Email API & SMTP
- No daily limit on paid plans
- Real-time webhooks
Marketing.
Per contactPay per contactUp to 500 contacts, free.
Scale up from $10/mo.
- Unlimited campaigns
- Drag-and-drop editor
- Lists & segments
Questions from Laravel developers.
The package needs PHP 8.4. Until you upgrade, point Laravel's SMTP mailer at Lettr with your API key as the password. Mailables and queues keep working, and you can switch to the package later without touching them.
MAIL_MAILER=smtp
MAIL_HOST=smtp.lettr.com
MAIL_PORT=587
MAIL_USERNAME=lettr
MAIL_PASSWORD=your-lettr-api-keyNo. The package registers a lettr mail transport, so Mailables, Mail::to(), queued mail and Mail::fake() work as before. It adds Mail::lettr() and the LettrMailable base class for sending templates stored in Lettr.
Yes, on the mail channel. With MAIL_MAILER=lettr, mail notifications are delivered by Lettr like any other Mailable. The package does not add a notification channel of its own.
Add a webhook endpoint in Lettr, or with Lettr::webhooks() from your app, and point it at a route you own. Lettr posts delivery, open, click, bounce and spam events to it, and can send Basic auth or OAuth 2.0 credentials with each request. The package does not ship a webhook controller, so you handle the request in your own route.
Install the package and send one email type at a time with Mail::mailer('lettr') while your current mailer stays the default. When you are happy, set MAIL_MAILER=lettr. Your Mailables do not change.
Yes. Sandbox API keys send to your own inbox without any DNS setup and are not billed. In your test suite, Mail::fake() works as usual.
The API accepts 3 requests per second per team, and the free plan sends up to 100 emails a day. Queue your sends: a RateLimitException carries retryAfter, so a job can wait and try again.
In the EU. Lettr is EU-hosted and compliant with GDPR and CCPA.