Transactional Email
Temps sends transactional email through configurable email providers. There are three provider types: AWS SES and Scaleway (which manage domain identities through the cloud vendor's admin API), and generic SMTP. Use SMTP when you only have SMTP credentials and cannot — or do not want to — call an upstream admin API. The canonical case is AWS, where IAM gives you SES SMTP credentials (email-smtp.<region>.amazonaws.com:587) but not API access, but it works equally with Sendgrid, Mailgun, Postmark, a self-hosted Postfix, or a local Mailpit/Mailhog relay for testing.
This page covers setting up a provider, sending mail, and open/click tracking. For the full create/edit/rotate API — request bodies, the masked response shape, the error table, and audit behavior — see Manage Email Providers.
The SMTP provider sends mail via the lettre crate. Email-provider routes are served under the /api prefix — for example POST /api/email-providers. Sending and managing providers requires a Temps API key with the relevant email_providers:* permission.
How It Works
The SMTP provider is the simplest of the three: it has no domain-management API, so it does not provision DNS records. Domains added under an SMTP provider are treated as already-verified imports rather than something Temps sets up:
create_identityreturns an empty domain identity — no SPF, no DKIM, no MX records.verify_identityreportsVerifiedimmediately.delete_identityis a no-op.
This means you own DNS at the upstream mail server. Configure DKIM/SPF/MX wherever your relay lives (the AWS SES console, your Sendgrid/Mailgun dashboard, your Postfix host) before relying on deliverability. Temps will hand the message to your relay regardless; it does not police your DNS.
When you create a provider, the request's provider_type selects which credential block is required:
provider_type | Required credential block | Serializes as |
|---|---|---|
| AWS SES | ses_credentials | ses |
| Scaleway | scaleway_credentials | scaleway |
| Generic SMTP | smtp_credentials | smtp |
Omitting the matching block for a given type rejects the request with 400 Bad Request.
SMTP Credentials Reference
The smtp_credentials block on POST /api/email-providers (and, when updating, on PATCH /api/email-providers/{id}) accepts these fields:
| Field | Type | Default | Notes |
|---|---|---|---|
host | string (required) | — | SMTP host, e.g. email-smtp.eu-west-1.amazonaws.com. Must be non-empty. |
port | integer (required) | — | SMTP port. Must be greater than 0. |
username | string (optional) | unset | Leave blank for unauthenticated relays. |
password | string (optional) | unset | Required when username is set. The server only applies credentials when a username is present. |
encryption | string | starttls | One of starttls, tls, or none (see below). |
accept_invalid_certs | boolean | false | Accept self-signed / invalid certificates. Only safe for local testing. |
Encryption modes
| Mode | What it does | Typical port | Used by |
|---|---|---|---|
starttls (default) | Connect plaintext, then upgrade to TLS | 587 | AWS SES SMTP, Sendgrid, Mailgun |
tls | Implicit TLS / SMTPS — TLS from the first byte | 465 | SMTPS endpoints |
none | No encryption at all | 25 / 1025 | Local testing only (Mailpit, Mailhog) |
For loopback hosts (localhost, 127.0.0.1, ::1), TLS verification is automatically loosened — invalid certificates and hostnames are accepted — so local testing works without setting accept_invalid_certs. This loosening applies only to loopback hosts. For any real relay, leave accept_invalid_certs at false.
When to use SMTP instead of the AWS SES provider
The deciding factor is which AWS credentials you have:
- Only SES SMTP credentials (
email-smtp.<region>.amazonaws.comusername/password, no IAM API access) — SMTP is the simpler path. You supply the host, port, and those credentials, and Temps relays mail without ever calling the SES admin API. - Full IAM API credentials — the AWS SES provider is recommended. Its
create_identitycreates the domain identity through the SES API and provisions DKIM (CNAME records on the root domain), SPF, and MX records for you, eliminating manual DNS configuration at the upstream relay.
Create an SMTP Provider
Add a provider that relays through AWS SES SMTP in eu-west-1:
AWS SES SMTP (STARTTLS, port 587)
curl -X POST https://your-temps-instance.com/api/email-providers \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "SES SMTP (eu-west-1)",
"provider_type": "smtp",
"region": "eu-west-1",
"smtp_credentials": {
"host": "email-smtp.eu-west-1.amazonaws.com",
"port": 587,
"username": "AKIAIOSFODNN7EXAMPLE",
"password": "your-ses-smtp-password",
"encryption": "starttls"
}
}'
For an SMTP provider, the region field is informational only — the host and port carry the real routing. It still appears on the provider record and in audit logs.
A local Mailpit/Mailhog relay needs no credentials and no encryption:
Local Mailpit (no auth, no TLS)
curl -X POST https://your-temps-instance.com/api/email-providers \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Local Mailpit",
"provider_type": "smtp",
"region": "local",
"smtp_credentials": {
"host": "localhost",
"port": 1025,
"encryption": "none"
}
}'
Update a Provider
PATCH /api/email-providers/{id} performs a partial update. Every field is optional, and any field you omit keeps its current value. Critically, omitting the credential block preserves the stored secret — so you can rename a provider or change its region without re-typing the password:
Rename without re-typing the password
curl -X PATCH https://your-temps-instance.com/api/email-providers/3 \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{ "name": "SES SMTP (production)" }'
The provider_type is immutable — to switch providers, delete the row and create a new one. Supplying a credential block that does not match the existing provider's type is rejected with 400 Bad Request. Only one credential block (ses/scaleway/smtp) may be supplied per update.
Send a Test Email
Use the Send Test action to confirm the relay works. The test email is delivered to the logged-in user with a branded body (dark-header / success-badge / details-table / footer layout) and the subject [Temps] Email provider test — <name>. The provider type is rendered as a human label (AWS SES, Scaleway, or SMTP), and the provider name and region are HTML-escaped.
Send a test email
curl -X POST https://your-temps-instance.com/api/email-providers/3/test \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@your-domain.com",
"from_name": "Your App"
}'
Using the Web Console
In the Email Providers page, the provider-type dropdown has a third option labeled "SMTP (import existing domain)" (with a Server icon). Selecting it shows the SMTP form, which:
- Warns that Temps cannot manage DKIM/SPF/MX via SMTP and that DNS must be configured at your upstream provider.
- Requires a password whenever you set a username.
- Autosuggests the conventional port when you change the encryption mode — 587 for STARTTLS, 465 for implicit TLS, 25 for no encryption.
The Edit dialog is available for all three provider types. It locks the provider_type indicator and prefills non-secret fields (for SMTP: host, port, encryption, and accept_invalid_certs). Secret fields render blank with a "Leave blank to keep current" hint, so submitting without retyping a password keeps the stored secret unchanged.
Open & Click Tracking
Temps can track opens, clicks, deliveries, and bounces on the email it sends, then surface per-email and platform-wide analytics. Tracking is off by default and is enabled per send.
Enable tracking when you send
Pass track_opens and/or track_clicks in the POST /api/emails request body. Both are optional booleans that default to false:
Send with open + click tracking on
curl -X POST https://your-temps-instance.com/api/emails \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@your-domain.com",
"to": ["customer@example.com"],
"subject": "Welcome to our platform!",
"html": "<h1>Hello</h1><p><a href=\"https://your-domain.com/start\">Get started</a></p>",
"track_opens": true,
"track_clicks": true
}'
What each flag does:
track_opens: trueinjects a 1×1 transparent GIF pixel into the HTML body (immediately before</body>, or appended if there is no</body>). When a mail client loads the pixel it hitsGET /api/emails/{email_id}/track/open, which returns the GIF (withno-storecache headers) and records an open event with the requester's IP and user-agent.track_clicks: truerewrites every<a href="http://…">/<a href="https://…">link to route throughGET /api/emails/{email_id}/track/click/{link_index}. That endpoint records a click event and then issues a302redirect to the original URL. Non-HTTP links are left untouched —mailto:,tel:,#anchor, andjavascript:hrefs are preserved exactly.
Tracking only rewrites the HTML body, so a send needs an html body to be trackable. The original html_body you submitted is stored unchanged; the rewritten version (with pixel and tracking links) is stored separately as tracked_html_body, so dashboard previews of the original don't register fake opens. Both tracking endpoints are public (no API key) since they're called by mail clients — but they are mounted under /api, e.g. /api/emails/{email_id}/track/open.
Per-email analytics
For a single email, two authenticated routes (both require an emails:read API key) return its tracking data:
| Route | Returns |
|---|---|
GET /api/emails/{id}/tracking | Summary: track_opens/track_clicks, open_count, click_count, unique_opens, unique_clicks, first_opened_at, first_clicked_at, plus a links array with each tracked link's original_url and per-link click_count. |
GET /api/emails/{id}/tracking/events | The raw event timeline for that email — each open/click with its event_type, link_url/link_index, ip_address, user_agent, and created_at. Optional event_type query filter (open, click). |
unique_opens / unique_clicks are counted by distinct IP address across the recorded events. In the web console, the email detail page renders this as an event timeline plus a per-link click breakdown.
Per-email tracking summary
curl https://your-temps-instance.com/api/emails/550e8400-e29b-41d4-a716-446655440000/tracking \
-H "Authorization: Bearer your-temps-api-key"
Platform-wide analytics
The console's Analytics tab aggregates tracking across all emails, backed by two global routes (both emails:read):
| Route | Returns |
|---|---|
GET /api/emails/events | A paginated log of tracking events across all emails. Query params: event_type (filter by open/click), page (default 1), page_size (default 20, max 100). |
GET /api/emails/events/stats | Aggregate counts — delivered, opened, clicked, bounced, complained — plus computed open_rate, click_rate, and bounce_rate. |
Global tracking stats
curl https://your-temps-instance.com/api/emails/events/stats \
-H "Authorization: Bearer your-temps-api-key"
Notes & Gotchas
- DNS is your responsibility. Imported SMTP domains are reported as verified, but deliverability depends entirely on DKIM/SPF/MX being set up correctly at your upstream relay. Temps does not verify or manage those records.
accept_invalid_certsis for local testing only. It disables certificate validation; never enable it for a real relay. Loopback hosts already loosen TLS automatically, so you rarely need this flag.- Credentials are applied only when a username is present. A blank username produces an unauthenticated relay connection, which is what open local relays (Mailpit/Mailhog) expect.
- Provider type cannot be changed after creation — the encrypted credential format is fixed at creation time.
- Tracking is per-send and off by default.
track_opens/track_clicksmust be set totrueon eachPOST /api/emailscall; there is no provider-level or global "always track" toggle. - Open tracking depends on the recipient loading images. Mail clients that block remote images won't fire the pixel, so open counts are a lower bound — this is inherent to pixel-based open tracking, not a Temps limitation.
- Only HTTP(S) links are click-tracked.
mailto:,tel:,#anchor, andjavascript:hrefs are deliberately left as-is, so they never count as clicks.
Next Steps
- Manage Email Providers — the provider-management API reference (create/edit/rotate SES, Scaleway, and SMTP providers) for how to actually configure and send mail
- Security Features — how Temps handles secrets, transactional auth email, and password reset delivery
- Monitoring & Alerts — wire up notification providers for uptime and error alerts