> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/platform/mail-and-notifications.md).

# Mail and notifications

Configure SMTP delivery from a shipped mailbox template, test it safely in simulation mode, and change what a notification says.

## What this is, and when you need it

Mail in OpenIAM is two separate configurations that people routinely conflate. **Delivery** is a *mailbox configuration* object: an SMTP account plus a *mailbox configuration template* holding the transport settings. **Content** is a `MAIL_TEMPLATE` row: a Freemarker body rendered with notification parameters supplied by whichever service triggered the send. Changing one never fixes a problem in the other.

This page configures delivery and proves it. Changing what a notification *says* is the [Notification templates](#notification-templates) section further down, and it is a different job with its own screen.

## At a glance

|                          |                                                                                                                                                                                                                                                 |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | A mailbox configuration that sends real OpenIAM notifications, proved first in simulation mode so no real user is mailed                                                                                                                        |
| **Where**                | **Administration → Mailbox Configurations** (`/webconsole/mailbox-configuration`); transport settings on **Administration → Mailbox Configurations → Mailbox Configuration Templates**; notification bodies at `/webconsole/mailTemplateEditor` |
| **You need first**       | An SMTP account and password, or an Entra app registration with `<TENANT_ID>` and `<CLIENT_ID>`; one mailbox you can read; the `email-manager` service running                                                                                  |
| **Takes**                | 20 to 40 minutes, including the simulated and then real test sends                                                                                                                                                                              |
| **Reversible**           | Yes. Deactivate or delete the configuration and the previous rotation resumes. Mail already sent cannot be recalled, which is what simulation mode is for                                                                                       |

## Before you begin

* **An SMTP account and its password**, or — for Microsoft 365 modern auth — an Entra app registration with a client secret or a PFX client certificate, and its `<TENANT_ID>` and `<CLIENT_ID>`.
* **One mailbox address you can read**, for simulation mode. Everything the system would have sent to real users arrives there instead.
* **The `email-manager` service running.** It owns both sending and the inbox sweep; the Webconsole only queues messages.
* **Do not hand-build a transport template.** Four vendor templates ship — `GMAIL_MBT`, `OFFICE365_MBT`, `SENDGRID_MBT` and `SENDINBLUE_MBT` — and one of them almost certainly matches your provider. Their hosts, ports and socket settings are in [The four shipped mailbox configuration templates](#the-four-shipped-mailbox-configuration-templates) below; the one real decision between them, STARTTLS against implicit TLS, is the section after that.

## Configure SMTP delivery, simulated first

{% stepper %}
{% step %}

### Confirm the template you are about to reuse

**Administration → Mailbox Configurations → Mailbox Configuration Templates**. Open **Office 365 SMTP Relay** (`OFFICE365_MBT`) and check that **SMTP Hostname** is `smtp.office365.com`, **SMTP Port** is `587`, **Enable STARTTLS?** is on and **Socket factory class** is empty. The IMAP host is on the field labelled **Incoming mail hostname**. Do not edit the template — it is shared by every configuration that points at it.
{% endstep %}

{% step %}

### Create the mailbox configuration in simulation mode

**Administration → Mailbox Configurations → New**. Set:

* **Name**: `O365 primary`
* **Mailbox Configuration Template**: Office 365 SMTP Relay
* **Default sender address**: `<TEST_USER>@example.com` — the mailbox that will authenticate
* **Default subject prefix**: `[OpenIAM]` — required by the validator
* **SMTP Username** / **SMTP Password**: the account credentials, or leave blank and set **Modern Authentication** with **Client Authentication Type** `clientSecret`, authority `https://login.microsoftonline.com/<TENANT_ID>` and scope `https://outlook.office365.com/.default`
* **Simulation Mode**: on, then **Default Receiver**: your own address (the field appears only after you tick the box, and is required)
* **Is Active**: on. OpenIAM round-robins across *every* active configuration that is not somebody else's failover, so a second active configuration means alternating senders — see [Which configuration is used](#which-configuration-is-used)
* **Read Inbox?**: off for now

Save. The password goes to `/secret/openiam/vault.secret.mailbox.<MB_ID>.password`; the row keeps only a mask.
{% endstep %}

{% step %}

### Send a test message

Expand **Send Test Email** on the configuration screen, fill **Email Address**, **Subject** and **Message** (and **Is HTML?** if you want an HTML body), and send. Success reports "Test email sent successfully"; a failure reports "Unable to send email to the specified user" with the root cause attached.

Two things about this panel. The screen warns, correctly, that *sending a test email does not save your configuration* — save first. And when simulation mode is on, the test sends to the address you typed rather than to **Default Receiver**, so it exercises the transport without disturbing your simulation target.
{% endstep %}

{% step %}

### Trigger one real notification, still simulated

Reset a test user's password with **Send Password via Email** selected (`org.openiam.ui.user.reset.password.option.sendByEmail.defaultValue` ships `true`, so it is already ticked). The mail arrives in *your* inbox because of simulation mode, addressed as it would have been. Read it: this is the only way to see the real parameter set, since the parameters exist only on the live send path.
{% endstep %}

{% step %}

### Turn simulation off, and only then widen

Clear **Simulation Mode**, save, reopen the record and confirm it is clear. Repeat the same password reset and confirm the real user receives it. Leave **Default Receiver** populated — it is inert once simulation is off, and you will want it next time.
{% endstep %}
{% endstepper %}

## Verification

A green message in the Webconsole means the message was handed to RabbitMQ. What proves a send is the audit trail written by `email-manager`:

| Evidence                                           | What it means                                                                                                                                                            |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `SEND_EMAIL_NOTIFICATION` succeeded                | The message reached the SMTP server. The event's description is the subject, and its info line carries `To`, `From`, `Subject`, `CC`, `BCC`, `Attachment` and `Sent at`. |
| `SEND_EMAIL_NOTIFICATION` failed                   | The SMTP transport threw. If the configuration has a failover, a second attempt is logged against it.                                                                    |
| `CUSTOMIZE_EMAIL_CONTENT`                          | A **Script to customize MimeMessage** ran on this send.                                                                                                                  |
| `MAKE_DECISION_FROM_EMAIL`, source `EMAIL_MANAGER` | An inbound reply was processed by the inbox sweep. Failures record the reason — `NOT_RECOGNIZED_INCOMING_EMAIL` for a subject the processor does not handle.             |

Retention for each of these is a `SystemAuditLog` property; see [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md).

## When it goes wrong

| Symptom                                                                                    | Inspect                                                                                                                                                                               | Fix                                                                                                                                                                                                                              |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Nothing sends; `NO_ACTIVE_EMAIL_CONFIGS` in the ESB log                                    | The `ACTIVE` flag on every mailbox configuration, and whether the only active one is nominated as somebody's failover                                                                 | Failover configurations are excluded from the rotation. Mark a non-failover configuration active.                                                                                                                                |
| Connection failure on port `587`                                                           | `SMTP_SOCKET_CLASS` on the template                                                                                                                                                   | Must be empty on a STARTTLS port. Clear it, or use the shipped `OFFICE365_MBT` row unmodified.                                                                                                                                   |
| Authentication rejected against Office 365                                                 | `MODERN_AUTH`, `AUTH_METHOD`, `AUTHORITY`, `SCOPE`, and `vault.secret.mailbox.<MB_ID>.clientSecret`                                                                                   | Basic SMTP AUTH is disabled on most tenants. Set modern auth with `clientSecret` or `clientCertificate`.                                                                                                                         |
| Mail sends but goes to one address                                                         | `SIMULATION_MODE` and `DEFAULT_RECEIVER`                                                                                                                                              | Simulation mode is still on.                                                                                                                                                                                                     |
| Simulation mode on but real users received mail                                            | `DEFAULT_RECEIVER` on the stored record                                                                                                                                               | The substitution is guarded on both fields, and the API can save the flag without a receiver. Populate it.                                                                                                                       |
| The test message arrives but recovery, activation and OTP mail never does                  | Whether a `SEND_EMAIL_NOTIFICATION` event exists at all. **Send Test Email** carries the configuration on screen and renders no template, so it proves the transport and nothing else | Transport is fine; look at the notification template's `IS_ENABLED` and name, at the recipient's active address, and at which configuration the rotation actually used                                                           |
| Password change works, no email arrives                                                    | `MAIL_TEMPLATE.IS_ENABLED`, and `TEMPLATE_NAME` still matching the notification type                                                                                                  | A disabled or renamed template sends nothing; look for `is disabled, email will not be sent` or `invalid notificationType` in the email-manager log.                                                                             |
| A specific user never receives any notification, others do                                 | Whether that user has an **active** email address at all — OpenIAM falls back from default-and-active to any active address, then gives up                                            | Look for `Send notification failed. Email was null for userId=` in the `email-manager` log — there is no audit failure for this case. See [What a user with no email address misses](#what-a-user-with-no-email-address-misses). |
| An approval or certification sits untouched and nobody was told                            | The approver or reviewer has no email address. The task exists, the send was abandoned, and `SEND_EMAIL_NOTIFICATION` was never attempted                                             | Give the approver an active address, then reissue. Audit approver populations for missing addresses rather than relying on complaints.                                                                                           |
| Email OTP fails with `EMAIL_REQUIRED` and the message `Email is not set to send the token` | The user has no active email address, so the attempt is rejected before any send                                                                                                      | Set an address, or move that user to a different OTP method — see [SMS and one-time passwords](/administration-guide/platform/sms-and-otp.md).                                                                                   |
| Email arrives with a blank value where a name should be                                    | The `MAIL_TEMPLATE_PROPERTIES` rows for that template ID, and the parameter name in the body                                                                                          | An undeclared or misspelled parameter renders empty. Match `REFERENCE_ID` exactly.                                                                                                                                               |
| Reply-to-approve had no effect                                                             | `ACTIVE_INBOX` on the configuration, then `org.openiam.email.inbox.sweep` (`900000` ms)                                                                                               | Enable **Read Inbox?**; then wait one sweep, or lower the interval.                                                                                                                                                              |
| Credentials changed in the vault but the old ones are still used                           | `vault.secret.mailbox.<MB_ID>.password` — the key is the configuration ID, not its name                                                                                               | Write against the right ID, then save the configuration to refresh the round-robin storage.                                                                                                                                      |

## The four shipped mailbox configuration templates

The transport settings belong to the *template*, not to the mailbox configuration. Four vendor templates have been seeded into `MAILBOX_CONFIG_TEMPLATE` since `4.2.1.1`. Confirm all four exist on your system at **Administration → Mailbox Configurations → Mailbox Configuration Templates** before building a fifth.

| `CONFIG_ID`      | Name                  | SMTP host                   | Port  | STARTTLS | Socket class                     | Socket fallback | IMAP host               | IMAP port | Inbox folder |
| ---------------- | --------------------- | --------------------------- | ----- | -------- | -------------------------------- | --------------- | ----------------------- | --------- | ------------ |
| `GMAIL_MBT`      | G-Mail SMTP Relay     | `smtp.gmail.com`            | `465` | `Y`      | `javax.net.ssl.SSLSocketFactory` | `Y`             | `imap.gmail.com`        | `993`     | `INBOX`      |
| `OFFICE365_MBT`  | Office 365 SMTP Relay | `smtp.office365.com`        | `587` | `Y`      | *(null)*                         | `N`             | `outlook.office365.com` | `993`     | `INBOX`      |
| `SENDGRID_MBT`   | SendGrid SMTP Relay   | `smtp.sendgrid.net`         | `465` | `Y`      | `javax.net.ssl.SSLSocketFactory` | `Y`             | *(null)*                | *(null)*  | *(null)*     |
| `SENDINBLUE_MBT` | SendInBlue SMTP Relay | `smtp-relay.sendinblue.com` | `587` | `N`      | `javax.net.ssl.SSLSocketFactory` | `N`             | *(null)*                | *(null)*  | *(null)*     |

All four ship with `SMTP_IS_AUTH = 'Y'`, and the socket factory port is `465` wherever a socket class is set. `OFFICE365_MBT` was first seeded with `SMTP_IS_STARTTLS = 'N'` and corrected to `'Y'` later in the same `4.2.1.1` release, so on a system upgraded through that release the row can still be wrong. Open the template and confirm **Enable STARTTLS?** is ticked before blaming the mail server.

A fifth row, `MIGRATED_MBT`, with the mailbox configuration `MIGRATED_MB` ("Current Migrated"), is built during the upgrade to `4.2.1.1` from whatever the `Gateway` property tab held at the time. It is not a vendor template; it is your old single SMTP account carried forward. If it is present, your deployment was upgraded rather than installed fresh — that is the only place this distinction is visible from the Webconsole.

### STARTTLS versus implicit TLS: the actual decision

This is the one real choice between the Google and Office 365 rows, and it is the difference between two ports, not two vendors.

**`GMAIL_MBT` uses implicit TLS.** Port `465`, `SMTP_SOCKET_CLASS = javax.net.ssl.SSLSocketFactory`, socket port `465`. OpenIAM passes both of those straight into the mail session as `mail.smtp.socketFactory.class` and `mail.smtp.socketFactory.port`, so the connection is encrypted from the first byte. STARTTLS is set as well but has nothing left to upgrade.

**`OFFICE365_MBT` uses explicit TLS.** Port `587`, `SMTP_IS_STARTTLS = 'Y'`, and — deliberately — no socket class and `SMTP_SOCKET_FALLBACK = 'N'`. The session starts in plaintext and issues `STARTTLS`. If you copy the Google row and only change the host and port, you get an SSL socket factory attempting an implicit handshake against a port that expects plaintext, and the send fails at connect time with no application-level error to read. Leave `SMTP_SOCKET_CLASS` empty on any port-587 template.

## The fields on a mailbox configuration

The transport settings above belong to the template. Everything below is on the mailbox configuration itself, the `MAILBOX_CONFIG` row, and several of these were never documented.

| Column                   | Screen label                                                | Notes                                                                                                                                      |
| ------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `NAME`                   | Name                                                        | Required.                                                                                                                                  |
| `DEFAULT_SENDER`         | Default sender address (typically the same as the username) | Required. Used as `From` when the sending service does not set one.                                                                        |
| `DEFAULT_SUBJECT_PREFIX` | Default subject prefix                                      | Required by the Webconsole validator, though the column is nullable. Prepended to every subject.                                           |
| `BCC_RECIPIENT`          | Optional BCC address                                        | Added to every message, including simulated ones.                                                                                          |
| `MB_CONFIG_ID`           | Mailbox Configuration Template                              | Required. The template row above.                                                                                                          |
| `FAILOVER_MB_ID`         | Failover Mailbox Configuration                              | Another mailbox configuration, retried once if the send throws. Circular chains are rejected.                                              |
| `ACTIVE`                 | Is Active                                                   | See "which configuration is used" below.                                                                                                   |
| `ACTIVE_INBOX`           | Read Inbox?                                                 | Defaults `N`. Only configurations with this set are swept for inbound decisions.                                                           |
| `SIMULATION_MODE`        | Simulation Mode                                             | See below.                                                                                                                                 |
| `DEFAULT_RECEIVER`       | Default Receiver                                            | The single address simulation mode redirects to. The field only appears once Simulation Mode is ticked.                                    |
| `CUSTOM_HNDLR`           | Script to customize MimeMessage                             | A Groovy script implementing `CustomMailConfigHelper.customizeMessage(MimeMessage)`, run on every send.                                    |
| `MODERN_AUTH`            | Modern Authentication                                       | A two-button toggle against **Basic Authentication**. Switches the sender from SMTP AUTH to OAuth 2.0 client credentials.                  |
| `AUTH_METHOD`            | Client Authentication Type                                  | `clientSecret` or `clientCertificate` (from the `AuthMethod` enum; `NONE` is the empty string). Required once Modern Authentication is on. |
| `AUTHORITY`              | Authority                                                   | The token endpoint authority, e.g. `https://login.microsoftonline.com/<TENANT_ID>`.                                                        |
| `SCOPE`                  | Scope                                                       | The single scope requested, e.g. `https://outlook.office365.com/.default`.                                                                 |

With `MODERN_AUTH` set, OpenIAM acquires an OAuth 2.0 client-credentials token from the **Authority** you give it, using either the client secret or a PFX certificate held in the vault, then opens the SMTP session with `mail.smtp.auth.mechanisms=XOAUTH2`, `mail.smtp.auth.login.disable=true` and `mail.smtp.auth.plain.disable=true`. STARTTLS is forced on in that path regardless of what the template says, so a template with STARTTLS off still negotiates it once modern authentication is on.

### Which configuration is used

Not "the active one". OpenIAM keeps a round-robin list of **every active configuration that is not referenced as another configuration's failover**, and each send takes the next entry. The list is rebuilt when a configuration is saved, which is why saving an unrelated configuration can change which account a message leaves from. Two active configurations means alternating senders. A configuration nominated as a failover is excluded from the rotation and used only on retry. With no active configuration at all, sends fail with `NO_ACTIVE_EMAIL_CONFIGS`.

### Simulation mode is the safe way to test

When `SIMULATION_MODE` is on **and** `DEFAULT_RECEIVER` is non-empty, OpenIAM replaces the `To`, and any `Cc` and `Bcc`, with the default receiver as it builds each message. Real users receive nothing; you receive everything, with the original subject and body.

{% hint style="warning" %}
The substitution is guarded on *both* fields: simulation mode with an empty default receiver mails the real recipients. The edit screen prevents that — it only shows **Default Receiver** once **Simulation Mode** is ticked, and refuses to save without it ("Default receiver is required") — but a record that reached the database by any other route, such as an import or a break-glass write, can carry the flag without a receiver. Check the stored record, not just the form. Note also that the configuration's **Optional BCC address** is added *after* the substitution, so that address still receives a copy.
{% endhint %}

## The Gateway property tab, and which one wins

The `Gateway` category on **Administration → System Configuration** is the pre-4.2.1.1 single SMTP account: 16 properties, all `org.openiam.smtp.*`, listed with their shipped defaults in [System properties](/administration-guide/reference/system-properties.md#gateway). The tab label now says deprecated.

**The mailbox configuration wins.** Nothing on the send path reads these transport keys any more: the upgrade to `4.2.1.1` read them once to build `MIGRATED_MBT`, and since then both the sender and the inbox sweep take host, port, STARTTLS, socket factory, IMAP host, IMAP port and inbox folder from the template of the active configuration. The one key still read at runtime is `org.openiam.smtp.mail.defaultSender`, used as the `From` address for report mails, user exports and workflow notifications. Editing the rest of this tab changes nothing, and nothing on screen will tell you so — the observable check is to change a value here, send a test message, and read the received headers: they will still show the template's server.

See [Where settings live](/administration-guide/start/where-settings-live.md) for why the Gateway tab is easy to mistake for the Access Gateway, and [System configuration](/administration-guide/platform/system-configuration.md) for the tab itself.

## Where the credentials are stored

Neither the property row nor the `MAILBOX_CONFIG` row holds a password.

**Per mailbox configuration**, secrets are written under `<vault path>vault.secret.mailbox.<MB_ID>.<suffix>`, where the vault path resolves to `/secret/openiam/` by default. The suffixes are `username`, `password`, `clientId`, `tenantId`, `clientSecret`, `authority`, `clientCertificate` and `clientCertificatePassword`. The key is the **mailbox configuration ID**, so renaming a configuration is safe but recreating it under a new ID orphans its secret.

**Legacy Gateway keys** are `/secret/openiam/vault.secret.mail.username` and `/secret/openiam/vault.secret.mail.password`. Saving the Gateway tab writes the typed value straight to the vault and then blanks the property value, which is why the screen shows a mask and never the secret. On the first ESB start after an upgrade to `4.2.1.1`, OpenIAM copies these two paths into `vault.secret.mailbox.MIGRATED_MB.username` and `.password` and deletes the old ones, so a runbook that still writes the legacy paths is writing secrets nothing reads.

## The service that sends mail, and why the inbox lagged

`email-manager` has exactly two timings. Neither appears on **Administration → System Configuration**: they are defaults built into the service and can only be changed as container overrides, which makes them the deployment owner's to set — see the [Installation Guide](/installation/installation.md). You cannot read their current values from the Webconsole; ask whoever owns the deployment, or infer them from behaviour as described below.

| Property                        | Ships    | Effect                                                                                                                                                                                                                                                                            |
| ------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `org.openiam.email.inbox.sweep` | `900000` | Milliseconds between IMAP sweeps — **15 minutes**. The delay is measured from the *end* of the previous sweep, not from its start, and one node at a time holds the sweep, so a slow sweep pushes the next one out rather than overlapping it.                                    |
| `org.openiam.mail.send.rate`    | `100`    | Milliseconds the outbound listener pauses after each send, throttling mail to roughly 10 per second. The figure was chosen for Office 365's cap of about 30 messages a minute; raising it slows delivery of a large batch, lowering it risks the provider throttling you instead. |

{% hint style="info" %}
**"Why was my approve-by-email reply ignored?"** It probably was not — it was queued. The sweep runs every 15 minutes by fixed delay after the previous run finishes, and only over configurations with **Read Inbox?** set. Wait one interval before investigating, or lower `org.openiam.email.inbox.sweep`. In a container, override it as `OPENIAM_PROP_org_openiam_email_inbox_sweep`.
{% endhint %}

Two things about how `email-manager` is deployed will reach you as an administrator, even though changing them does not.

**It ships logging at `trace`.** The service's shipped deployment sets its log level to `trace`, which is a debugging default rather than a production one. The consequence you will see is log volume: an `email-manager` log that is unreadably verbose, and grows fast, is the shipped setting rather than a fault. Lowering it is a deployment change — the [Installation Guide](/installation/installation.md) — and worth raising before you go live.

**Four inbox settings in that deployment are inert.** `INBOX_FOLDER_NAME`, `INBOX_PORT`, `INBOX_SSL_ENABLE` and `INCOMING_MAIL_HOST` are set alongside the real overrides but are read by nothing in this release, because they are not property overrides — they are plain variables with no matching property. Setting or correcting them will not change where OpenIAM reads mail from. **IMAP settings come from the mailbox configuration template**, which is the only place changing them has an effect, and the only place you can confirm them.

## Notification templates

OpenIAM ships 213 `MAIL_TEMPLATE` rows. The full inventory is in [Shipped mail templates](/administration-guide/reference/shipped-mail-templates.md) — find the template the feature already sends and edit that, rather than creating one nothing triggers.

The model is worth understanding before you edit anything:

* **The body is Freemarker**, read from `MAIL_TEMPLATE.CONTENT`, with `CAPTION` rendered the same way to produce the subject and `IS_HTML` deciding the MIME type.
* **The template is found by `TEMPLATE_NAME`, not by ID.** OpenIAM looks for a name exactly matching the notification type the sending feature asked for, so a renamed template stops being found and the `email-manager` log says only `Mail not sent, invalid notificationType`.
* **`IS_ENABLED = 'N'` makes the send a no-op**, logged as `Template for notification type '<name>' is disabled, email will not be sent` and reported to the caller as a plain `false`.
* **Values come from the sending service** as notification parameters, referenced as `${req.getNotificationParam("<NAME>")?.valueObj}`.
* **`MAIL_TEMPLATE_PROPERTIES` declares which parameter names a template may use.** Each row carries `MAIL_TEMPLATE_ID`, `REFERENCE_ID` (the parameter name), `PROPERTY_NAME` (the label in the editor's insert list), `PROPERTY_VALUE` (the snippet inserted) and `VALUE_IS_SCRIPT`.
* **An undeclared parameter renders empty.** It resolves to null and disappears; there is no error and nothing in the log. A template that mails a blank line where a value should be is almost always a parameter name that the sending service does not supply.

{% hint style="danger" %}
**`NEW_USER_EMAIL_SUPERVISOR` (template ID `11`) mails the generated password in clear text.** Its shipped body is:

`The login Id is: ${req.getNotificationParam("identity").valueObj}` / `The password is: ${req.getNotificationParam("password").valueObj}`

Enable supervisor notification on user creation and you are emailing working credentials, to the supervisor rather than the user, in plaintext, with no expiry. Fix it by deleting the password line from the body and relying on the activation flow instead: `org.openiam.send.user.activation.link` ships `true` and `NEW_USER_ACTIVATION_NOTIFICATION` (template ID `139`) already exists to send a one-time link.
{% endhint %}

#### Nothing sends it unless a script sets one flag

`NEW_USER_EMAIL_SUPERVISOR` has no schedule, no batch task and no configuration checkbox behind it on the managed-system or policy side. OpenIAM sends it in exactly one situation, and every one of these four conditions has to hold at the same time:

1. The user object being saved carries `emailCredentialsToSupervisor` set to `true`. It defaults to `false`.
2. It is a **create**, not an update. A user who already has an ID never triggers this mail, however the flag is set.
3. The save succeeded.
4. The user carries at least one supervisor, and that supervisor resolves to a real user record.

Two things set the flag. The Webconsole's own new-user form sets it from the **Notify supervisor via email** checkbox, and a script can set it on the object it is building:

{% code overflow="wrap" %}

```groovy
pUser.setEmailCredentialsToSupervisor(true)
```

{% endcode %}

Set it in the transformation script that builds the user, and make sure the same script has populated the supervisor collection — the flag alone does nothing when there is no supervisor, and OpenIAM takes that path without logging anything or failing the save. There is no system property equivalent and no audit action for the flag itself; the evidence that it worked is a `SEND_EMAIL_NOTIFICATION` audit event whose subject matches the template's caption, and the evidence that it did not is the absence of one. If the mail is not arriving from a bulk load, check the flag and the supervisor first, not the template.

{% hint style="danger" %}
**Think hardest about this flag in a bulk import.** OpenIAM decrypts each new user's password and puts the cleartext into the notification parameters, so turning it on for a load of a thousand joiners mails a thousand working passwords to a set of managers, where they sit in mailboxes and archives indefinitely. Apply the fix above to the template body first. A supervisor notification that says "an account has been created for X, they will receive an activation link" is almost always what was wanted.
{% endhint %}

### The OTP email template

`OTP_CODE` is the email OTP body, shipped since `4.1.8.0`. Its parameter is declared as the `MAIL_TEMPLATE_PROPERTIES` row `OTP_CODE_EMAIL_TOKEN` with `REFERENCE_ID = 'EMAIL_TOKEN'`, and the one-time code is inserted into the body with `${req.getNotificationParam("EMAIL_TOKEN")?.valueObj}`.

As of `4.2.2.0` both subject and body use four parameters — `EMAIL_TOKEN`, `identity`, `companyName` and `managedSystemName` — and the email OTP path supplies exactly those four and no others. `companyName` comes from `org.openiam.ui.page.title.organization.name`, which ships empty, so the closing line of a stock OTP email is blank until you set that property. See [SMS and one-time passwords](/administration-guide/platform/sms-and-otp.md).

## What a user with no email address misses

Nothing in OpenIAM requires a user to have an email address, and a synchronization that stops supplying one is a supported configuration — see [Editing a shipped transformation script](/administration-guide/automation/synchronization-scripts.md#editing-a-shipped-transformation-script-removing-the-email-and-upn-block). What matters operationally is the list of things that then do not happen, because almost all of them fail quietly.

Every dependency below resolves the address the same way: OpenIAM takes the first email that is **both** default and active; failing that, the first **active** email; failing that, nothing at all. So "no email" in this table means no active address, not merely no default one — a user whose only address is marked inactive is, for every row below, a user with no email address. Check the **Active** flag on the address, not just its presence.

| Dependency                                       | Behaviour with no address                                                                                                                                      | Evidence it left behind                                                                                                                                                                                                                                                                 |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Any template notification** (`email-manager`)  | The address is validated first, so the send is abandoned before the template is even fetched                                                                   | One line in the `email-manager` log: `Send notification failed. Email was null for userId=%s`. **No audit failure** — nothing in the Webconsole.                                                                                                                                        |
| **Admin-initiated password reset**               | The reset itself succeeds; the notification branch is guarded and simply not entered                                                                           | **Nothing at all** — no log line, no audit entry. The administrator sees a successful reset.                                                                                                                                                                                            |
| **Email OTP / MFA**                              | Hard failure: the recipient address resolves to nothing and the attempt is rejected with `EMAIL_REQUIRED` and the message `Email is not set to send the token` | The user **cannot authenticate** by that method. This is the one dependency that fails loudly.                                                                                                                                                                                          |
| **Self-service OTP enrolment**                   | The email option is simply absent from the enrolment screen                                                                                                    | Nothing. The user never sees the choice, and there is no message explaining why.                                                                                                                                                                                                        |
| **Certification, SoD and workflow report mails** | OpenIAM resolves the address, finds none, and skips the send                                                                                                   | **Nothing.** No log, no audit entry, no indication the report was generated and never delivered.                                                                                                                                                                                        |
| **Workflow request report**                      | Abandoned before the send                                                                                                                                      | `Cannot send workflow report email: no email address for user …` — a warning, and only in the workflow service log.                                                                                                                                                                     |
| **Password recovery by email lookup**            | Rejected with `EMAIL_REQUIRED` for a blank address, or `NO_USER_FOUND_FOR_GIVEN_EMAIL` when no user carries the address given                                  | The recovery flow reports failure to the user. A user with no address can never be found by this route.                                                                                                                                                                                 |
| **Synchronization matching on `EMAIL`**          | No candidate matches, so the record is treated as unmatched — a new user, or an orphan, depending on **Detect orphans**                                        | Either a new duplicate identity or a `SYNCHRONIZATION_ORPHAN` audit entry. See [How a synchronization matches and processes a record](/administration-guide/lifecycle/synchronize-and-import-data/synchronization-matching-and-processing.md#orphan-detection-changes-what-a-run-does). |
| **Orphan rows**                                  | The orphan's email column is filled with the literal string `No email`                                                                                         | Visible at `/webconsole/orphan-management`. Do not mistake it for a stored value.                                                                                                                                                                                                       |

{% hint style="danger" %}
**An approver or a certification reviewer with no email address is never notified, and that fact surfaces nowhere except the `email-manager` log.** The request or the certification campaign is created normally, the task appears in the person's **My Tasks**, and no message is ever sent — so unless that person happens to log in and look, the item simply waits. The only trace is the line `Send notification failed. Email was null for userId=…` in the `email-manager` log. `SEND_EMAIL_NOTIFICATION` is not audited as a failure either, because the transport was never reached — so an audit search shows no failed send, and a successful search shows no send at all.

Before you approve a feed that stops supplying email addresses, list the users who hold an approver, reviewer or delegated-administrator role and confirm every one of them has an active address. Service accounts and shared accounts do not need one; anybody in an approval or certification path does.
{% endhint %}

## Related

* [Shipped mail templates](/administration-guide/reference/shipped-mail-templates.md) — all 213 templates
* [System configuration](/administration-guide/platform/system-configuration.md) — the deprecated `Gateway` tab and its 16 keys
* [System property reference](/administration-guide/reference/system-properties.md) — every property named here
* [Edit a synchronization transformation script](/administration-guide/automation/synchronization-scripts.md#editing-a-shipped-transformation-script-removing-the-email-and-upn-block) — removing the email and UPN block from an import, and the five checks that go with it
* [Change configuration safely](/administration-guide/start/change-safely.md) — the change and rollback standard

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/platform/mail-and-notifications.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
