Amazon SES
Amazon Simple Email Service (SES) is the cheapest way to send large volumes of transactional email, and a natural choice if you already run on AWS. It needs a bit more setup than other providers – IAM, a region, leaving the sandbox – but after that it is very reliable.
Choose SES when you send thousands of emails a month or more, care about price per message, or want everything inside your own AWS account. If you want the quickest possible setup, Postmark, Resend or Mailgun are easier to start with. See the services overview.
Prerequisites
Section titled “Prerequisites”- An AWS account with SES enabled in the region you want to send from.
- A verified identity in that region: preferably a whole domain (SES → Identities → Create identity → Domain), or at least the single From address.
- Production access (leaving the sandbox) before you send to arbitrary recipients – see Sandbox & limits.
- A dedicated IAM user with an access key and only these permissions:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["ses:SendEmail", "ses:SendRawEmail", "ses:GetAccount"], "Resource": "*" } ]}ses:SendEmail / ses:SendRawEmail are needed for sending (Frontmail uses the SES v2 API with a raw MIME message), ses:GetAccount for Test connection and the daily health check, which also reads your quota and sandbox status. You can narrow Resource to your identity and configuration set ARNs. Do not use root credentials or an admin user.
Connect in Frontmail
Section titled “Connect in Frontmail”- Open Email services → Add service → Amazon SES.
- Fill in the fields:
| Field | Where to find it | Example |
|---|---|---|
| AWS region | The region where your identity is verified (top-right of the SES console) | eu-central-1 |
| Access key ID | IAM → Users → frontmail-ses → Security credentials → Create access key | AKIAIOSFODNN7EXAMPLE |
| Secret access key | Shown once when the access key is created | wJalrXUtnFEMI/K7MDENG/… |
| Configuration set | SES → Configuration sets (needed for delivery events) | frontmail |
| SNS topic ARN | Optional; SNS → Topics – events from other topics are then ignored | arn:aws:sns:eu-central-1:123456789012:frontmail-events |
| Default From email | Optional; a verified identity in the selected region (an address on a verified domain, or a verified address). Used when the template has no From | hello@example.com |
| Default From name | Optional; display name for the default sender | Acme |
| Max emails per second | Optional; keep it at or below your SES maximum send rate | 14 |
- Click Verify & save. Frontmail calls
GetAccountand shows whether the account is still in the sandbox, its maximum send rate and 24-hour quota. - The service is saved once the check passes. SES has no “account address”, so set a Default From email here or a From email in every template – otherwise sending fails with
sender_not_configured(see Default sender). You can change it later on the service page without re-verifying. - The access key is encrypted with AWS KMS envelope encryption; it is only decrypted inside the sender process.
DNS: SPF, DKIM and DMARC
Section titled “DNS: SPF, DKIM and DMARC”Easy DKIM – when you create a domain identity, SES gives you three CNAME records:
abcdef1234._domainkey.example.com. CNAME abcdef1234.dkim.amazonses.com.ghijkl5678._domainkey.example.com. CNAME ghijkl5678.dkim.amazonses.com.mnopqr9012._domainkey.example.com. CNAME mnopqr9012.dkim.amazonses.com.AWS rotates the keys behind them automatically. Once they resolve, the identity shows Verified.
SPF via a custom MAIL FROM domain – by default the envelope sender is an amazonses.com address, so SPF passes but does not align with your domain. For DMARC alignment on SPF too, set a custom MAIL FROM (e.g. bounce.example.com) on the identity and publish:
bounce.example.com. MX 10 feedback-smtp.eu-central-1.amazonses.com.bounce.example.com. TXT "v=spf1 include:amazonses.com ~all"(Replace eu-central-1 with your region.)
DMARC – start in monitoring mode, then tighten:
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"Regions
Section titled “Regions”SES is regional: identities, sandbox status, quotas, configuration sets and suppression lists are all per region. The region you pick in Frontmail must be the one where:
- your domain or address is verified,
- you requested production access,
- your configuration set and SNS topic live.
The IAM access key itself is global, but using it against a region where nothing is set up fails with “Email address is not verified”. For EU data residency pick an EU region such as eu-central-1 (Frankfurt), eu-west-1 (Ireland) or eu-north-1 (Stockholm).
Sandbox & limits
Section titled “Sandbox & limits”Every new SES account starts in the sandbox in each region:
- you can send only to verified addresses or domains,
- at most 200 messages per 24 hours,
- at most 1 message per second.
Request production access in SES → Account dashboard → Request production access. Describe the use case (transactional email from a web form via Frontmail), how recipients opted in and how you handle bounces and complaints. Approval usually takes about a day.
After approval your quota starts at a modest level (e.g. 50,000/day, 14/s) and grows with good sending history. The current values are shown by Test connection; set Max emails per second at or below your maximum send rate.
Webhooks (delivery tracking)
Section titled “Webhooks (delivery tracking)”SES publishes events through Amazon SNS. Frontmail subscribes to your SNS topic over HTTPS.
-
SNS → Topics → Create topic (Standard), e.g.
frontmail-events, in the same region. -
Create subscription → protocol HTTPS → endpoint = the webhook URL from the service page in Frontmail (copy button):
https://api.frontmail.dev/webhooks/providers/ses/<token>Leave raw message delivery off. Frontmail verifies the SNS signature and confirms the subscription automatically – within a few seconds the subscription status changes from Pending confirmation to Confirmed.
-
SES → Configuration sets → Create set (e.g.
frontmail) → Event destinations → Add destination → choose event types → destination Amazon SNS → your topic. -
Put the configuration set name (and optionally the topic ARN) into the Frontmail service and save.
| SES event type | Frontmail event |
|---|---|
| Deliveries | delivered |
Hard bounces (Permanent) |
bounced – added to the suppression list |
Soft bounces (Transient), delivery delays |
recorded on the message |
| Complaints | complained – added to the suppression list |
| Opens | opened |
| Clicks | clicked |
| Rejects, rendering failures | recorded on the message |
Signature verification: every SNS message is signed. Frontmail downloads the signing certificate only from sns.<region>.amazonaws.com, verifies the signature (SignatureVersion 1 or 2) and rejects messages older than one hour. If you set SNS topic ARN, notifications from any other topic are rejected. If you leave it empty, Frontmail pins the topic of the first validly signed message (normally the subscription confirmation) and from then on rejects messages from any other topic – an SNS signature only proves that some AWS account sent the message.
Frontmail adds a frontmail_message_id message tag to each email so events can be matched back to the message.
Common errors
Section titled “Common errors”- auth – the service is broken (invalid/deleted access key, missing IAM permission, sending paused, sender not verified). The service is marked
unhealthyand the owner gets an email plus a dashboard banner. With a fallback service on the template, the message goes through it; otherwise the credit is refunded and the message waits in the hold queue asheld_service_erroruntil you fix the service (re-checked on save and in the daily health check), then it is sent automatically. - permanent – this message cannot be sent (invalid or unverified recipient, rejected content). Fallback service if configured, otherwise
failedwith the credit refunded. - transient – throttling, timeouts, 5xx. Retried with exponential backoff; after the last retry
failedand the credit refunded.
| SES error | Class | What to do |
|---|---|---|
InvalidClientTokenId / UnrecognizedClientException / SignatureDoesNotMatch |
auth | The access key ID or secret is wrong or deleted. Create a new key for the IAM user. |
AccessDeniedException: … not authorized to perform ses:SendRawEmail |
auth | Add the missing action to the IAM policy (see above). |
MessageRejected: Email address is not verified … hello@example.com (the sender) |
auth | Verify the domain or From address in the selected region. |
MailFromDomainNotVerifiedException |
auth | Fix the custom MAIL FROM MX/TXT records or remove the custom MAIL FROM. |
SendingPausedException / AccountSuspendedException |
auth | AWS paused sending (often high bounce or complaint rate). Check SES → Reputation metrics and AWS notices. |
MessageRejected: Email address is not verified … customer@gmail.com (a recipient) |
permanent | The account is still in the sandbox – request production access or verify the recipient. |
BadRequestException / ValidationException (invalid address) |
permanent | See invalid recipient. |
TooManyRequestsException / Throttling: Maximum sending rate exceeded |
transient | Lower Max emails per second to your SES send rate; ask AWS for a higher quota. |
InternalFailure, ServiceUnavailable, timeouts |
transient | Nothing – Frontmail retries. |
Checklist
Section titled “Checklist”- Domain identity verified with Easy DKIM (3 CNAMEs) in the chosen region
- Custom MAIL FROM (MX + SPF) and DMARC
p=nonepublished - Production access granted in that region
- IAM user with only
ses:SendEmail,ses:SendRawEmail,ses:GetAccount - Test connection passes and shows
sandbox: false - Configuration set → SNS topic → HTTPS subscription to the Frontmail webhook URL, status Confirmed
- Configuration set name entered in Frontmail
- Max emails per second at or below your SES send rate