---
id: custom-email-templates
title: Customize message templates for Ory Identity emails
sidebar_label: Email templates
---

# Email templates

```mdx-code-block
import Tabs from "@theme/Tabs"
import TabItem from "@theme/TabItem"
import CodeBlock from "@theme/CodeBlock"
```

Ory Identities comes with built-in templates for all messages sent by the system. You can replace the default templates with
custom ones that can carry your own branding, visual elements, and communication tone.

## Built-in templates

```mdx-code-block
<Tabs groupId="email-templates">
<TabItem value="recovery" label="Account recovery" default>
```

When you set the account verification method to `code`, the system uses the `recovery_code.valid` template to send the recovery
code to the user.

If you enabled sending attempted recovery notifications to unregistered addresses, the system uses the `recovery_code.invalid`
template.

If you set the account recovery method to `link`, the system uses these templates instead:

- `recovery.valid`
- `recovery.invalid`

To learn more about the recovery flow, read
[Account recovery and password reset](../self-service/flows/account-recovery-password-reset.mdx).

```mdx-code-block
</TabItem>
<TabItem value="verification" label="Address verification">
```

When you set the account verification method to `code`, the system uses the `verification_code.valid` template to send the
verification code and a URL to the user.

If you enabled notifications to unknown recipients, the `verification_code.invalid` template is used.

If you set the account verification method to `link`, the system uses these templates instead:

- `verification.valid`
- `verification.invalid`

To learn more about the verification flow, read
[Email and phone verification](../self-service/flows/verify-email-account-activation.mdx).

```mdx-code-block
</TabItem>

<TabItem value="login" label="Login code">
```

When you enable the one-time code method the login flow will need to send out an email to users signing in through the one-time
code method. The system will then use the `login_code.valid` template to send the login code to the user.

To learn more about login via a one-time code, read the [one-time code](../passwordless/07_code.mdx) documentation.

```mdx-code-block
</TabItem>

<TabItem value="registration" label="Registration code">
```

When you enable the one-time code method the registration flow will need to send out an email to users signing up using the
one-time code method. The system will then use the `registration_code.valid` template to send the registration code to the user.

To learn more about registration via a one-time code, read the [one-time code](../passwordless/07_code.mdx) documentation.

```mdx-code-block
</TabItem>
</Tabs>
```

## Using custom message templates

Templates can be customized to fit your own branding and requirements. If you don't customize a specific template, the system
automatically uses the built-in template.

A custom email server is required to use custom templates. Read more about
[custom SMTP and HTTP servers](./01_sending-emails-smtp.mdx).

```mdx-code-block
<Tabs>
<TabItem value="console" label="Ory Console" default>
```

1. Go to <ConsoleLink route="project.emailTemplates" />
2. Select the email template you want to customize.

The recovery & verification templates only show the versions for the method (**one-time code** or **link**) you have selected in
the flow configuration.

- <ConsoleLink route="project.recovery" />
- <ConsoleLink route="project.verification" />

```mdx-code-block
</TabItem>
<TabItem value="cli" label="Ory CLI" default>
```

1. List all your projects and get the Ory Identities config file:

   ```shell
   ## List all available workspaces
   ory list workspaces

   ## List all available projects
   ory list projects --workspace <workspace-id>

   ## Get config
   ory get identity-config --project <project-id> --workspace <workspace-id> --format yaml > identity-config.yaml
   ```

2. Encode your custom template to base64 using the following command:

   ```shell
   echo "Your custom template" | base64
   ```

   or https://www.base64encode.org/.

