Lettr templates let you manage email designs in the Lettr dashboard while your application provides the dynamic data. This separation means designers can update email layouts without touching application code, and developers can change data without worrying about design.
This page covers sending templates, versioning, and managing templates programmatically via the API.
Sending with Templates
The simplest way to send a template email:
$response = $lettr->emails()->sendTemplate(
from: 'sender@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome!',
templateSlug: 'welcome-email',
substitutionData: [
'first_name' => 'John',
'activation_url' => 'https://example.com/activate/abc123',
],
);
The array keys correspond to merge tags in your template (e.g., {{first_name}}). If a required merge tag is missing from the array, the tag renders as an empty string in the sent email. See Merge Tags & Template Language for the full syntax including conditionals and loops.
Use the Get Merge Tags method to retrieve the list of available merge tags for a template. This helps you ensure you’re providing all required data when sending.
Template Versioning
Specify a template version to use a specific iteration:
$response = $lettr->emails()->sendTemplate(
from: 'sender@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome!',
templateSlug: 'welcome-email',
templateVersion: 2,
substitutionData: ['first_name' => 'John'],
);
Omit the templateVersion parameter to use the active (published) version. Lettr always serves the most recently published version unless you explicitly request a different one.
When to use versioning:
- Production safety — Pin to a known-good version so publishing a new draft doesn’t affect live emails
- A/B testing — Send different versions to different cohorts and compare metrics in Analytics
- Gradual rollout — Test a new version with a subset of users before publishing it as the active version
See Template Versions for how versioning works in the Lettr dashboard.
Using the Builder
For more control, use the email builder with templates:
$response = $lettr->emails()->send(
$lettr->emails()->create()
->from('sender@yourdomain.com')
->to(['recipient@example.com'])
->subject('Your Order #{{order_id}}')
->useTemplate('order-confirmation', version: 1, projectId: 123)
->substitutionData([
'order_id' => '12345',
'customer_name' => 'John Doe',
'items' => [
['name' => 'Product A', 'price' => 29.99],
['name' => 'Product B', 'price' => 49.99],
],
'total' => 79.98,
])
);
The builder combines template usage with other email features like tracking, metadata, and custom headers. Use it when you need fine-grained control over the email alongside template content.
You can override the template’s subject line by calling ->subject() on the builder. This is useful for adding dynamic content to subjects, like order numbers or user names.
Listing Templates
Retrieve available templates via the API:
use Lettr\Dto\Template\ListTemplatesFilter;
$response = $lettr->templates()->list();
foreach ($response->templates as $template) {
echo $template->name . ' (' . $template->slug . ')';
}
// With pagination and filtering
$filter = ListTemplatesFilter::create()
->projectId(123)
->perPage(20)
->page(2);
$response = $lettr->templates()->list($filter);
The list method returns basic template information including name, slug, and active version. Use this to build dynamic template selectors in your application or verify that templates exist before sending.
Cache the template list in your application to avoid repeated API calls. Templates don’t change frequently, so a cache with a 5-10 minute TTL is usually sufficient.
Get Template Details
$template = $lettr->templates()->get('welcome-email');
echo $template->name;
echo $template->slug;
echo $template->html;
echo $template->activeVersion;
echo $template->versionsCount;
// With specific project
$template = $lettr->templates()->get('welcome-email', projectId: 123);
The html property contains the full HTML content of the template, including merge tags. Use this to preview templates or implement custom template editing in your application.
The HTML returned from the API includes Lettr’s merge tag syntax. You’ll need to process substitutions yourself if you want to preview with actual data — or just send a test email using the API’s built-in substitution engine.
Retrieve merge tags (template variables) from a template:
$response = $lettr->templates()->getMergeTags('welcome-email');
foreach ($response->mergeTags as $tag) {
echo $tag->key; // e.g., 'user_name'
echo $tag->required; // true/false
echo $tag->type; // e.g., 'string', 'object'
// Nested tags (for objects)
if ($tag->children !== null) {
foreach ($tag->children as $child) {
echo $child->key;
echo $child->type;
}
}
}
// With specific project and version
$response = $lettr->templates()->getMergeTags(
'welcome-email',
projectId: 123,
version: 2,
);
Merge tags define the dynamic data your template expects. The response includes:
key — The merge tag name (used in {{key}} syntax)
required — Whether the tag must be provided when sending
type — Data type (string, object, array)
children — Nested tags for objects and arrays
Use this method to:
- Validate that you’re providing all required data before sending
- Build dynamic forms for template data input
- Generate type-safe DTOs in statically-typed languages
When working with nested data structures (objects and arrays), check the children property to understand the full shape of the expected data. This is especially important for loop blocks that iterate over arrays.
Create a Template
use Lettr\Dto\Template\CreateTemplateData;
// With HTML content
$template = $lettr->templates()->create(new CreateTemplateData(
name: 'My Template',
slug: 'my-template',
projectId: 123,
html: '<html>...</html>', // provide html OR json, not both
));
// Or with TOPOL.io JSON format
$template = $lettr->templates()->create(new CreateTemplateData(
name: 'My Template',
json: '{"blocks":[]}', // TOPOL.io editor JSON
));
echo $template->id;
echo $template->slug;
Creating templates programmatically is useful for:
- Migrating templates from another email service
- Generating templates from a design system or component library
- Building template management features in your application
Template slugs must be unique within your account. If you try to create a template with an existing slug, the API returns a 422 validation error.
Delete a Template
$lettr->templates()->delete('my-template');
// With specific project
$lettr->templates()->delete('my-template', projectId: 123);
Deleting a template is permanent and cannot be undone. All template versions are deleted. If any emails in your application reference this template slug, they will fail to send after deletion.
What’s Next