# Managing Email Templates for Transactional Emails in Logto: Complete Implementation Guide

> Learn to manage transactional email templates in Logto using the Management API. Implement multilingual variants with Handlebars for dynamic content and simplify email delivery.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-07-04

---

**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`](https://github.com/logto-io/logto/blob/main/packages/schemas/tables/email_templates.sql), the table is defined as:

```sql
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`](https://github.com/logto-io/logto/blob/main/packages/toolkit/connector-kit/src/types/email-template.ts):

```typescript
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`](https://github.com/logto-io/logto/blob/main/packages/toolkit/connector-kit/src/index.ts) and supports nested property paths such as `{{application.name}}`.

```typescript
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`:

```http
GET /api/tenants/{tenantId}/email-templates
Accept: application/json
Authorization: Bearer <admin-access-token>

```

The response includes an array of template objects:

```json
[
  {
    "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:

```http
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:

1. **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`.

2. **API Validation → Database** – The core service validates payloads using `emailTemplateDetailsGuard` and atomically persists valid templates to the `email_templates` table.

3. **Template Retrieval → Rendering** – When Logto triggers a transactional email (e.g., sending a verification code), it fetches the appropriate template by `templateType` and `languageTag`, then executes `replaceSendMessageHandlebars` with the runtime payload (e.g., `{ code: "123456" }`).

4. **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 `replyTo` or `sendFrom` overrides defined in the template.

## Summary

- **Storage**: Templates are stored in the `email_templates` table with uniqueness constraints per tenant, language, and template type.
- **Validation**: The `emailTemplateDetailsGuard` Zod schema in [`packages/toolkit/connector-kit/src/types/email-template.ts`](https://github.com/logto-io/logto/blob/main/packages/toolkit/connector-kit/src/types/email-template.ts) ensures data integrity.
- **Templating**: The `replaceSendMessageHandlebars` function in [`packages/toolkit/connector-kit/src/index.ts`](https://github.com/logto-io/logto/blob/main/packages/toolkit/connector-kit/src/index.ts) handles 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`](https://github.com/logto-io/logto/blob/main/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.