3. Add your Base64-encoded custom message templates to the config (don't forget to add base64:// before the encoded template):

   ```yaml title="identity-config.yaml"
   courier:
     smtp:
       from_name: MyProject via Ory
       templates:
         recovery:
           invalid:
             email:
               body:
                 html: "base64://ENCODED_HTML_TEMPLATE"
                 plaintext: "base64://ENCODED_PLAINTEXT_TEMPLATE"
           valid:
             email:
               body: {}
         verification:
           invalid:
             email:
               body: {}
           valid:
             email:
               body: {}
         login:
           valid:
             email:
               body: {}
         registration:
           valid:
             email:
               body: {}
   ```

4. Upload the edited config:

   ```shell
   ory update identity-config --project <project-id> --workspace <workspace-id> --file updated_config.yaml
   ```

:::danger

In the Ory Network, you can use only Base64-encoded templates. `http://` or `file://` URIs are not supported. If you provide
custom templates using unsupported methods, the system uses the default templates.

:::

```mdx-code-block
</TabItem>
</Tabs>
```

## Creating templates

Templates use the Go template engine in the `text/template` package for rendering the `email.subject.gotmpl` and
`email.body.plaintext.gotmpl` templates, and the `html/template` package for rendering the `email.body.gotmpl` template.

Learn more:

- https://pkg.go.dev/text/template
- https://pkg.go.dev/html/template

<a name="spring-disabled"></a>

:::tip

Templates can use the [Sprig](https://github.com/Masterminds/sprig) library, which provides
[more than 100 commonly used template functions](http://masterminds.github.io/sprig/)

:::

:::danger

For security reasons, these Sprig functions are disabled in the Ory Network:

- Date functions: `date`,`date_in_zone`,`date_modify`, `now`, `htmlDate`, `htmlDateInZone`, `dateInZone`, `dateModify`
- Strings: `randAlphaNum`, `randAlpha`, `randAscii`, `randNumeric`, `uuidv4`
- OS: `env`, `expandenv`
- Network: `getHostByName`

:::

### Available variables

The variables available for use in email templates change depending on the flow and the selected method:

```mdx-code-block
<Tabs groupId="email-templates">
<TabItem value="recovery_code" label="Recovery (via code)" default>

For the `recovery_code.valid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` | The email address the email will be sent to |
| `RecoveryCode` | The recovery code |
| `Identity` | The identity to be recovered |
| `ExpiresInMinutes` | the expiration time of the code in minutes |

:::note

The `recovery_code.invalid` template does not allow to send a direct link to the user, as the recovery flow enforces anti-CSRF measures, which would lead to the flow failing, in case the user opens the link in a different browser.

:::

For the `recovery_code.invalid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` | the email address the email will be sent to |

</TabItem>
<TabItem value="verification_code" label="Verification (via code)">

For the `verification_code.valid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` |  the email address the email will be sent to |
| `VerificationCode` | the verification code |
| `VerificationURL` | the verification link |
| `Identity` | the identity of the email address |
| `ExpiresInMinutes` | the expiration time of the code in minutes |

For the `verification_code.invalid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` | the email address the email will be sent to |

</TabItem>
<TabItem value="recovery" label="Recovery (via magic link)">

For the `recovery.valid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` | The email address the email will be sent to |
| `RecoveryURL` | The recovery link |
| `Identity` | The identity to be recovered |
| `ExpiresInMinutes` | the expiration time of the link in minutes |

For the `recovery.invalid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` | the email address the email will be sent to |

</TabItem>
<TabItem value="verification" label="Verification (via magic link)">

For the `verification.valid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` |  the email address the email will be sent to |
| `VerificationURL` | the verification link |
| `Identity` | the identity of the email address |
| `ExpiresInMinutes` | the expiration time of the link in minutes |

For the `verification.invalid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` | the email address the email will be sent to |

</TabItem>
<TabItem value="login_code" label="Login (via code)">

For the `login_code.valid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` |  the email address to whom the email is sent |
| `LoginCode` | the login code |
| `Identity` | the identity of the email address |
| `ExpiresInMinutes` | the expiration time of the code in minutes |

</TabItem>

<TabItem value="registration_code" label="Registration (via code)">

For the `registration_code.valid` template, the following variables are available:

| Variable | Description |
| --- | --- |
| `To` | the email address to whom the email is sent |
| `RegistrationCode` | the registration code |
| `Traits` | the provided traits as specified by the Identity Schema |
| `ExpiresInMinutes` | the expiration time of the code in minutes |

</TabItem>
</Tabs>
```

### Mandatory template formats

Each template must have two versions: `html` and `plaintext`.

- `html` version uses the HTML syntax to achieve the desired look and functionality (such as clickable buttons) of the email
  message.
- `plaintext` version can't contain any HTML. Must contain only plain text content and any necessary `gotmpl` logic. This version
  is used as a fallback when the `html` version cannot be delivered, for example when the user's mail server blocks HTML in all
  incoming messages.

````mdx-code-block
<Tabs>
<TabItem value="html" label="HTML" default>

```gotmpl title="courier/template/templates/verification/valid/email.body.gotmpl"
Hi, please verify your account by clicking the following link:

<a href="{{ .VerificationURL }}">{{ .VerificationURL }}</a>
```

</TabItem>
<TabItem value="plaintext" label="Plain text">

The `plaintext` version doesn't contain any HTML and keeps only the gotmpl logic (`{{ .VerificationURL }}`) and plain text.

```gotmp title="courier/template/templates/verification/valid/email.body.plaintext.gotmpl"
Hi, please verify your account by clicking the following link: {{ .VerificationURL }}
```

</TabItem>
</Tabs>
````

## Customizing template content for specific users

To enable customizing the content of templates based on the identity of the recipient of the email, the `Identity` object is
available inside the templates. This object is a map containing all the attributes of an identity defined in the identity schema,
such as `id`, `state`, `recovery_addresses`, `verifiable_addresses` and `traits`.

:::tip

Read [this document](../manage-identities/overview) to learn more about the Ory Identity and the identity schema.

:::

### Translated templates (i18n)

You can use nested templates to render `email.subject.gotmpl`, `email.body.gotmpl` and `email.body.plaintext.gotmpl` templates
based on user settings, for example based on their chosen language.

To enable i18n customization of templates, customize the identity schema to include the user's preferred communication language.
For example:

```json title="Sample custom Identity Schema with user language"
{
  // ...
  "properties": {
    "traits": {
      "type": "object",
      "properties": {
        // ...
        // highlight-start
        "lang": {
          "type": "string",
          "title": "Your preferred language"
      },
      // highlight-end
      "required": [
        "email"
      ],
      "additionalProperties": false,
    }
  }
}
```

This identity trait can then be used inside the template to render a section conditionally.

The following example defines various templates for the `recovery_code.valid` template and renders the respective template
depending on the language set in the `lang` identity traits, that was defined above:

```gotmpl title="recovery_code/valid/email.body.gotmpl"
{{define "en"}}
Hi,

Please enter the following code to recover your account:

{{ .RecoveryCode }}
{{end}}

{{define "fr"}}
Bonjour,

Veuillez entrer le code suivant pour récupérer votre compte:

{{ .RecoveryCode }}
{{end}}

{{define "de"}}
Hallo,

Bitte geben Sie den folgenden Code ein, um Ihr Konto wiederherzustellen:

{{ .RecoveryURL }}
{{end}}

{{- else if eq .Identity.traits.lang "fr" -}}
{{ template "fr" . }}
{{- else if eq .Identity.traits.lang "de" -}}
{{ template "de" . }}
{{- else -}}
{{ template "en" . }}
{{- end -}}
```

:::tip

You can use Sprig functions in the nested templates. For security reasons, some functions are disabled in the Ory Network.
[See the list of disabled functions here.](#creating-templates)

:::

### Metadata in templates

As an administrator, you can set identity metadata, such as the user's language, in your application code using Identity metadata
property. Read [this document](../manage-identities/10_managing-users-identities-metadata.mdx#metadata) to learn more.

You can access `metadata_public` through `.Identity.metadata_public` in email templates.

The following example requires that the field `lang` is set in the public metadata. Your application could set this value after
user completes registration.

```gotmpl title="recovery_code/valid/email.body.gotmpl"
{{define "en"}}
Hi,

Please enter the following code to recover your account:

{{ .RecoveryCode }}
{{end}}

{{define "fr"}}
Bonjour,

Veuillez entrer le code suivant pour récupérer votre compte:

{{ .RecoveryCode }}
{{end}}

{{define "de"}}
Hallo,

Bitte geben Sie den folgenden Code ein, um Ihr Konto wiederherzustellen:

{{ .RecoveryURL }}
{{end}}

{{- if eq .Identity.metadata_public.lang "fr" -}}
{{ template "fr" . }}
{{- else if eq .Identity.metadata_public.lang "de" -}}
{{ template "de" . }}
{{- else -}}
{{ template "en" . }}
{{- end -}}
```

:::danger

Since metadata is not validated by Ory Identities, missing entries or unexpected values can cause errors in the template rendering
process. If the system encounters errors in the rendering process, Ory Identities uses the default templates.

:::

### Transient payload in templates

The transient payload allows you to pass additional data along with certain self-service flows. Transient payloads can be used for
passing temporary information to your email template without storing it permanently.

The following `example_key` transient payload

```
...
    traits: {
      email: "user@example.com",
    },
    transient_payload: {
      example_key: "This is an example value"
    },
...
```

can be accessed in the email template using `{{index .TransientPayload "example_key"}}`

```gotmpl

<h1>Recovery Details</h1>
<p>To: {{.To}}</p>
<p>Recovery URL: {{.RecoveryURL}}</p>
<p>Transient Payload: {{index .TransientPayload "example_key"}}</p>
<p>
  {{- if eq (index .TransientPayload "lang") "foo" -}}
  FOO
  {{- else -}}
  BAR
  {{- end -}}
</p>
```

This results into the following email to be sent to the user.

```gotmpl
<h1>Recovery Details</h1>
<p>To: user@example.com</p>
<p>Recovery URL: https://example.com/recover</p>
<p>Transient Payload: This is an example value</p>
<p>
  BAR
</p>
```
