SMTP & Email Alerts

Send automated email notifications when entity values change.

Overview

ControlBird's SMTP & Email Alerts feature delivers automated email notifications whenever an entity's value changes. It uses the same controller / endpoint / mapper configuration model as the device-integration protocols: a controller manages the service, an endpoint describes the SMTP server connection, and one or more mappers watch specific entities and send mail when their value changes. Encryption is supported through STARTTLS and implicit TLS, message content is template-driven, and per-mapper cooldowns provide rate limiting to prevent email floods.

Use this feature to alert operators about critical conditions, notify teams about system events, or produce change reports, all without writing any code. An endpoint can authenticate with a stored password, or by connecting a Google or Microsoft 365 account instead (see below). An optional OAuth attribution mode can also send each message from the email address of the signed-in user who made the change, which is useful for audit trails. SMTP is a write-only protocol: ControlBird only sends mail, it never reads a mailbox, and that holds for a connected account too: it is authorized to send, not to read the mailbox.

Prefer a guided tutorial?

New to this? Follow the SMTP Email Alerts walkthrough for a step-by-step tour, then come back here for the full reference.

Key Concepts

  • Mapper pattern. When a watched entity's value changes, the watching SmtpMapper triggers the controller to send an email. This is the same controller / endpoint / mapper layering used by the protocol integrations.
  • Three entity types. A SmtpClientController runs the service, a SmtpClientEndpoint defines the server connection and sender identity, and each SmtpMapper binds one watched entity to a set of recipients and templates.
  • Template variables. Subject and body templates support {{value}} (current entity value), {{entity_name}} (the watched entity's path), and {{timestamp}} (ISO 8601 time).
  • Rate limiting. A configurable cooldown sets the minimum time between emails for a single mapper.
  • Account authentication. Setting an endpoint's AuthType to OAuth2AuthCode connects a Google or Microsoft 365 account and sends through it instead of a stored password. This authenticates the SMTP connection itself, which is a different mechanism from OAuth attribution below: attribution only changes the From address of an outgoing message.
  • OAuth attribution. With UseWriterEmailIfAvailable enabled, a change made by an OpenID Connect user can be sent from that user's OAuthEmail address instead of the endpoint's default sender.
  • Header-injection safety. Subject lines are sanitized so template content can never inject extra mail headers.

Architecture

An email-alert integration is assembled from three layers of entities. The controller manages the service lifecycle, the endpoint carries the SMTP server connection and sender identity, and each mapper binds a watched entity to its recipients and message templates.

LayerEntityRole
ControllerSmtpClientControllerManages the overall SMTP email notification service
EndpointSmtpClientEndpointSMTP server connection, credentials, and sender identity
MapperSmtpMapperWatches an entity and sends templated mail when its value changes

Entity Reference

SmtpClientController

Manages the overall SMTP email notification service, following the same controller layer used by the device-integration protocols.

FieldTypePurpose
NameStringDisplay name for the controller (e.g. EmailNotifications)
DescriptionStringOptional documentation describing this controller's purpose
DisabledBoolSet to true to disable all SMTP functionality without removing configuration
EndpointConnectionModeChoiceControls how the endpoint connection is established across the deployment

SmtpClientEndpoint

Defines the SMTP server connection details, credentials, and sender identity.

FieldTypePurpose
HostStringSMTP server hostname (e.g. smtp.gmail.com, smtp.office365.com)
PortIntSMTP server port; typically 587 for STARTTLS, 465 for implicit TLS, 25 for plaintext
SecurityChoiceEncryption mode: None (plaintext), StartTls (default), or ImplicitTls
UsernameStringSMTP authentication username (optional if the server allows anonymous)
PasswordStringSMTP authentication password, or an app-specific password for services like Gmail; used only when AuthType is Password
AuthTypeChoiceHow the endpoint authenticates: Password (default), OAuth2AuthCode to connect a Google or Microsoft 365 account, or None for a relay that needs no authentication
OAuthClientIDStringClient ID from your own OAuth app registration; required when AuthType is OAuth2AuthCode
OAuthClientSecretStringClient secret from the same OAuth app registration
OAuthAuthorizationEndpointStringThe provider's authorization URL; filled in automatically by the Gmail and Microsoft 365 presets in the Add Integration wizard
OAuthTokenEndpointStringThe provider's token URL; filled in automatically by the presets
OAuthScopesStringSpace-separated OAuth scopes requested during authorization; filled in automatically by the presets
FromAddressStringDefault sender email address used in all notifications
FromNameStringDisplay name for the sender (optional; creates Name <email> format)
TimeoutMsIntSMTP connection timeout in milliseconds (default 30000)
UseWriterEmailIfAvailableBool When true, sends from the OAuth user's email address instead of FromAddress if the writer has AuthMethod = OpenID Connect
OAuthLastErrorStringRuntime field: the error from the last failed authorization or token refresh, when AuthType is OAuth2AuthCode (read-only)

SmtpMapper

Watches an entity's value for changes and triggers SMTP notifications with customizable templates and recipients. The last three fields are runtime statistics updated after every send attempt and are read-only.

FieldTypePurpose
TargetEntityEntityReferenceThe entity whose value changes trigger email notifications
TargetFieldString The field on TargetEntity to watch. Defaults to Data, which is correct for most device points and calculated values. For an Alarm, set this to CurrentSeverity, which changes on activation, escalation, and clear (a cleared alarm reports Normal); the fastest way to wire this up is the Add email notification action in the Active Alarms app, which sets both fields for you
RecipientsStringComma-separated list of recipient email addresses (at least one required)
CcRecipientsStringComma-separated list of CC recipients (optional)
SubjectTemplateString Email subject line with template variables {{value}}, {{entity_name}}, {{timestamp}}
BodyTemplateStringEmail body content with the same template variables; supports plain text or HTML
ContentFormatChoiceEmail body format: Text (plain text) or Html (HTML with text fallback)
CooldownMsIntRate limiting: minimum milliseconds between emails for this mapper (0 = no rate limit)
TriggerModeChoiceTrigger condition: OnChange (only when the value differs) or OnAnyWrite (on any write)
EmailsSentIntRuntime counter: total emails successfully sent by this mapper (read-only)
LastSentAtTimestampRuntime field: when the last email was successfully sent (read-only)
LastErrorStringRuntime field: error message from the last failed send attempt (read-only)

Template Variables

Subject and body templates accept the following placeholders, expanded at send time:

VariableResolves to
{{value}}The current value of the watched entity
{{entity_name}}The watched entity's path
{{timestamp}}The current time in ISO 8601 format

Content normalization

Subject lines have newlines removed to prevent header injection, and body line endings are normalized. When ContentFormat is Html, a plain-text alternative is generated automatically, so clients that cannot render HTML still receive readable text.

SMTP Server Configuration

Set the endpoint Host, Port, and Security to match your mail provider. The three security modes are None (plaintext), StartTls (upgrade the connection after connecting), and ImplicitTls (TLS from the first byte). Authentication is set separately with AuthType: Password for a stored username and password, OAuth2AuthCode to connect a Google or Microsoft 365 account, or None for a relay that needs no authentication.

Connecting a Google or Microsoft 365 account

In the Add Integration wizard, picking Gmail or Microsoft 365 as the provider fills in the server address, port, security mode, and that provider's OAuth endpoints and scope. It cannot fill in an OAuth app of your own, and that step is not optional: Google and Microsoft both require every application to register itself before it can request access to an account, so you register your own app and paste its client ID and secret into the wizard. Expect a real, multi-step setup rather than a single click:

  • Gmail: in Google Cloud Console, create a project, enable the Gmail API, configure the OAuth consent screen with the gmail.send scope, and create an OAuth client ID for a web application.
  • Microsoft 365: in the Microsoft Entra admin center, register an app, grant it the SMTP.Send and offline_access API permissions, and create a client secret.

Register the exact redirect URI

Both app registrations ask for a redirect (callback) URI. Register this one, character for character:

<your platform origin>/api/oauth/endpoint/callback

Your platform origin is the scheme and host you use to reach ControlBird, with no path after it, for example https://plant1.example.com. Getting this wrong is the hardest failure in this flow to diagnose: a mismatch fails at Google or Microsoft, before any ControlBird screen appears, so the error and its wording come entirely from the provider.

Back in ControlBird, paste the client ID and client secret into the matching provider group, save the endpoint, then use its Connect with OAuth control. A popup carries you through the provider's sign-in and consent screens; when it closes, the endpoint reports itself connected. Tokens refresh automatically before they expire; if a refresh fails, the control changes to Reconnect and the failure is recorded rather than left silent (see OAuthLastError above and LastError on each mapper).

Gmail rewrites the From address

Gmail sends as the authenticated account, not whatever FromAddress says. If FromAddress does not match the connected account, or one of its verified send-as aliases, Gmail substitutes the authenticated address instead of rejecting the message, and it does this silently. Set FromAddress to the mailbox you connected, or a message that looks fine in ControlBird will arrive from an address you did not choose.

A factory restore disconnects every mailbox

A factory restore recreates the node's data directory, and the stored authorization for every connected mailbox goes with it. Each one needs to be reconnected afterward, the same as any other endpoint configured before the restore. This is easy to miss until an incident, when notifications that used to fire silently do not.

Password authentication and other providers

AuthType = Password still works for any SMTP server that accepts it, including Amazon SES and self-hosted mail relays, and remains the default for new endpoints.

# Amazon SES
Host     = email-smtp.us-east-1.amazonaws.com
Port     = 465
Security = ImplicitTls
AuthType = Password

# Gmail or Microsoft 365, password authentication (legacy)
Host     = smtp.gmail.com          # or smtp.office365.com
Port     = 587
Security = StartTls
AuthType = Password
Username = [email protected]
Password = your-app-password
FromAddress = [email protected]

Password authentication is on borrowed time

Microsoft has begun rejecting Basic-authentication SMTP submissions for Exchange Online, turns it off by default for existing tenants at the end of 2026, and removes it entirely in 2027. Google Workspace withdrew less-secure-app sign-in in May 2025. For either provider, a username-and-password mail connection will stop working eventually; connect the account with OAuth where you can.

Gmail requires App Passwords

When 2-factor authentication is enabled on a Google account, regular passwords fail with SMTP authentication errors. Generate an App Password in your Google Account settings and use it as the endpoint Password. This only applies to AuthType = Password; a connected account (OAuth2AuthCode) does not use a password at all.

Message Templates

Mapper templates support plain text or HTML. The examples below use the template variables to embed the live value, the entity identifier, and the time of the change.

# Simple plain-text alert
Subject = Alert: {{entity_name}} changed
Body    = Value changed to {{value}} at {{timestamp}}
# HTML report (ContentFormat = Html)
Subject = ControlBird: {{entity_name}} Update
Body    =
  <h2>Value Update</h2>
  <p>Entity: <strong>{{entity_name}}</strong></p>
  <p>New Value: <code>{{value}}</code></p>
  <p><small>{{timestamp}}</small></p>

Recipients

Recipients and CcRecipients are comma-separated lists; surrounding whitespace is trimmed. At least one valid recipient is required for the mapper to send.

Recipients = [email protected], [email protected]

Rate Limiting

Set CooldownMs to throttle a noisy mapper. Any email that would fall inside the cooldown window since the last send is skipped.

# Send at most one email per minute from this mapper
CooldownMs = 60000   # any change within 60s of the last send is skipped

Cooldown scope

Rate limiting applies per mapper only: multiple mappers send independently and do not respect each other's cooldowns. The cooldown resets when the service restarts.

OAuth Email Attribution

When UseWriterEmailIfAvailable is enabled on the endpoint, an email triggered by a user action can be sent from that user's address, which is useful for audit trails. All three of the following conditions must hold:

  • UseWriterEmailIfAvailable = true on the endpoint.
  • The writer's AuthMethod is OpenID Connect.
  • The writer has a non-empty OAuthEmail field.

From-address fallback

If the OAuth email lookup fails or the feature is disabled (for example a Native or LDAP user makes the change, or the OpenID Connect user has no OAuthEmail), the endpoint's FromAddress is used instead, and it must be set. Alarm and other service-driven notifications are authored by a service, not an OpenID Connect user, so the writer-email path never applies to them and FromAddress is required. If it is empty, the mapper reports a FromAddress required error instead of silently dropping the email.

Common Patterns

  • Critical alerts. Watch an alarm or status entity with TriggerMode = OnChange and a short plain-text template so operators get immediate, low-noise notifications. For an alarm, point TargetEntity at the alarm and set TargetField to CurrentSeverity (written on activation, escalation, and clear alike). The quickest way to set this up is the Add email notification action on any alarm in the Active Alarms app, which creates the wiring for you.
  • HTML status reports. Use ContentFormat = Html with a richly formatted body; the automatic plain-text alternative covers clients that cannot render HTML.
  • Throttled high-frequency signals. Pair a frequently changing source with CooldownMs to cap the email rate while still surfacing the latest value.
  • Audited operator actions. Enable UseWriterEmailIfAvailable so change notifications carry the originating OpenID Connect user's address.
  • Monitoring the service. Watch EmailsSent, LastSentAt, and LastError on each mapper to confirm delivery and catch failures.

Troubleshooting & Limitations

  • Gmail authentication errors. Use an App Password when 2-factor authentication is enabled; a normal password will be rejected.
  • Controller will not initialize. FromAddress must be a valid email format; an invalid value prevents controller initialization.
  • Mapper never sends. The Recipients field cannot be empty: at least one comma-separated address is mandatory. Malformed addresses cause the entire send to fail.
  • Attribution From not applied. OAuth attribution only works for OpenID Connect users; Native and LDAP users cannot use it. If the conditions are not met the endpoint's FromAddress is used instead, so it must be set, and alarm notifications in particular always fall through to it.
  • Account connection fails or stops sending. Check OAuthLastError on the endpoint and LastError on the affected mapper: OAuth failures (a missing token, an expired refresh token, a rejected authentication attempt) are recorded there rather than dropped silently. A stuck connection usually means the account's consent was revoked or the client secret changed; reconnect the endpoint to fix it.
  • Header injection. Newlines in subject templates would allow header injection, so the service strips them from subjects automatically.
  • No retries. The service does not retry failed sends. A failure is recorded in LastError and the send is abandoned.
  • Write-only protocol. SMTP only sends mail. There is no inbound or receive path by design.
  • Inspecting traffic. The service logs SMTP traffic (MAIL FROM, TO, SUBJECT, and error responses) for diagnosis.