---
id: messaging--smtp-mail-client
title: "Mail client"
summary: "Sends an e-mail over SMTP for every record it receives. The mail is either fully described by an incoming `SmtpMessage`, or built from the configuration (recipient, subject, body) with the body optionally taken from a `TextRecord`. Use it for notifications, reports and replies to collected mail."
audience: user
keywords: "input, continue, host, port, authtype, username, password, enablessl, to, subject, body"
---
# Mail client

Sends an e-mail over SMTP for every record it receives. The mail is either fully described by an incoming `SmtpMessage`, or built from the configuration (recipient, subject, body) with the body optionally taken from a `TextRecord`. Use it for notifications, reports and replies to collected mail.

## Pins

| Pin | Kind | Types | Description |
|---|---|---|---|
| Input | input | `Viaducta.Plugins.Smtp.Types.SmtpMessage`, `Viaducta.Plugins.TextPlugin.Types.TextRecord`, `Viaducta.TypeSystem.Metadata.NoInputData` | Triggers sending one mail per record. |
| Continue | continue | `Viaducta.Plugins.Smtp.Types.SmtpMessage` | Called after the mail was handed to the SMTP server, with the original input data and metadata. |

## Configuration

| Field | Type | Default | Description |
|---|---|---|---|
| `Host` | string | `viaducta.mail.service` | Required. SMTP server host name or IP. The special value `viaducta.mail.service` sends through the platform's own SMTP settings (see Behaviour). |
| `Port` | integer | `25` | SMTP port, 1-65535. Typical values: `25`, `587` (STARTTLS), `465`. |
| `AuthType` | dropdown | `0` | How to sign in, see Authentication. |
| `UserName` | string (max 256) | | SMTP user name (mailbox address with OAuth2). Leave empty for servers without authentication. Also used as the default sender address. |
| `Password` | password (max 256) | | SMTP password or app password, `AuthType` `0` only. Use an expression setting (`=Secrets.SmtpPassword`) instead of a literal value. |
| `EnableSSL` | boolean | `true` | Use TLS for the SMTP connection. |
| `To` | string | | Recipient(s) for `TextRecord` / `NoInputData` input. Separate several addresses with `,`, `;` or space. |
| `Subject` | string | | Subject for `TextRecord` / `NoInputData` input; fallback subject for an `SmtpMessage` whose `Subject` is null. |
| `Body` | text (multi-line) | | Body for `NoInputData` input; fallback body for an `SmtpMessage` with an empty `Body`. |

## Authentication

`AuthType` selects how the component signs in. Every option runs unattended: tokens are fetched, cached in memory and renewed by the engine, so nobody has to sign in while flows run. Store secrets as expression settings (`=Secrets.X`).

| `AuthType` | Fields | Use for |
|---|---|---|
| `0` Username and password (default) | `UserName`, `Password` | Classic servers; Gmail / Google accounts with an **app password**. No sign-in when `UserName` is empty. |
| `1` OAuth2 client credentials | `TokenEndpoint`, `ClientId`, `ClientSecret`, `Scope` | Microsoft 365 / Exchange Online, app-only: endpoint `https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token`, scope `https://outlook.office365.com/.default`. The Entra app needs the IMAP / POP / SMTP `.AccessAsApp` permission and an Exchange service principal with mailbox access. |
| `2` OAuth2 refresh token | `TokenEndpoint` (empty = Google), `ClientId`, `ClientSecret`, `RefreshToken`, optional `Scope` | Personal Gmail accounts. Get the refresh token once (for example with the Google OAuth Playground, scope `https://mail.google.com/`), using an OAuth client whose consent screen is *published*: in *Testing* status Google expires refresh tokens after 7 days. |
| `3` Google service account | `ServiceAccountKey` (JSON key), optional `Scope` | Google Workspace. Needs domain-wide delegation of `https://mail.google.com/`; the service account acts as the mailbox in `UserName`. |
| `4` OAuth2 access token | `AccessToken` | A token produced elsewhere, e.g. by an expression. Not renewed by the component. |

- With OAuth2 (`1`-`4`) `UserName` is the mailbox address and is required; the sign-in uses SASL XOAUTH2.
- If the server rejects a cached token, the component fetches a new one and retries once.
- Refresh tokens rotated by the server are kept in memory for the life of the engine process; the configured `RefreshToken` is used again after a restart.

- The mail client sends OAuth2 mail through MailKit: `EnableSSL` with port `465` is TLS from the start, other ports require STARTTLS; without `EnableSSL` STARTTLS is used when offered. Password sign-in keeps using the .NET SMTP client.
- With `Host` = `viaducta.mail.service` the authentication settings are not used.

## Behaviour

How the mail is filled, per input type:

| Mail part | `SmtpMessage` input | `TextRecord` input | `NoInputData` input |
|---|---|---|---|
| From | `MailFrom`; if empty `UserName`; if still empty and `Host` is `viaducta.mail.service`, `viaducta.alarms@viaducta.com`; otherwise error | `UserName` (or `viaducta.alarms@viaducta.com` with the platform service) | same as `TextRecord` |
| To | `To` (required, config `To` is ignored) | config `To` | config `To` |
| CC / Bcc | `CC`, `Bcc` | none | none |
| Subject | `Subject`, else config `Subject`, else "No subject provided" | config `Subject` | config `Subject` |
| Body | `Body`, else config `Body` | `TextRecord.Value` | config `Body` |
| HTML | `IsBodyHtml` as given | auto: HTML if the body contains `<body` | auto: HTML if the body contains `<body` |
| Priority | `Priority`: `0` Normal, `1` Low, `2` High | Normal | Normal |
| Attachments, headers | `Attachments` (`FileName`, `ContentType`, `Content` bytes), `Headers`, `MessageId` (written as `Message-ID`), `InReplyTo` (`In-Reply-To`), `References` (`References`, space-separated) | none | none |
| Reply to | `ReplyTo` (split like `To`) | none | none |

- Address lists (`To`, `CC`, `Bcc`) are split on `,`, `;` and space; each part must be a plain address.
- Body encoding is UTF-8.
- `Host` = `viaducta.mail.service`: host, port, SSL and credentials come from the platform SMTP configuration; `Port`, `EnableSSL`, `UserName` and `Password` of the component are not used for the connection.
- Any other host: a new SMTP connection is made with `Host`, `Port`, `EnableSSL`; credentials are sent only when `UserName` is set.
- On start the flow fails if `Host` is empty or `Port` is out of range. Send errors (authentication, rejected recipient, missing `To`/`MailFrom`, attachment without `FileName` or `ContentType`) throw an exception for the record.

## Notes

- For `TextRecord` and `NoInputData` input the mail's *Sender* header is always the platform SMTP user name, also when a custom `Host` is configured. If the platform SMTP user name is not set, these inputs fail with an invalid address error; send an `SmtpMessage` (its sender is `MailFrom`) to avoid it.
- With `SmtpMessage` input only an empty or null `Body` falls back to the config `Body`, and only a null `Subject` (not an empty string) falls back to the config `Subject`.
- Message ids (`MessageId`, `InReplyTo`, `References`) can be given with or without angle brackets; the headers get them added when missing.
- Display-name addresses such as `John Doe <john@example.com>` are broken apart by the space separator; use plain addresses.
- The `SmtpMessage.Priority` field declares a default of `1`, which means Low; set `0` explicitly for normal priority when you build the message in a mapper.
- `Continue` is declared as `SmtpMessage`, but it forwards whatever was received (also a `TextRecord` or `NoInputData`).
- Combine with Mail collector to answer incoming mail: copy the received `MessageId` into `InReplyTo` and swap `MailFrom` / `To` in a mapper.

## Example

```json
{ "Host": "smtp.example.com", "Port": 587, "EnableSSL": true, "UserName": "reports@example.com", "Password": "=Secrets.SmtpPassword", "To": "team@example.com", "Subject": "Daily report" }
```

Timer → (component producing a `TextRecord` with the HTML or text body) → **Mail client**
