Email Notifications (SMTP)
Overview
Accent CMS includes a built-in SMTP notification service that plugins and core features can use to send email. Typical use cases include contact form submissions, comment notifications, build reports, and health alerts.
The service is disabled by default and requires the Standard edition or higher.
Configuration
Add an smtp section to your config.yaml:
smtp: enabled: true host: smtp.example.com port: 587 username: "your-smtp-user" password: "" # prefer env var (see below) from: "noreply@example.com" tls: starttls # starttls, implicit, or none rate_limit: 10 # max emails per minute
| Setting | Default | Description |
|---|---|---|
enabled | false | Activate the SMTP service |
host | (required) | SMTP server hostname |
port | 587 | SMTP server port |
username | Authentication username | |
password | "" | Authentication password (prefer env var) |
from | (required) | Default sender address |
tls | starttls | TLS mode: starttls, implicit, or none |
rate_limit | 10 | Maximum emails per minute |
TLS Modes
| Mode | Port | Description |
|---|---|---|
starttls | 587 | Connect plain, upgrade to TLS. Default for most providers. |
implicit | 465 | Connect directly over TLS. Older style, still used by some providers. |
none | 1025 | No encryption. Only for local dev servers (Mailpit, MailHog). Do not use in production. |
Password Security
Store the SMTP password in an environment variable rather than config.yaml:
export ACCENTCMS_SMTP_PASSWORD="your-smtp-password" accent serve --config config.yaml
The environment variable takes priority over the password field in config. This keeps credentials out of version control.
How It Works
The SMTP service provides two interfaces:
For plugins: An internal HTTP endpoint at /_internal/smtp/send (localhost-only). WASM plugins POST a JSON body:
{ "to": ["recipient@example.com"], "subject": "Contact form submission", "body_text": "Plain text version", "body_html": "<p>HTML version (optional)</p>" }
Responses: 200 OK, 400 Bad Request, 429 Too Many Requests, 500 Internal Server Error, 503 Service Unavailable.
For core features: A Rust API (SmtpService::send()) used by built-in features like build reports and health alerts. No HTTP overhead.
Rate Limiting
The rate_limit setting caps the number of emails per minute (default: 10). When exceeded, the service returns HTTP 429 (Too Many Requests). The counter resets every 60 seconds.
This prevents accidental email floods from misconfigured plugins or form spam.
Edition Requirements
The SMTP service requires the Standard edition or higher. Core edition binaries do not include the SMTP library. When smtp.enabled is false (the default), no SMTP connections are made and no resources are consumed.
Supported SMTP Providers
Any standard SMTP server works:
| Provider | Host | Port | Notes |
|---|---|---|---|
| Gmail | smtp.gmail.com | 587 | Requires App Password |
| SendGrid | smtp.sendgrid.net | 587 | API key as password |
| Mailgun | smtp.mailgun.org | 587 | Domain-specific credentials |
| AWS SES | email-smtp.{region}.amazonaws.com | 587 | IAM SMTP credentials |
| Self-hosted (Postfix) | localhost | 25 | No auth needed on loopback |
Plugin Usage
Plugins that need to send email add 127.0.0.1 to their allowed_hosts in plugin.toml:
[plugin]
allowed_hosts = ["127.0.0.1"]
Then POST to /_internal/smtp/send using Extism’s HTTP host function. See the bundled contact-form example plugin for a complete example.