> 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.


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.fortifiedid.se/smtp/smtp.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
