Skip to main content
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.
ApproachBest For
Mail FacadeExisting Laravel apps — drop-in replacement, no code changes
Quick Template SendingOne-off template emails without creating a Mailable class
Lettr FacadeDirect API access with full control over every parameter
Email BuilderComplex emails with tracking, metadata, and multiple recipients
Custom MailablesReusable, 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:
PropertyDescription
requestIdUnique identifier for the API request — useful for debugging in Lettr logs
acceptedNumber 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

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:
  1. Explicit tag — if you call ->tag() on the Mailable, that value wins.
  2. Template slug — if you use ->template('slug'), the server uses the slug as the tag.
  3. 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

ApproachAuto TagOverride
LettrMailable with ->template('slug')Template slug (server-side)->tag() on the Mailable
LettrMailable with raw HTMLMailable class name in kebab-case->tag() on the Mailable
Quick sendTemplateTemplate slug (server-side)tag: parameter
Email builder / Lettr facadeNone->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