> For the complete documentation index, see [llms.txt](https://docs.fortifiedid.se/smtp/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fortifiedid.se/smtp/smtp.md).

# SMTP

## Introduction

Platform module for application outbound SMTP requests.

Multiple instances of the module can be configured to support outbound request with different properties, like timeouts, proxies or SSL/TLS settings.&#x20;

Callers targets the requested instance by its name which must be configuration unique.

Messages kan be sent in plain text or HTML format.

## Configuration

{% hint style="info" %}
**Module name:**  `SmtpClient`
{% endhint %}

{% tabs %}
{% tab title="Properties" %}

<table><thead><tr><th width="260">Name</th><th width="273">Description</th><th width="278">Default value</th><th width="131" data-type="checkbox">Mandatory</th></tr></thead><tbody><tr><td><code>name</code></td><td>Destination name. Unique identifier for a specific configuration/connection/service.  This value must be unique for all smtpclient configurations.</td><td><code>"default"</code></td><td>false</td></tr><tr><td><code>host</code></td><td>SMTP server host.</td><td>N/A</td><td>true</td></tr><tr><td><code>port</code></td><td>SMTP server port.</td><td>N/A</td><td>true</td></tr><tr><td><code>from</code></td><td>Who is the message from.</td><td><code>"noreply@fortifiedid.se"</code></td><td>false</td></tr><tr><td><code>user_name</code></td><td>User name when identifying against SMTP server.</td><td>N/A</td><td>false</td></tr><tr><td><code>password</code></td><td>Used when authenticating against SMTP server.</td><td>N/A</td><td>false</td></tr><tr><td><code>start_tls</code></td><td>Defines if communications must use TLS. Allowed values are : <code>"OPTIONAL"</code>, <code>"DISABLED"</code> or <code>"REQUIRED"</code></td><td><code>"OPTIONAL"</code></td><td>false</td></tr><tr><td><code>keep_alive</code></td><td>Should a pool be used keeping connections open. </td><td><code>true</code></td><td>false</td></tr><tr><td><code>reconnect_interval</code></td><td>value for reconnect interval in ms.</td><td><code>1000</code></td><td>false</td></tr><tr><td><code>keep_alive_timeout</code></td><td>This value determines how long a connection remains unused, in seconds, in the pool before being evicted and closed.</td><td><code>600</code></td><td>false</td></tr><tr><td><code>connect_timeout</code></td><td>How many milliseconds before a connect attempt fails.</td><td><code>5000</code></td><td>false</td></tr><tr><td><code>disable_esmtp</code></td><td>set if ESMTP should be tried as first command (EHLO), rfc 1896</td><td><code>true</code></td><td>false</td></tr><tr><td><code>idle_timeout</code></td><td>Timeout in seconds.</td><td><code>600</code></td><td>false</td></tr><tr><td><code>login</code></td><td>possible options for a login into a SMTP server either <code>"DISABLED"</code>, <code>"NONE"</code>, <code>"REQUIRED"</code> or <code>"XOAUTH2"</code></td><td><code>"NONE"</code></td><td>false</td></tr><tr><td><code>reconnect_attempts</code></td><td>Number of reconnect attempts.</td><td><code>3</code></td><td>false</td></tr><tr><td><code>use_alpn</code></td><td>Whether to use or not Application-Layer Protocol Negotiation.</td><td><code>false</code></td><td>false</td></tr><tr><td><code>ssl_trust_all</code></td><td>Ignore SSL validation.</td><td><code>false</code></td><td>false</td></tr><tr><td><code>ssl_handshake_timeout</code></td><td>the SSL handshake timeout in ms.</td><td><code>10</code></td><td>false</td></tr><tr><td><code>use_ssl</code></td><td>is SSL/TLS enabled.</td><td><code>false</code></td><td>false</td></tr><tr><td><code>proxy_host</code></td><td>Host for proxying.</td><td>N/A</td><td>false</td></tr><tr><td><code>proxy_port</code></td><td>Port for proxying.</td><td>N/A</td><td>false</td></tr><tr><td><code>proxy_user</code></td><td></td><td>N/A</td><td>false</td></tr><tr><td><code>proxy_password</code></td><td></td><td>N/A</td><td>false</td></tr><tr><td><code>auth_methods</code></td><td>Authentication methods supported by the SMTP server. The following are supported: <code>"XOAUTH2, DIGEST-MD5, CRAM-SHA256, CRAM-SHA1, CRAM-MD5, PLAIN, LOGIN"</code>. Use "" if you are unsure of which auth methods are supported. </td><td><code>"DIGEST-MD5, CRAM-SHA256, CRAM-SHA1, CRAM-MD5, PLAIN, LOGIN"</code></td><td>false</td></tr><tr><td><code>slow_mail_warning_ms</code></td><td>Logs a warning if SMTP send is still in progress after this many milliseconds. <code>0</code> or lower disables the warning timer.</td><td><code>5000</code></td><td>false</td></tr><tr><td><code>send_timeout_ms</code></td><td>Local timeout for the entire SMTP request, including attachment preparation. <code>0</code> means disabled</td><td><code>0</code></td><td>false</td></tr><tr><td><code>log_activity</code></td><td>Enables "low-level" mail activity logging.</td><td><code>false</code></td><td>false</td></tr></tbody></table>
{% endtab %}

{% tab title="Example" %}

```json
{
	"name": "SmtpClient",
	"enabled": true,
	"config": {
		"name": "smtp01",
		"host": "smpt.acme.org",
		"port": 25,
		"user_name": "smtp_user",
		"password": "smtp_password",
		"auth_methods": "DIGEST-MD5, CRAM-SHA256, LOGIN"
	}
}
```

{% endtab %}
{% endtabs %}

## OAuth 2.0

OAuth 2.0 allows an SMTP client to authenticate by using a short-lived access token instead of a static password. In practice, the client first obtains a token from an identity provider, such as Microsoft Entra ID, and then uses that token with the SMTP server, typically through XOAUTH2. This improves security by reducing reliance on long-lived credentials and allowing authentication to be controlled through modern identity and access policies.

#### To use OAuth 2.0 :

`disable_esmtp` and `keep_alive` must be `false`.&#x20;

`auth_methods` must be `XOAUTH2`.

`password` must not be set.

A separate `oauth2` configuration block must be added.

{% tabs %}
{% tab title="oauth2 properties" %}

<table><thead><tr><th width="201.78515625">Name</th><th width="303.3125">Description</th><th width="182.51171875">Default value</th><th data-type="checkbox">Mandatory</th></tr></thead><tbody><tr><td><code>http_destination</code></td><td>Name of the HttpClient module that should be used to fetch the token.</td><td></td><td>true</td></tr><tr><td><code>token_url</code></td><td>Full token endpoint URL, for example an Entra token endpoint.</td><td></td><td>true</td></tr><tr><td><code>token_parameters</code></td><td>Key/value parameters sent in the token request body. Typical examples: grant_type, client_id, client_secret, scope.</td><td></td><td>true</td></tr><tr><td><code>token_headers</code></td><td>Extra HTTP headers to include in the token request.</td><td></td><td>false</td></tr><tr><td><code>access_token_field</code></td><td>Name of the field in the token response that contains the access token.</td><td><code>"access_token"</code></td><td>false</td></tr><tr><td><code>expires_in_field</code></td><td>Name of the field in the token response that contains token lifetime in seconds.</td><td><code>"expires_in"</code></td><td>false</td></tr><tr><td><code>expires_at_field</code></td><td>Name of the field in the token response that contains an absolute expiry timestamp (epoch seconds). If configured and present, it takes precedence over <code>expires_in_field</code>.</td><td></td><td>false</td></tr><tr><td><code>refresh_skew_seconds</code></td><td>Safety margin before expiry. The token will be refreshed before its actual expiry time.</td><td><code>60</code></td><td>false</td></tr><tr><td><code>http_timeout_ms</code></td><td>Timeout for the token HTTP request.</td><td><code>5000</code></td><td>false</td></tr></tbody></table>
{% endtab %}

{% tab title="Example" %}

```json5
{
  "name": "default",
  "host": "smtp.example.com",
  "port": 587,
  "start_tls": "REQUIRED",
  "user_name": "sender@example.com",
  "disable_esmtp": false,
  "keep_alive": false,
  "oauth2": {
    "http_destination": "oauth-http",
    "token_url": "https://login.example.com/oauth2/token",
    "token_parameters": {
      "grant_type": "client_credentials",
      "client_id": "...",
      "client_secret": "...",
      "scope": "https://outlook.office365.com/.default"
    },
    "token_headers": {
      "Accept": "application/json"
    },
    "access_token_field": "access_token",
    "expires_at_field": "expires_at",
    "expires_in_field": "expires_in",
    "refresh_skew_seconds": 60,
    "http_timeout_ms": 5000
  }
}
```

{% endtab %}
{% endtabs %}

## Troubleshooting

To enable more information in logs use these loggers in the log4j2.xml file:

```
<AsyncLogger name="foss.platform.modules.smtp.SmtpRequestHandler" level="TRACE"/>
```

or for a broader scope use:

```
<AsyncLogger name="foss.platform.modules.smtp" level="TRACE"/>
```

For low-level logging with `log_activity` set to `true`, use this logger:

```
<AsyncLogger name="io.vertx.ext.mail.impl.SMTPConnection" level="TRACE"/>
```

Required level by message type:

* `DEBUG`
  * request received
  * request prepared
  * attachment preparation
  * attachment file read
  * SMTP send start
* `INFO`
  * SMTP send success
* `WARN`
  * slow send warning from `slow_mail_warning_ms`
  * late SMTP success ignored after timeout
  * late SMTP failure ignored after timeout
* `ERROR`
  * local SMTP timeout
  * SMTP send failure
  * request preparation failure
* `TRACE`

  * full Vert.x mail response payload

Practical guidance:

* use `DEBUG` for normal SMTP troubleshooting
* use `WARN` if the focus is only slow/hanging mail behavior
* use `TRACE` if response payload details are needed

## Metrics

This module produces metrics for each request.&#x20;

{% tabs %}
{% tab title="Naming" %}

<table data-header-hidden><thead><tr><th width="95"></th><th></th></tr></thead><tbody><tr><td><code>name</code></td><td><code>fortified.internal.smtpclient_&#x3C;metric-name></code></td></tr><tr><td><code>tags</code></td><td><code>name=&#x3C;instance-name></code></td></tr></tbody></table>
{% endtab %}

{% tab title="Metric" %}

<table><thead><tr><th width="143">Name</th><th width="97">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>pending</code></td><td>gauge</td><td>Number of pending requests</td></tr><tr><td><code>success</code></td><td>counter</td><td>Number of successful request</td></tr><tr><td><code>failure</code></td><td>counter</td><td>Number of failed request</td></tr><tr><td><code>request</code></td><td>timer</td><td>Request time in ms</td></tr></tbody></table>
{% endtab %}

{% tab title="JMX example" %}

```
/ JMX name for HttpClient request failure counter on 
```

```
// module instance "smtp01" 
```

```
metrics:name=fortifiedInternalHttpclient_failure.name.smtp01,type=meters
```

{% endtab %}
{% endtabs %}

## Events

When sending e-mail using following events are possible

* `SMTP001` - Mail sent
  * traceID  if present
  * to - comma separated list&#x20;
  * cc - comma separated list&#x20;
  * bcc - comma separated list&#x20;
  * subject - message subject
* `SMTP002` - Mail could not be sent
  * traceID  if present
  * subject - message subject
* `SMTP003` - Mail send timed out
  * traceID  if present
  * subject - message subject
  * Note: local timeout in SMTP module; final delivery outcome may still be unknown.
