Managing Email Templates for Transactional Emails in Logto: Complete Implementation Guide
Logto stores transactional email templates as records in the email_templates table, supporting multilingual variants per tenant with Handlebars interpolation for dynamic content, managed through the Management API and rendered by email connectors.
Logto provides a robust system for managing email templates for transactional emails, enabling developers to customize verification codes, password resets, and MFA notifications across multiple languages. In the logto-io/logto repository, this functionality is implemented through a combination of database schema definitions, validation guards, and connector-kit utilities. This guide examines the complete architecture from storage schema to template rendering.
Database Schema for Email Templates
The email_templates Table Structure
Logto persists every transactional email template as a record in the email_templates table, defined in schema migration 1.24.1. The schema guarantees uniqueness per tenant, language tag, and template type, allowing a single tenant to maintain distinct versions of the same template for different locales.
In packages/schemas/tables/email_templates.sql, the table is defined as:
create table email_templates (
id uuid primary key,
tenant_id uuid not null,
language_tag varchar(10) not null,
template_type varchar(64) /* @use TemplateType */ not null,
subject text not null,
content text not null,
content_type varchar(20) /* 'text/html' | 'text/plain' */,
reply_to varchar(255),
send_from varchar(255)
);
This structure supports both HTML and plain text content types, with optional sender and reply-to overrides per template.
Template Types and Validation
TemplateType Enum for Transactional Messages
Logto defines a TemplateType enum that enumerates built-in transactional messages such as SignIn, Register, ForgotPassword, and MfaVerification. When an email connector sends a message, it specifies the templateType and optionally a languageTag to retrieve the appropriate localized content.
Zod Schema Validation with emailTemplateDetailsGuard
Before persisting templates, Logto validates the payload shape using emailTemplateDetailsGuard, a Zod schema defined in packages/toolkit/connector-kit/src/types/email-template.ts:
export const emailTemplateDetailsGuard = z.object({
subject: z.string(),
content: z.string(),
contentType: z.union([z.literal('text/html'), z.literal('text/plain')]).optional(),
replyTo: z.string().optional(),
sendFrom: z.string().optional(),
}) satisfies z.ZodType<EmailTemplateDetails>;
This guard ensures that only well-formed templates with valid content types are stored in the database.
Dynamic Content Rendering with Handlebars
The replaceSendMessageHandlebars Utility
When rendering templates, Logto replaces Handlebars expressions ({{…}}) with runtime data supplied by the caller. The helper replaceSendMessageHandlebars lives in packages/toolkit/connector-kit/src/index.ts and supports nested property paths such as {{application.name}}.
export const replaceSendMessageHandlebars = (template: string, payload: Record<string, any>) => {
const regex = /{{\s*([^{}\s]+)\s*}}/g;
return template.replaceAll(regex, (handleBar, key) => {
const value = key.split('.').reduce((obj, part) => obj?.[part], payload);
return value ?? '';
});
};
This utility is called by email connectors to render the final message content before sending.
Management API Operations
Listing Tenant Email Templates
Administrators can retrieve all templates for a specific tenant using the Management API endpoint implemented in packages/core/src/apis/email-templates:
GET /api/tenants/{tenantId}/email-templates
Accept: application/json
Authorization: Bearer <admin-access-token>
The response includes an array of template objects:
[
{
"id": "2f7e9c18-b0d5-4e4a-a1c5-c2e9b0a5e4d8",
"languageTag": "en",
"templateType": "SignIn",
"subject": "Your sign-in code",
"content": "Your verification code is {{code}}",
"contentType": "text/plain"
}
]
Creating and Updating Templates
To create or modify a template, send a PUT request to the type-specific endpoint. Logto validates the body with emailTemplateDetailsGuard before persisting:
PUT /api/tenants/{tenantId}/email-templates/SignIn
Content-Type: application/json
Authorization: Bearer <admin-access-token>
{
"languageTag": "en",
"subject": "Your sign-in code",
"content": "Your verification code is {{code}}",
"contentType": "text/plain",
"replyTo": "no-reply@example.com",
"sendFrom": "Logto <support@example.com>"
}
End-to-End Template Rendering Flow
The complete flow for managing email templates for transactional emails in Logto follows four distinct stages:
-
Admin Console → Management API – Administrators use the Console UI or direct API calls to create, update, or delete templates via the endpoints in
packages/core/src/apis/email-templates. -
API Validation → Database – The core service validates payloads using
emailTemplateDetailsGuardand atomically persists valid templates to theemail_templatestable. -
Template Retrieval → Rendering – When Logto triggers a transactional email (e.g., sending a verification code), it fetches the appropriate template by
templateTypeandlanguageTag, then executesreplaceSendMessageHandlebarswith the runtime payload (e.g.,{ code: "123456" }). -
Connector → Provider – The rendered subject and content are passed to the configured email connector (SendGrid, SMTP2GO, MailJunky, etc.), which dispatches the message via the provider's API or SMTP server, respecting any
replyToorsendFromoverrides defined in the template.
Summary
- Storage: Templates are stored in the
email_templatestable with uniqueness constraints per tenant, language, and template type. - Validation: The
emailTemplateDetailsGuardZod schema inpackages/toolkit/connector-kit/src/types/email-template.tsensures data integrity. - Templating: The
replaceSendMessageHandlebarsfunction inpackages/toolkit/connector-kit/src/index.tshandles dynamic variable interpolation with support for nested paths. - Management: CRUD operations are exposed through the Management API under
packages/core/src/apis/email-templates. - Extensibility: The system supports multiple email connectors (SendGrid, SMTP2GO, MailJunky) that consume rendered templates and dispatch to providers.
Frequently Asked Questions
Where does Logto store email templates?
Logto stores email templates as records in the email_templates table, defined in packages/schemas/tables/email_templates.sql. Each record includes the template content, subject, content type, and optional sender configuration, scoped to a specific tenant and language tag.
How does Logto handle multiple languages for email templates?
Logto supports multilingual templates through the language_tag column in the email_templates table. When sending an email, Logto matches the requested templateType with the appropriate languageTag, allowing tenants to maintain distinct versions of templates (e.g., English vs. Japanese sign-in emails) under the same template type.
What variables can I use in Logto email templates?
Logto email templates use Handlebars syntax ({{variableName}}) for variable interpolation. The system supports nested property paths such as {{application.name}} or {{code}}. The actual values are provided at runtime by the calling service and processed through the replaceSendMessageHandlebars utility before sending.
How do I update email templates programmatically in Logto?
You can update templates programmatically using the Management API. Send a PUT request to /api/tenants/{tenantId}/email-templates/{templateType} with the template details in the request body. The API validates the payload using emailTemplateDetailsGuard and updates the database record atomically.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →