[data-reveal]{opacity:1!important;transform:none!important}
Built-in Emails
Interfaces

Built-in Emails

The DMS sends seven emails of its own.

Overview

The DMS sends seven emails of its own. Each one is a template registered through RegisterHtmlTemplate, rendered by the frontend server from a Vue component in frontend-vue/layers/dms-layout/app/emails/, and delivered through @antelopejs/interface-email. They share one frame, EmailLayout: the branding logo, a white card, a heading, the body, a separator and a footer.

TemplateSent whenSent toEnglish subject
EmailUserValidationA user signs up while auth.mustValidateEmail is on, or asks for a new codeThe new account's addressVerify Your Email
EmailResetPasswordA user asks for a password resetThe account's addressReset Your Password
EmailTwoFactorA user signs in with email as their second factorThe account's addressYour verification code
EmailAdminInviteA member is invited to the platform or to a workspaceThe invited addressYou're invited to join <workspace> on <platform>, shorter when a name is unknown
EmailChangeVerificationA user asks to change their sign-in emailThe new addressConfirm your new sign-in email
EmailChangeNoticeThe same requestThe current addressYour sign-in email is being changed
EmailExportReadyA data export delivered by email has finishedThe user who asked for itYour data export is ready

Each email, subject included, is written in the recipient's language, English or French (see Language).

These names are taken: pick other names for your own emails. To change what a built-in email looks like, see Replacing a built-in email.

Props

What the backend passes to each template. A template that replaces a built-in one receives the same props.

TemplateProps
EmailUserValidationuserName, validationCode, expiresInHours (from auth.emailValidationTokenLifetime), expiresIn
EmailResetPassworduserName, resetCode, expiresInHours (from auth.passwordRecoverTokenLifetime), expiresIn
EmailTwoFactoruserName, verificationCode
EmailAdminInviteemail, signupLink, expiresInDays, and when known workspaceName, inviterName, platformName (meta.title)
EmailChangeVerificationuserName, newEmail, code, expiresInHours (from auth.emailValidationTokenLifetime), expiresIn
EmailChangeNoticeuserName, newEmail
EmailExportReadyuserName, downloadLink, expiresInHours, expiresIn, partial, failedSources (the export sources that could not be included)

userName is the user's name, or their email address when they have none. expiresInHours is the lifetime as a number, for the template to word in the recipient's language; expiresIn is the same lifetime already worded in English (24 hours), kept for templates written before expiresInHours.

Rendered examples

These captures show HTML returned by the DMS frontend's real render endpoint, opened directly in Chromium, rather than the playground's HTML code viewer. All names, codes, addresses and links are fictitious; no email is sent. The default SVG logo is intentionally omitted by publicLogoOnly.

EmailUserValidation

The signup email asks Alex to verify their address with a six-digit code, valid for 24 hours.

EmailResetPassword

The password-reset email uses a separate code and the configured recovery lifetime: two hours in this example.

EmailTwoFactor

The second-factor email provides a sign-in code. Its footer tells the recipient to ignore the message if they did not attempt to sign in.

EmailAdminInvite

The invitation names the inviter, workspace and platform when they are known. Its button uses a fictitious destination here.

EmailChangeVerification

The new address receives the code that confirms the sign-in email change.

EmailChangeNotice

The current address receives a notice, not the confirmation code. It tells the recipient what to do if they did not request the change.

EmailExportReady

A complete export provides a download button and an expiry. A partial export also names the sources that could not be included.

These are browser previews, not Gmail, Outlook or Apple Mail compatibility tests. See Email Layout for the custom digest, logo variants and the limits of dark-mode support.

Language

Each email is written in the recipient's language: the account's language, or for an invitation the language it was sent in. English and French are supported; any other language falls back to English.

The body text lives in the dms-layout locale files (i18n/locales/layout-*.json), under one key per email (emails.user_validation, emails.reset_password, emails.two_factor, emails.admin_invite, emails.email_change_verification, emails.email_change_notice, emails.export_ready) plus the shared emails.common. Locale files merge in module priority order, so a module registered with a priority above the DMS's (0) can reword any of these keys in its own i18n/locales/*.json:

frontend-vue/i18n/locales/acme-en-GB.json
{
  "emails": {
    "reset_password": {
      "footer": "Didn't ask for this? Contact [email protected]."
    }
  }
}

The subject is set by the backend, which sends it in the same language. It cannot be reworded from a module.

Every built-in email draws branding.logo.email.light from the app config, or branding.logo.default.light when no email logo is set (see Email Layout), with publicLogoOnly on. The logo shows only when a mail client can load it: a PNG or JPEG served from a public http(s) address. The default DMS logo is an SVG, which Gmail and Outlook refuse, so out of the box the emails carry no logo. To show yours, set an email logo in your layer's app/app.config.ts, and serve the dashboard from a public clientBaseUrl:

frontend-vue/app/app.config.ts
export default defineDmsAppConfig({
  branding: {
    logo: {
      email: {
        light: "/branding/logo-email.png",
        dark: "/branding/logo-email-dark.png",
      },
    },
  },
});

The loader hands every layer's app/app.config.ts to the email renderer too (@antelopejs/dms-frontend 0.6.0 or later), merged in module priority order, so a module registered with a priority above the DMS's (0) wins. dark is optional: when it passes the same check and differs from light, the header becomes a <picture> whose dark source the mail clients that follow the reader's dark mode pick, and the others draw light.

Replacing a built-in email

The render server looks a template up by its file name, in module priority order, and renders the first match. A module registered with a priority above the DMS's (0, see AddFrontendModule) therefore replaces a built-in email by shipping a component of the same name in its dms.email.ts:

frontend-vue/dms.email.ts
export const serverEmailTemplates = import.meta.glob("./app/emails/**/*.vue");
frontend-vue/app/emails/EmailResetPassword.vue
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import EmailLayout from "#dms-layout/app/emails/components/EmailLayout.vue";
import EmailOTP from "#dms-layout/app/emails/components/EmailOTP.vue";

defineProps<{ userName: string; resetCode: string; expiresInHours: number }>();

const { t, locale } = useI18n();
</script>

<template>
  <EmailLayout :title="t('emails.reset_password.title')" :lang="locale" public-logo-only>
    <EText>{{ t("emails.common.greeting", { name: userName }) }}</EText>
    <EText>Use this code to choose a new Acme password:</EText>
    <EmailOTP :code="resetCode" :expires-in="t('emails.common.hours', expiresInHours)" />
  </EmailLayout>
</template>

The replacement receives the props listed in Props and keeps the backend's subject. Do not register the name again with RegisterHtmlTemplate: the DMS already sends it. At equal priority the first module by id wins, so set the priority explicitly rather than relying on it.