Manage Email Providers
This page is the API reference for managing email providers — creating them, editing them in place, and rotating credentials. For how to actually send mail and turn on open/click tracking, see Transactional Email.
Temps lets you edit an existing email provider in place with a partial-update endpoint, PATCH /api/email-providers/{id}, and a matching Edit dialog in the console. Use it to rename a provider, change its region, enable or disable it, or rotate its credentials — without deleting and recreating the row. The key safety property: omitting a credential block leaves the stored secret untouched, so you can rename a provider or flip its active flag without re-typing API keys or SMTP passwords.
This applies to all three provider types: AWS SES, Scaleway, and generic SMTP. The previously disabled Edit dropdown item is now live for each of them.
Email providers are part of the temps-email plugin, and its routes are served under the /api prefix. The handler declares the route as /email-providers/{id}; the live path is therefore /api/email-providers/{id}. All endpoints require a valid Temps API key and the EmailProvidersWrite permission.
How it works
A PATCH carries only the fields you want to change. Every field in the request body is optional, and any field you leave out keeps its current value. The endpoint enforces a few rules:
provider_typeis immutable. There is noprovider_typefield on the update request — the encrypted credentials format is fixed at creation time. To switch a provider from, say, SES to SMTP, delete the row and create a new one.- Credentials are preserved unless you send them. If you omit the credential block, the encrypted blob stays byte-identical in the database. If you supply a credential variant, it is re-serialized, re-encrypted, and replaces the stored blob.
- Only one credential block per update. Supplying more than one of
ses_credentials,scaleway_credentials, orsmtp_credentialsis rejected with400. - The credential block must match the existing
provider_type. Sending SMTP credentials to an SES provider is rejected with400before it reaches the service layer. - Empty names are rejected. A
namethat trims to empty returns400(Provider name cannot be empty).
On success the endpoint returns 200 with the masked EmailProviderResponse (the credentials field is masked for display, never the raw secret).
Audit logging
A successful update emits an EMAIL_PROVIDER_UPDATED audit event only when something actually changed. The event records the names of the fields that changed (for example name, region, is_active, credentials) and never their values — so secrets and credential material never reach the audit log. (Creating a provider emits EMAIL_PROVIDER_CREATED.)
Request body
UpdateEmailProviderRequest — all fields optional. Omit a field to leave it unchanged.
| Field | Type | Notes |
|---|---|---|
name | string | New display name. Must not be empty. |
region | string | Cloud region. For SMTP this is informational; host/port carry the routing. |
is_active | boolean | Enable or disable the provider without deleting it. |
ses_credentials | object | Replace AWS SES credentials. Only valid when the provider is SES. |
scaleway_credentials | object | Replace Scaleway credentials. Only valid when the provider is Scaleway. |
smtp_credentials | object | Replace generic SMTP credentials. Only valid when the provider is SMTP. |
There is no provider_type field — it cannot be changed.
Credential block fields
| Block | Fields |
|---|---|
ses_credentials | access_key_id, secret_access_key |
scaleway_credentials | api_key, project_id |
smtp_credentials | host, port, username (optional), password (optional), encryption (starttls / tls / none, default starttls), accept_invalid_certs (default false) |
Examples
Assume an SES provider with ID 1.
Rename only (credentials preserved)
Sending just name leaves the stored AWS keys byte-identical.
Rename a provider
curl -X PATCH https://your-temps-instance.com/api/email-providers/1 \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{"name": "Production SES (EU)"}'
Change region and disable
Update region and toggle active
curl -X PATCH https://your-temps-instance.com/api/email-providers/1 \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{"region": "eu-west-1", "is_active": false}'
Rotate credentials
Supplying a credential block re-encrypts and replaces the stored secret. The variant must match the provider's type (here, SES).
Rotate SES keys
curl -X PATCH https://your-temps-instance.com/api/email-providers/1 \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{
"ses_credentials": {
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}'
For an SMTP provider, rotate the password while keeping host/port:
Rotate SMTP password
curl -X PATCH https://your-temps-instance.com/api/email-providers/2 \
-H "Authorization: Bearer your-temps-api-key" \
-H "Content-Type: application/json" \
-d '{
"smtp_credentials": {
"host": "email-smtp.eu-west-1.amazonaws.com",
"port": 587,
"username": "AKIAIOSFODNN7EXAMPLE",
"password": "new-smtp-password",
"encryption": "starttls"
}
}'
Successful response
200 OK — masked EmailProviderResponse
{
"id": 1,
"name": "Production SES (EU)",
"provider_type": "ses",
"region": "eu-west-1",
"is_active": false,
"credentials": { },
"created_at": "2025-12-03T10:30:00Z",
"updated_at": "2026-05-29T10:30:00Z"
}
In the console
The Edit dialog (on the Email Providers page) mirrors these rules:
- The
provider_typeindicator is read-only — labelled as immutable, since it can't be changed. - Secret fields render blank with the helper text "Leave blank to keep current." Leave them empty to preserve the stored secret.
- Only fields that diverge from the loaded state are sent on submit. If you change nothing, no request is sent.
- For SMTP, the non-secret fields — host, port, encryption, and
accept_invalid_certs— are prefilled from the maskedGETresponse, so you only re-enter the password if you want to rotate it.
Errors and gotchas
| Situation | Status | Detail |
|---|---|---|
Empty name | 400 | Provider name cannot be empty (service layer) |
Credential block doesn't match provider_type | 400 | Credential block does not match existing provider type ({existing}) (rejected by the handler before the service layer) |
| More than one credential block supplied | 400 | Only one credential block (ses/scaleway/smtp) may be supplied per update |
SMTP block with empty host or port == 0 | 400 | smtp_credentials.host is required / smtp_credentials.port must be greater than 0 |
| Provider ID not found | 404 | Provider not found |
| Missing key / wrong permission | 401 / 403 | Requires a valid key with EmailProvidersWrite |
The handler intercepts a mismatched credential block first and returns Credential block does not match existing provider type ({existing}). The service layer keeps a defensive backstop with a longer message — Cannot change provider type from {existing} to {new} on existing provider (id={id}) — which is the form abbreviated in the changelog. Both map to HTTP 400.
The update path is covered by a unit-test suite in temps-email exercising rename, no-op (nothing sent), empty-name rejection, type-mismatch rejection, credential rotation (the ciphertext changes), and credential preservation (the ciphertext is byte-identical when the caller omits the block).
Next steps
- Security Features — how credentials are encrypted at rest
- CLI Reference — manage providers from the command line