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.

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_identity returns an empty domain identity — no SPF, no DKIM, no MX records.
  • verify_identity reports Verified immediately.
  • delete_identity is 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_typeRequired credential blockSerializes as
AWS SESses_credentialsses
Scalewayscaleway_credentialsscaleway
Generic SMTPsmtp_credentialssmtp

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:

FieldTypeDefaultNotes
hoststring (required)SMTP host, e.g. email-smtp.eu-west-1.amazonaws.com. Must be non-empty.
portinteger (required)SMTP port. Must be greater than 0.
usernamestring (optional)unsetLeave blank for unauthenticated relays.
passwordstring (optional)unsetRequired when username is set. The server only applies credentials when a username is present.
encryptionstringstarttlsOne of starttls, tls, or none (see below).
accept_invalid_certsbooleanfalseAccept self-signed / invalid certificates. Only safe for local testing.

Encryption modes

ModeWhat it doesTypical portUsed by
starttls (default)Connect plaintext, then upgrade to TLS587AWS SES SMTP, Sendgrid, Mailgun
tlsImplicit TLS / SMTPS — TLS from the first byte465SMTPS endpoints
noneNo encryption at all25 / 1025Local testing only (Mailpit, Mailhog)

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.com username/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_identity creates 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"
    }
  }'

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: true injects 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 hits GET /api/emails/{email_id}/track/open, which returns the GIF (with no-store cache headers) and records an open event with the requester's IP and user-agent.
  • track_clicks: true rewrites every <a href="http://…"> / <a href="https://…"> link to route through GET /api/emails/{email_id}/track/click/{link_index}. That endpoint records a click event and then issues a 302 redirect to the original URL. Non-HTTP links are left untouchedmailto:, tel:, #anchor, and javascript: hrefs are preserved exactly.

Per-email analytics

For a single email, two authenticated routes (both require an emails:read API key) return its tracking data:

RouteReturns
GET /api/emails/{id}/trackingSummary: 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/eventsThe 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):

RouteReturns
GET /api/emails/eventsA 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/statsAggregate 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_certs is 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_clicks must be set to true on each POST /api/emails call; 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, and javascript: 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

Was this page helpful?