Get Started
AWS SES Setup
Connect your AWS account and verify a sending domain.
#AWS SES Setup
This platform sends mail through your AWS SES account — you provide the credentials, Amazon does the sending, and you pay Amazon directly (about $0.10 per 1,000 emails).
There are two ways to wire up SES:
- Dashboard setup (recommended) — paste your AWS keys at Settings → SES, click one button, the app creates the SES configuration set, SNS topics, and event destinations for you.
- Manual setup — run the AWS CLI commands yourself if you want fine-grained control or you already have SES infrastructure you want to reuse.
Either way, you'll end up with the same thing:
- An SES configuration set named
emailforsaas-tracking - Three SNS topics —
emailforsaas-bounces,emailforsaas-complaints,emailforsaas-deliveries - Event destinations pointed at those topics
- An HTTPS SNS subscription pointed at
/api/webhooks/seson your deployment
#Prerequisites
- An AWS account
- Billing enabled (SES is pay-as-you-go, roughly $0.10 / 1,000 emails)
- A domain you can add DNS records for
#1. Create an IAM user
Create an IAM user (e.g. emailforsaas-ses) with an access key and the following inline policy. The app reads these credentials to send mail and manage SES infrastructure on your behalf.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SES",
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail",
"ses:GetAccount",
"ses:CreateEmailIdentity",
"ses:DeleteEmailIdentity",
"ses:GetEmailIdentity",
"ses:PutEmailIdentityDkimSigningAttributes",
"ses:CreateConfigurationSet",
"ses:CreateConfigurationSetEventDestination",
"ses:GetConfigurationSet"
],
"Resource": "*"
},
{
"Sid": "SNS",
"Effect": "Allow",
"Action": [
"sns:CreateTopic",
"sns:Subscribe",
"sns:GetTopicAttributes",
"sns:SetTopicAttributes",
"sns:Publish"
],
"Resource": "arn:aws:sns:*:*:emailforsaas-*"
}
]
}
Save the Access Key ID and Secret Access Key somewhere safe — you'll paste them into the dashboard next.
#2. Dashboard setup (recommended)
- Sign in to your deployment and open Settings → SES.
- Paste the Access Key ID, Secret Access Key, and region (default
us-east-1). - Click Set up SES.
The app runs an authenticated Convex action (api.sesSetup.setupSes) that:
- Validates the credentials and reports whether you're still in SES sandbox mode.
- Creates the
emailforsaas-trackingconfiguration set if it doesn't exist. - Creates the three SNS topics.
- Subscribes
https://<your-deployment>.convex.site/api/webhooks/sesto each topic (HTTPS). - Wires each SES event type to the matching SNS topic as an event destination.
Credentials are encrypted with AES-256-GCM using the ENCRYPTION_KEY env var before being stored, and only decrypted at send time.
Already have an SES configuration set you want to reuse? The setup action is idempotent — it will skip anything that already exists.
#3. Manual setup (optional)
If you'd rather do the wiring yourself, run these commands. Replace <account-id> and <deployment> with real values.
# 1. Configuration set
aws sesv2 create-configuration-set \
--configuration-set-name emailforsaas-tracking \
--region us-east-1
# 2. SNS topics
aws sns create-topic --name emailforsaas-bounces --region us-east-1
aws sns create-topic --name emailforsaas-complaints --region us-east-1
aws sns create-topic --name emailforsaas-deliveries --region us-east-1
# 3. Subscribe the webhook (auto-confirmed by our endpoint)
for topic in bounces complaints deliveries; do
aws sns subscribe \
--topic-arn "arn:aws:sns:us-east-1:<account-id>:emailforsaas-${topic}" \
--protocol https \
--notification-endpoint "https://<deployment>.convex.site/api/webhooks/ses"
done
# 4. Event destinations
aws sesv2 create-configuration-set-event-destination \
--configuration-set-name emailforsaas-tracking \
--event-destination-name bounces \
--event-destination '{"Enabled":true,"MatchingEventTypes":["BOUNCE"],"SnsDestination":{"TopicArn":"arn:aws:sns:us-east-1:<account-id>:emailforsaas-bounces"}}'
aws sesv2 create-configuration-set-event-destination \
--configuration-set-name emailforsaas-tracking \
--event-destination-name complaints \
--event-destination '{"Enabled":true,"MatchingEventTypes":["COMPLAINT"],"SnsDestination":{"TopicArn":"arn:aws:sns:us-east-1:<account-id>:emailforsaas-complaints"}}'
aws sesv2 create-configuration-set-event-destination \
--configuration-set-name emailforsaas-tracking \
--event-destination-name deliveries \
--event-destination '{"Enabled":true,"MatchingEventTypes":["DELIVERY","SEND","OPEN","CLICK"],"SnsDestination":{"TopicArn":"arn:aws:sns:us-east-1:<account-id>:emailforsaas-deliveries"}}'
Then set the deployment environment variables:
npx convex env set AWS_ACCESS_KEY_ID "..."
npx convex env set AWS_SECRET_ACCESS_KEY "..."
npx convex env set AWS_SES_REGION "us-east-1"
npx convex env set SES_CONFIGURATION_SET "emailforsaas-tracking"
#4. Verify a sending domain
- Open Domains → Add domain in the dashboard.
- Enter the domain you want to send from (e.g.
mail.yourcompany.com) and region. - Copy the three DKIM CNAME records into your DNS provider.
- DNS propagation is usually instant on Cloudflare, minutes on most registrars, up to 72 hours worst case.
The domain flips to verified automatically once SES sees the records. Check with:
dig CNAME <token>._domainkey.yourdomain.com
While you're there, add an SPF record (v=spf1 include:amazonses.com ~all) and a DMARC record (v=DMARC1; p=none; rua=mailto:postmaster@yourdomain.com) to hit strong inbox placement.
#5. Leave the SES sandbox
Every new SES account is in sandbox mode — you can only send to verified recipient addresses, and you're capped at a small daily volume.
- AWS Console → SES → Account dashboard → Request production access.
- Fill in:
- Mail type: Transactional (or Transactional + Marketing if you'll run campaigns)
- Website URL
- Use case description
- Expected daily volume
- Approvals are usually returned within 24 hours.
Until you're approved, the dashboard displays a sandbox badge and restricts sending to verified addresses.
#Verifying the setup
From Settings → SES, click Validate setup. The platform checks:
- Credentials are present and still valid
- Configuration set exists
- All three SNS topics exist and have an active HTTPS subscription
- Event destinations are wired to the right topics
Any red flag shows up as a specific step failure so you know exactly what to fix.
#Troubleshooting
#Webhooks aren't firing
- Confirm each SNS subscription is in
Confirmedstate:aws sns list-subscriptions-by-topic --topic-arn arn:aws:sns:us-east-1:<id>:emailforsaas-bounces - Convex HTTP logs (
npx convex logs) will show incoming POSTs to/api/webhooks/ses. The first request from SNS after subscription is aSubscriptionConfirmation— the endpoint auto-confirms it.
#Emails show failed status
- Check Convex logs for the actual SES error.
- Verify you're out of the sandbox, or that you're only sending to verified addresses.
- Confirm the
fromaddress uses a verified domain.
#DKIM verification stuck on pending
- DNS propagation. Run
digagainst your domain's three DKIM CNAMEs and the provider you pasted the records into. - Some registrars silently append the root domain —
abc._domainkey.yourdomain.combecomesabc._domainkey.yourdomain.com.yourdomain.com. Fix by entering justabc._domainkeyas the host.