Skip to main content
Generate type-safe PHP code from your Lettr templates. Catch errors at compile time instead of runtime. The SDK provides three Artisan commands that connect to the Lettr API, read your templates and their merge tags, and generate PHP classes you can use in your application. This means your IDE can autocomplete template slugs, enforce required merge tags, and flag typos before you deploy.

Why Code Generation?

Using string literals for template slugs and merge tags is error-prone:
// ❌ Typos cause runtime errors
Mail::lettr()->sendTemplate('welcom-email', 'Welcome!', ['nme' => 'John']);
With generated code, your IDE catches mistakes immediately:
// ✅ Type-safe, autocomplete-friendly
Mail::lettr()->sendTemplate(
    LettrTemplate::WelcomeEmail->value,
    'Welcome!',
    new WelcomeEmailData(name: 'John')
);
The first example silently sends an email with the wrong template slug and missing merge tags. The second example fails at compile time with clear error messages — LettrTemplate::WelcomEmail doesn’t exist, and WelcomeEmailData requires name, not nme.

Generate Everything at Once

The lettr:init command can generate all code for you during setup:
php artisan lettr:init
Or generate individually using the commands below.

Template Enum

Generate an enum containing all your template slugs:
php artisan lettr:generate-enum
This creates app/Enums/LettrTemplate.php:
namespace App\Enums;

enum LettrTemplate: string
{
    case WelcomeEmail = 'welcome-email';
    case OrderConfirmation = 'order-confirmation';
    case PasswordReset = 'password-reset';
    case InvoiceReady = 'invoice-ready';
    // ... all your templates
}
The command fetches all templates from your Lettr account (using the API key in your .env) and generates a backed enum case for each one. The case name is a PascalCase version of the template slug.

Usage

use App\Enums\LettrTemplate;

// IDE autocomplete shows all available templates
Mail::lettr()
    ->to($user->email)
    ->sendTemplate(LettrTemplate::WelcomeEmail->value, 'Welcome!', $data);

Benefits

  • Autocomplete - Your IDE suggests available templates
  • Refactoring - Rename templates safely across your codebase
  • Discovery - See all templates without leaving your editor

Template DTOs

Generate data transfer objects for each template’s merge tags:
php artisan lettr:generate-dtos
This creates a DTO for each template in app/Dto/Lettr/:
namespace App\Dto\Lettr;

class WelcomeEmailData
{
    public function __construct(
        public string $first_name,
        public string $activation_url,
        public ?string $company_name = null,
    ) {}

    public function toArray(): array
    {
        return array_filter([
            'first_name' => $this->first_name,
            'activation_url' => $this->activation_url,
            'company_name' => $this->company_name,
        ], fn($v) => $v !== null);
    }
}
The command reads each template’s merge tags from the Lettr API and generates a DTO class with typed constructor parameters. Required merge tags become required parameters; optional ones get default null values. The toArray() method strips null values so only provided merge tags are sent.

Usage

use App\Dto\Lettr\WelcomeEmailData;

$data = new WelcomeEmailData(
    first_name: 'John',
    activation_url: $url,
);

Mail::lettr()
    ->to($user->email)
    ->sendTemplate('welcome-email', 'Welcome!', $data->toArray());

Benefits

  • Required fields - Constructor enforces required merge tags
  • Type hints - Know exactly what data each template needs
  • Documentation - DTOs serve as living documentation
Combine DTOs with the template enum for maximum safety:
Mail::lettr()
    ->to($user->email)
    ->sendTemplate(LettrTemplate::WelcomeEmail->value, 'Welcome!', $data->toArray());

Mailable Classes

Generate Mailable classes for your templates:
php artisan lettr:pull --with-mailables
This creates Mailables in app/Mail/Lettr/:
namespace App\Mail\Lettr;

use Lettr\Laravel\Mail\LettrMailable;
use Illuminate\Mail\Mailables\Envelope;

class WelcomeEmail extends LettrMailable
{
    public function __construct(
        private string $firstName,
        private string $activationUrl,
    ) {}

    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Welcome to Our App',
        );
    }

    public function build(): static
    {
        return $this
            ->template('welcome-email')
            ->substitutionData([
                'first_name' => $this->firstName,
                'activation_url' => $this->activationUrl,
            ]);
    }
}
Generated Mailables extend LettrMailable and follow standard Laravel conventions. The constructor takes typed parameters for each merge tag, and the build() method maps them to the template’s substitution data.

Usage

use App\Mail\Lettr\WelcomeEmail;

Mail::to($user->email)->send(
    new WelcomeEmail(
        firstName: $user->first_name,
        activationUrl: $url,
    )
);

Benefits

  • Standard Laravel - Works with queues, events, and testing
  • Encapsulation - Template logic stays in one place
  • Familiar API - Same patterns you already use

Keeping Code in Sync

When you add or modify templates in Lettr, regenerate your code:
# Regenerate enum with new templates
php artisan lettr:generate-enum

# Regenerate DTOs with updated merge tags
php artisan lettr:generate-dtos
Generated files are safe to commit to version control. They act as a snapshot of your templates at generation time. If a template is renamed or a merge tag is added in Lettr, the generated code won’t update automatically — run the commands again to pick up changes.
Add these commands to your CI/CD pipeline to ensure code stays in sync with your templates. If the generated output differs from what’s committed, you know someone updated a template without regenerating.
  1. Create or update a template in the Lettr dashboard
  2. Run php artisan lettr:generate-enum and php artisan lettr:generate-dtos
  3. Commit the generated files
  4. Use the enum and DTO in your application code

Configuration

Customize paths and names in config/lettr.php:
'templates' => [
    'enum_path' => app_path('Enums'),
    'enum_namespace' => 'App\\Enums',
    'enum_class' => 'LettrTemplate',

    'dto_path' => app_path('Dto/Lettr'),
    'dto_namespace' => 'App\\Dto\\Lettr',

    'mailable_path' => app_path('Mail/Lettr'),
    'mailable_namespace' => 'App\\Mail\\Lettr',
],
OptionDefaultDescription
enum_pathapp/EnumsDirectory for the generated enum file
enum_namespaceApp\EnumsPHP namespace for the enum
enum_classLettrTemplateClass name for the generated enum
dto_pathapp/Dto/LettrDirectory for generated DTO files
dto_namespaceApp\Dto\LettrPHP namespace for DTOs
mailable_pathapp/Mail/LettrDirectory for generated Mailable files
mailable_namespaceApp\Mail\LettrPHP namespace for Mailables

Artisan Commands Reference

CommandWhat It GeneratesOutput Location
lettr:initEnum + DTOs + config (interactive)Configured paths
lettr:generate-enumTemplate slug enumapp/Enums/LettrTemplate.php
lettr:generate-dtosMerge tag DTOsapp/Dto/Lettr/
lettr:pull --with-mailablesMailable classes + Blade templatesapp/Mail/Lettr/ + resources/views/emails/lettr/

What’s Next