Lettr integrates seamlessly with Laravel’s mail system. This page covers every approach — from the standard Mail facade to the low-level email builder — so you can choose the one that fits your use case.
| Approach | Best For |
|---|
| Mail Facade | Existing Laravel apps — drop-in replacement, no code changes |
| Quick Template Sending | One-off template emails without creating a Mailable class |
| Lettr Facade | Direct API access with full control over every parameter |
| Email Builder | Complex emails with tracking, metadata, and multiple recipients |
| Custom Mailables | Reusable, testable email classes using Lettr templates |
Using Laravel’s Mail Facade
If you’ve set Lettr as your default mailer, use Laravel’s standard Mail facade:
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;
Mail::to('customer@example.com')->send(new OrderShipped($order));
This is the simplest approach if you’re migrating from another mail provider. Your existing Mailable classes work without modification — just change the mailer in your .env and all emails route through Lettr.
If Lettr isn’t your default mailer, specify it explicitly:
Mail::mailer('lettr')->to('customer@example.com')->send(new OrderShipped($order));
Quick Template Sending
Send a Lettr template without creating a Mailable class:
use Illuminate\Support\Facades\Mail;
Mail::lettr()
->to('user@example.com')
->sendTemplate('welcome-email', 'Welcome!', [
'name' => 'John',
'company' => 'Acme Inc',
]);
The array keys map to merge tags in your Lettr template. For example, 'name' => 'John' fills the {{name}} merge tag. See Merge Tags & Template Language for the full syntax.
With additional options:
Mail::lettr()
->to('user@example.com')
->cc('manager@example.com')
->bcc('archive@example.com')
->sendTemplate(
templateSlug: 'order-confirmation',
subject: 'Order Confirmation',
substitutionData: [
'order_id' => $order->id,
'items' => $order->items->toArray(),
],
version: 2,
);
Use version to pin a specific template version in production. Omit it to always use the active (published) version. See Template Versioning for details.
Using the Lettr Facade
For full control, use the Lettr facade directly. This bypasses Laravel’s mail abstraction and calls the Lettr API with explicit parameters.
HTML Email
use Lettr\Laravel\Facades\Lettr;
Lettr::emails()->sendHtml(
from: 'hello@yourdomain.com',
to: 'recipient@example.com',
subject: 'Hello from Lettr',
html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>'
);
Plain Text Email
Lettr::emails()->sendText(
from: 'hello@yourdomain.com',
to: ['user1@example.com', 'user2@example.com'],
subject: 'Plain text update',
text: 'This is a plain text email.'
);
The to parameter accepts both a single email string and an array of recipients.
Template Email
Lettr::emails()->sendTemplate(
from: 'hello@yourdomain.com',
to: 'customer@example.com',
subject: 'Your order is ready!',
templateSlug: 'order-ready',
substitutionData: [
'customer_name' => $customer->name,
'order_number' => $order->number,
]
);
When using the Lettr facade, you must provide the from address explicitly since it doesn’t use Laravel’s MAIL_FROM_ADDRESS config automatically.
Email Builder
For complex emails, use the fluent email builder. This gives you access to every option the Lettr API supports in a chainable interface:
use Lettr\Laravel\Facades\Lettr;
$response = Lettr::emails()->send(
Lettr::emails()->create()
->from('sender@yourdomain.com', 'Your Company')
->to(['recipient@example.com'])
->cc(['cc@example.com'])
->bcc(['bcc@example.com'])
->subject('Monthly Newsletter')
->html('<h1>Newsletter</h1><p>{{content}}</p>')
->text('Newsletter: {{content}}')
->substitutionData(['content' => $newsletterContent])
->withOpenTracking(true)
->withClickTracking(true)
->tag('welcome')
);
// Access the response
echo $response->requestId;
echo $response->accepted;
The builder is useful when you need to combine multiple features in a single email — for example, HTML content with a plain text fallback, tracking, metadata, and attachments.
Response Object
The send() method returns a response object with:
| Property | Description |
|---|
requestId | Unique identifier for the API request — useful for debugging in Lettr logs |
accepted | Number of recipients the API accepted for delivery |
Custom Mailable Classes
Create a Mailable that uses Lettr templates:
namespace App\Mail;
use Lettr\Laravel\Mail\LettrMailable;
use Illuminate\Mail\Mailables\Envelope;
class OrderConfirmation extends LettrMailable
{
public function __construct(
private Order $order
) {}
public function envelope(): Envelope
{
return new Envelope(
subject: "Order #{$this->order->id} Confirmed",
);
}
public function build(): static
{
return $this
->template('order-confirmation')
->substitutionData([
'order_id' => $this->order->id,
'customer_name' => $this->order->customer->name,
'items' => $this->order->items->toArray(),
'total' => $this->order->total,
]);
}
}
Send it like any Laravel Mailable:
Mail::to($customer->email)->send(new OrderConfirmation($order));
Custom Mailables are the best choice when you send the same template from multiple places in your application. They encapsulate the template slug, merge tag mapping, and subject line in one class, keeping your controllers and jobs clean.
You can auto-generate Mailable classes from your Lettr templates. See Mailable Classes for details.
Mailables with Queues
LettrMailable classes work with Laravel’s queue system. To send asynchronously:
Mail::to($customer->email)->queue(new OrderConfirmation($order));
Make sure your Mailable class implements ShouldQueue and uses the Queueable trait, as you would with any standard Laravel Mailable.
Attachments
Add attachments to your emails:
use Lettr\Laravel\Facades\Lettr;
use Lettr\Dto\Attachment;
Lettr::emails()->send(
Lettr::emails()->create()
->from('billing@yourdomain.com')
->to(['customer@example.com'])
->subject('Your Invoice')
->template('invoice-email')
->substitutionData(['invoice_number' => 'INV-001'])
->attachments([
new Attachment(
name: 'invoice.pdf',
type: 'application/pdf',
data: base64_encode(file_get_contents($pdfPath))
),
])
);
Attachments must be base64-encoded. The type parameter should be a valid MIME type. See Attachments for supported types and size limits.
Tracking
Enable open and click tracking:
Lettr::emails()->send(
Lettr::emails()->create()
->from('marketing@yourdomain.com')
->to(['subscriber@example.com'])
->subject('Check out our new features')
->html($htmlContent)
->withOpenTracking(true)
->withClickTracking(true)
);
When tracking is enabled, Lettr inserts a tracking pixel for opens and rewrites links for click tracking. You can view tracking data in the Analytics dashboard or receive events via webhooks. See Tracking for details on how tracking works and privacy considerations.
Tags let you group emails for analytics. You can filter and break down metrics by tag in the Analytics dashboard, and the home dashboard shows per-tag Sent, Opened, and Rate stats automatically. See Tags for the full reference.
In most cases you don’t need to set a tag yourself — the Laravel package resolves it automatically using this priority:
- Explicit tag — if you call
->tag() on the Mailable, that value wins.
- Template slug — if you use
->template('slug'), the server uses the slug as the tag.
- Class name — if neither of the above applies, the package generates a kebab-case tag from the Mailable class name (e.g.,
OrderConfirmation becomes order-confirmation).
Automatic Tag from Lettr Template
When you use ->template('slug') on a LettrMailable, the server automatically uses the template slug as the tag. No code needed — your analytics are grouped by template out of the box. The Emails page in the dashboard shows Sent, Opened, and Rate per template.
Automatic Tag from Mailable Class Name
When sending raw HTML (without a Lettr template) through a LettrMailable, the package auto-generates a kebab-case tag from the class name. For example, OrderConfirmation becomes order-confirmation.
Overriding with Laravel’s tag() Method
Call ->tag('my-tag') on any Mailable to override the automatic tag:
class WeeklyNewsletter extends LettrMailable
{
public function build(): static
{
return $this
->template('newsletter')
->tag('welcome'); // overrides the automatic 'newsletter' tag
}
}
If multiple tags are set (via Laravel’s tag() method), they are joined with _ into a single string. The combined string must not exceed 64 characters.
Quick Template Sending
The sendTemplate method accepts a tag parameter. If omitted, the template slug is used automatically:
Mail::lettr()
->to('user@example.com')
->sendTemplate('welcome-email', 'Welcome!', [
'name' => 'John',
], tag: 'onboarding'); // omit to use 'welcome-email' as the tag
Email Builder
Use ->tag() on the email builder:
Lettr::emails()->send(
Lettr::emails()->create()
->from('marketing@yourdomain.com')
->to(['subscriber@example.com'])
->subject('Monthly Newsletter')
->html($htmlContent)
->tag('newsletter-2024-01')
);
Raw HTML via Lettr Facade
When sending raw HTML via the Lettr facade (sendHtml, sendText), no automatic tag is applied. Set one manually using the email builder with ->tag().
Summary
| Approach | Auto Tag | Override |
|---|
LettrMailable with ->template('slug') | Template slug (server-side) | ->tag() on the Mailable |
| LettrMailable with raw HTML | Mailable class name in kebab-case | ->tag() on the Mailable |
Quick sendTemplate | Template slug (server-side) | tag: parameter |
| Email builder / Lettr facade | None | ->tag() on the builder |
Testing
Use Laravel’s Mail::fake() for testing:
use Illuminate\Support\Facades\Mail;
use Lettr\Laravel\Mail\InlineLettrMailable;
public function test_welcome_email_is_sent()
{
Mail::fake();
// Trigger the email
$this->post('/register', ['email' => 'user@example.com']);
Mail::assertSent(InlineLettrMailable::class, function ($mail) {
return $mail->hasTo('user@example.com');
});
}
Mail::fake() intercepts all outbound mail, so no real emails are sent during tests. This works identically to testing any other Laravel mailer.
For custom Mailable classes, assert against the class directly:
Mail::assertSent(OrderConfirmation::class, function ($mail) {
return $mail->hasTo('customer@example.com')
&& $mail->order->id === 42;
});
What’s Next