Email Templates API
The Email Templates API lets administrators customize the transactional emails sent by Auris. Each of the thirteen template types corresponds to a specific event (verification, password reset, magic link, security alerts, …) and can be customized with HTML and dynamic variables, independently per locale.
Auris ships with default templates for all types in five locales (en, it, fr, de, es). When a custom template exists for a type and locale, it is used for all future emails of that type; otherwise the branded default is used. Resolution order at send time: custom (requested locale) -> custom (en) -> default (requested locale) -> default (en).
All endpoints require a Bearer token with the manage:security permission and select the tenant via the x-tenant header (the tenant’s realm identifier).
These endpoints return plain JSON objects — they do not use the { ok, data } envelope found in
other parts of the Auris API.
List Templates
/api/email-templatesRequires: manage:securityList all thirteen template types with their customization status for the tenant. Full HTML bodies are not included — fetch a single template for that.
Success response (200)
{
"templates": [
{
"type": "verification",
"label": "Email Verification",
"description": "Sent when a new user registers to verify their email address.",
"variables": ["email", "link", "expiresIn", "year", "month", "day", "date"],
"hasCustomTemplate": true,
"active": true,
"updatedAt": "2026-07-20T10:00:00.000Z",
"customLocales": ["en", "it"]
}
],
"availableLocales": ["en", "it", "fr", "de", "es"],
"wellKnownLocales": { "en": "English", "it": "Italiano", "pt-BR": "Português (Brasil)" }
}| Field | Type | Description |
|---|---|---|
type | string | Template type identifier (see Template Types below) |
label | string | Human-readable template name |
description | string | When the email is sent |
variables | string[] | Variables available for this type, including the auto-injected global date variables (year, month, day, date) |
hasCustomTemplate | boolean | true if at least one custom version exists (any locale) |
active | boolean | Whether a custom version is active (true when no custom template exists — the default is always active) |
updatedAt | string | null | ISO 8601 timestamp of the most recent customization, or null |
customLocales | string[] | Locales for which a custom version exists |
availableLocales | string[] | Locales with built-in default templates |
wellKnownLocales | object | Locale code -> display label map used by the Console |
Get Template
/api/email-templates/{type}?locale={locale}Requires: manage:securityGet a template for one locale: the custom content (if any) plus the branded default baseline. The locale query parameter defaults to en.
Success response (200)
{
"type": "verification",
"locale": "it",
"label": "Email Verification",
"description": "Sent when a new user registers to verify their email address.",
"variables": ["email", "link", "expiresIn", "year", "month", "day", "date"],
"hasCustomTemplate": false,
"subject": null,
"htmlBody": null,
"metadata": null,
"active": true,
"updatedAt": null,
"defaultSubject": "Verifica il tuo indirizzo email",
"defaultHtmlBody": "<!DOCTYPE html>...",
"availableLocales": ["en", "it", "fr", "de", "es"],
"customLocales": ["en"],
"hasDefaultTemplate": true,
"wellKnownLocales": { "en": "English", "it": "Italiano" }
}subject, htmlBody, and metadata are null when no custom template exists for the requested locale. defaultSubject and defaultHtmlBody always contain the built-in template, rendered with the tenant’s branding (logo, color, white-label footer) exactly as a live send would use it. hasDefaultTemplate indicates whether the requested locale has a built-in default (en, it, fr, de, es); other locales fall back to English.
Error responses
| HTTP | Description |
|---|---|
400 | Invalid template type or invalid locale code |
401 / 403 | Missing or insufficient permissions |
404 | Tenant not found |
Update Template
/api/email-templates/{type}Requires: manage:securityCreate or update the custom template for one locale. The locale can be passed in the body or as a query parameter; it defaults to en. The saved template is active immediately.
Request body
{
"subject": "Verify your email — {{email}}",
"htmlBody": "<!DOCTYPE html><html><body><h1>Verify your email</h1><p>Hello {{email}},</p><p><a href=\"{{link}}\">Confirm your address</a></p><p>The link expires in {{expiresIn}}.</p></body></html>",
"locale": "en",
"metadata": { "source": "code" }
}| Field | Type | Required | Description |
|---|---|---|---|
subject | string | Yes | Email subject line. May include {{variable}} placeholders |
htmlBody | string | Yes | Full HTML email body. Maximum size 500 KB |
locale | string | No | Locale code (BCP-47-like, e.g. en, pt-BR). Defaults to en |
metadata | object | No | Editor state used by the Console (theme, builder layout, source mode). Validated against a closed schema — unrecognized fields are rejected, not silently dropped. Maximum size 256,000 characters |
The HTML body is sanitized on the server before storage: <script> tags, event handler attributes (onclick, …), javascript:/data: URIs, <iframe>/<embed>/<object>/<form>/<base> tags, and dangerous CSS constructs are stripped.
Success response (200) — the saved template record:
{
"id": "cltpl123",
"tenantId": "tnt_ScUtrVKUKGxtu7WY",
"type": "verification",
"locale": "en",
"subject": "Verify your email — {{email}}",
"htmlBody": "<!DOCTYPE html>...",
"metadata": { "source": "code" },
"active": true,
"createdAt": "2026-07-23T09:00:00.000Z",
"updatedAt": "2026-07-23T09:00:00.000Z"
}Error responses
| HTTP | Description |
|---|---|
400 | Invalid template type, invalid locale, or missing subject/htmlBody |
413 | htmlBody exceeds the 500 KB limit, or metadata exceeds 256,000 characters |
422 | metadata does not match the expected schema (unrecognized shape or field) |
Changes take effect immediately for all future emails of this type and locale. There is no staging or draft mechanism — preview and test the template before saving.
Delete Template
/api/email-templates/{type}?locale={locale}Requires: manage:securityDelete the custom template and revert to the built-in default. With the locale query parameter, only that locale’s custom version is deleted; without it, ALL locales for the type are deleted.
Success response (200)
{ "success": true }Preview Template
/api/email-templates/{type}/previewRequires: manage:securityRender a subject and HTML body with realistic sample variables for the template type. Nothing is sent and nothing is stored.
Request body
{
"subject": "Verify your email — {{email}}",
"htmlBody": "<!DOCTYPE html>..."
}Success response (200)
{
"subject": "Verify your email — [email protected]",
"html": "<!DOCTYPE html>...",
"variables": {
"email": "[email protected]",
"link": "https://console.auris.dev/verify-email?token=abc123",
"expiresIn": "24 hours",
"year": "2026",
"month": "07",
"day": "23",
"date": "2026-07-23"
}
}The variables field echoes the sample values that were substituted. The HTML body is sanitized before rendering; the subject is rendered as plain text (no HTML escaping of values).
Test Email
/api/email-templates/{type}/testRequires: manage:securityRender the given subject and HTML body with sample data and send them as a real email. By default the email goes to the authenticated administrator’s own address.
Request body
{
"subject": "Verify your email — {{email}}",
"htmlBody": "<!DOCTYPE html>...",
"recipientEmail": "[email protected]"
}| Field | Type | Required | Description |
|---|---|---|---|
subject | string | Yes | Subject line (with placeholders) |
htmlBody | string | Yes | HTML body (with placeholders) |
recipientEmail | string | No | Optional override recipient. When omitted (or empty), the test is sent to the authenticated admin’s own inbox. Must be a valid email address |
Success response (200)
{ "success": true, "sentTo": "[email protected]" }Error responses
| HTTP | Description |
|---|---|
400 | Invalid template type, missing subject/htmlBody, invalid recipientEmail, or the caller’s email address could not be determined |
500 | Email delivery failed |
The rendered subject is prefixed with [TEST] so test emails are easy to distinguish from real
transactional mail. Delivery uses the tenant’s SMTP configuration when present and active, otherwise
the platform default sender.
Template Types
Auris supports thirteen transactional email template types:
| Type | When Sent | Variables |
|---|---|---|
verification | A new user registers and must verify their email | email, link, expiresIn |
password_reset | A user requests a password reset | email, link, expiresIn |
invitation | A user is invited to join a tenant | email, orgName, role, inviterEmail, link, expiresIn |
mfa_code | Email-based two-factor authentication | email, code, expiresIn |
magic_link | Passwordless login via magic link | email, link, approveLink, expiresIn, tenantName |
login_alert | A new login is detected on the account | email, device, ipAddress, location, time |
welcome | A user successfully verifies their email | email, name, orgName, dashboardLink |
password_changed | A password change is confirmed | email, name, time, ipAddress |
account_locked | An account is locked after failed login attempts | email, name, lockDuration, attempts, ipAddress, time |
suspicious_login | An unusual login is detected | email, name, device, ipAddress, location, time, reason |
email_changed | The primary email changes (sent to the old address) | email, name, newEmail, time |
license_issued | A license key is issued | key, jwtToken, product, expiresAt, features |
ciba | Sign-in approval requested via CIBA backchannel flow | email, appName, bindingMessage, approveUrl, denyUrl |
The global date variables year, month, day, and date (format YYYY-MM-DD) are auto-injected into every render, in addition to the per-type variables above.
Template Syntax
{{variable}}— replaced with the value. In the HTML body, values are HTML-escaped automatically to prevent injection; in the subject line values are inserted as plain text.{{{variable}}}— raw interpolation without escaping. Only for trusted, server-built HTML fragments.{{#if variable}}...{{/if}}— the block is kept only when the variable resolves to a non-empty string; otherwise it is removed entirely (markup included). Example: themagic_linkdefault template renders the secondary approve button only whenapproveLinkis provided.
Unknown placeholders are left as-is in the output. Dot notation and loops are not supported.
<h1>Sign in to {{tenantName}}</h1>
<p><a href="{{link}}">Sign in on this device</a></p>
{{#if approveLink}}
<p><a href="{{approveLink}}">Approve on the original device</a></p>
{{/if}}
<p>The link expires in {{expiresIn}}.</p>Permissions Reference
| Permission | Description |
|---|---|
manage:security | List, view, update, delete, preview, and test all email templates for the tenant |
Related
- Customizing Email Templates — Step-by-step template customization guide
- Email Templates — Edit templates visually from the Console