Manage Email Providers
Temps lets you edit, rotate credentials for, and disable email providers without deleting and recreating them. This page is the API reference for the PATCH /api/email-providers/{id} endpoint and its matching Edit dialog in the console. For sending mail and enabling open/click tracking, see Transactional Email.
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(Validation error: Provider name cannot be empty). - SNS topics are provider-bound. An SES provider accepts one exact
sns_topic_arn; omit it to preserve the current value, send a string to rotate it, or sendnullto clear it.
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, sns_topic_arn, or credentials) and never credential values. 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. |
sns_topic_arn | string or null | SES only. Set or rotate the exact authorized SNS topic; send null to clear it. |
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"
}
}'
Configure or clear the SES SNS topic
Authorize one SNS topic
curl -X PATCH https://your-temps-instance.com/api/email-providers/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"sns_topic_arn":"arn:aws:sns:us-east-1:123456789012:temps-events"}'
Clear the SNS topic
curl -X PATCH https://your-temps-instance.com/api/email-providers/1 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"sns_topic_arn":null}'
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",
"sns_topic_arn": "arn:aws:sns:eu-west-1:123456789012:temps-events",
"is_active": false,
"credentials": { },
"created_at": "2025-12-03T10:30:00+00:00",
"updated_at": "2026-05-29T10:30:00+00:00"
}
In the console
The Edit dialog (on the Email Providers page) mirrors these rules:
- The
provider_typeindicator is read-only — it cannot be changed after creation. - AWS SES providers show an SNS Topic ARN field for delivery, bounce, and complaint events.
- Secret fields render blank with the placeholder 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 | Validation error: 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 — Validation error: Cannot change provider type from {existing} to {new} on existing provider (id={id}). 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