Skip to main content

Email with Amazon SES

What it is​

Amazon SES (Simple Email Service) sends the product's emails: one-time sign-in codes, receipts, notices. To make mail arrive in inboxes rather than spam, the sending domain proves it is allowed to send, using three DNS records: SPF (which servers may send), DKIM (a signature on each email) and DMARC (what receivers should do with mail that fails the other two).

Why we do it​

  • New SES accounts start in a sandbox: they can only send to addresses you have verified, and only about 200 emails a day. Production access must be requested.
  • Since 2024, Gmail and Yahoo reject or spam-folder bulk mail without SPF, DKIM and DMARC.
  • If too many emails bounce or are marked as spam, AWS pauses sending for the whole account, including sign-in codes.

Before you start​

  • The domain zone in Route 53 (step 5).
  • The sending address agreed with the client, for example no-reply@example.com.
  • The main region; SES is configured per region.

How it fits together​

Steps​

1. Verify the domain with DKIM​

SES, Configuration, Identities, Create identity, Domain, example.com. Keep Easy DKIM with 2048-bit keys and tick Publish DNS records to Route 53. SES adds three CNAME records. The identity turns Verified within minutes.

2. Set a custom MAIL FROM domain​

In the identity, Custom MAIL FROM domain, use mail.example.com. Publish the records it shows:

NameTypeValue
mail.example.comMX10 feedback-smtp.<region>.amazonses.com
mail.example.comTXT"v=spf1 include:amazonses.com ~all"

3. Publish DMARC​

NameTypeValue
_dmarc.example.comTXT"v=DMARC1; p=none; rua=mailto:dmarc@example.com"

Start with p=none and read the reports for two weeks. Then move to p=quarantine. If the client already sends mail from this domain (Google Workspace, Microsoft 365), check their existing SPF and DMARC records first and merge, do not overwrite.

4. Create a configuration set and handle bounces​

  1. Configuration sets, Create set, acme-prod.
  2. Add an event destination for Bounce, Complaint and Delivery, to an SNS topic or EventBridge.
  3. The API subscribes and marks the address as undeliverable, so we never retry it.
  4. Keep the account-level suppression list on for bounces and complaints.

5. Request production access​

SES, Account dashboard, Request production access. Describe: what emails you send (transactional: sign-in codes, receipts), how people sign up, how you handle bounces and complaints (step 4), and expected daily volume. AWS usually answers within a day.

Do this in prod only. Dev and staging stay in the sandbox and send only to verified team addresses, so test mail can never reach a real user.

6. Send from the API​

import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';
const ses = new SESv2Client({ region: process.env.AWS_REGION });

await ses.send(new SendEmailCommand({
FromEmailAddress: 'Acme <no-reply@example.com>',
Destination: { ToAddresses: [to] },
ConfigurationSetName: process.env.SES_CONFIGURATION_SET,
Content: { Simple: { Subject: { Data: subject }, Body: { Html: { Data: html }, Text: { Data: text } } } },
}));

The task role needs ses:SendEmail on the identity and the configuration set.

Check it worked​

  1. Send a test from dev to your own verified address.
  2. In the received email, view the original headers: spf=pass, dkim=pass, dmarc=pass.
  3. Send to bounce@simulator.amazonses.com and check the bounce event reached the API.

If it goes wrong​

You seeIt means
"Email address is not verified"The account is still in the sandbox, or the recipient is not verified in dev
Mail arrives in spamSPF, DKIM or DMARC fails. Check the headers
Sending paused by AWSBounce rate near 5% or complaints near 0.1%. Clean the list, handle bounces
Client's own company mail started failingTheir SPF or DMARC record was overwritten. Merge the records

Rules​

Rule

Sign-in codes and receipts are transactional email. Marketing email needs consent, an unsubscribe link and, ideally, a separate configuration set or subdomain, so a marketing complaint spike cannot stop sign-in codes. See Marketing and email.

Rule

Never send real email from dev or staging to anyone outside the team.

Done when​

  • Domain verified with DKIM; custom MAIL FROM with SPF; DMARC published.
  • Configuration set with bounce and complaint handling.
  • Production access granted in prod; dev and staging stay in the sandbox.
  • A test email passes SPF, DKIM and DMARC.