Migrate from AWS SES
Step-by-step guide to moving from Amazon Simple Email Service (SES) to Sendbound. SDK replacement, IAM cleanup, SNS notification migration, and SMTP relay removal.
This guide covers everything specific to migrating from Amazon Simple Email Service (SES).
Before starting, read the general migration guide for the parallel-run strategy and DNS playbook.
What's different
| AWS SES | Sendbound | |
|---|---|---|
| Auth | IAM credentials (access key + secret) or SMTP password | Bearer token (sk_live_xxx) |
| SDK | @aws-sdk/client-ses or SMTP relay | Plain fetch — no SDK |
| Region config | Required (us-east-1, etc.) | Not required |
| Templates | SES Email Templates (limited) or app-side rendering | Full template editor with live preview |
| Event notifications | SNS topics → SQS / Lambda | HTTPS webhook endpoint |
| Suppression | Account-level suppression list | Per-project suppression list |
| Sandbox mode | SES sandbox restricts recipients | No sandbox — verify domain and send freely |
If you're on SES sandbox, you're restricted to verified email addresses only. With Sendbound you can send to any address as soon as your domain is verified — no sandbox to escape.
Export from AWS SES
Document verified identities
aws ses list-identities --identity-type Domain
aws ses list-identities --identity-type EmailAddress
Note which domains and addresses you send from — you'll verify the same domains in Sendbound.
Export SES templates
# List all templates
aws ses list-templates
# Export each template
aws ses get-template --template-name YOUR_TEMPLATE_NAME
The response includes HtmlPart and TextPart — save these as HTML files to import into Sendbound's template editor.
Export the account-level suppression list
aws sesv2 list-suppressed-destinations \
--query 'SuppressedDestinationSummaries[*].EmailAddress' \
--output text \
> suppressions.txt
Document SNS notification configuration
For each verified identity:
aws ses get-identity-notification-attributes \
--identities yourdomain.com
Note the SNS topic ARNs for bounces, complaints, and deliveries. You'll replace these with Sendbound webhooks.
API migration
Sending a transactional email
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
const ses = new SESClient({ region: 'us-east-1' });
await ses.send(new SendEmailCommand({
Source: 'hello@yourdomain.com',
Destination: {
ToAddresses: ['user@example.com'],
},
Message: {
Subject: { Data: 'Welcome', Charset: 'UTF-8' },
Body: {
Html: { Data: '<p>Welcome aboard!</p>', Charset: 'UTF-8' },
Text: { Data: 'Welcome aboard!', Charset: 'UTF-8' },
},
},
}));
Using a stored template
import { SendTemplatedEmailCommand } from '@aws-sdk/client-ses';
await ses.send(new SendTemplatedEmailCommand({
Source: 'hello@yourdomain.com',
Destination: { ToAddresses: ['user@example.com'] },
Template: 'WelcomeEmail',
TemplateData: JSON.stringify({
name: 'Jane',
confirm_url: 'https://app.example.com/confirm',
}),
}));
SNS notification migration
SES uses SNS topics to deliver bounce, complaint, and delivery notifications to your application. Sendbound replaces this entire stack with a single HTTPS webhook.
What you're replacing
SES → SNS Topic → SQS Queue → Lambda/Worker → Your handler
What you get with Sendbound
Sendbound → HTTPS POST → Your endpoint
Update your bounce/complaint handler
// Lambda function triggered by SNS → SQS
export async function handler(event: SQSEvent) {
for (const record of event.Records) {
const snsMessage = JSON.parse(record.body);
const sesNotification = JSON.parse(snsMessage.Message);
if (sesNotification.notificationType === 'Bounce') {
const emails = sesNotification.bounce.bouncedRecipients.map(r => r.emailAddress);
await markAsBounced(emails);
}
if (sesNotification.notificationType === 'Complaint') {
const emails = sesNotification.complaint.complainedRecipients.map(r => r.emailAddress);
await markAsComplained(emails);
}
}
}
IAM cleanup
After cutover, clean up the SES-specific AWS resources:
# Remove SES send permissions from the IAM role/user
aws iam detach-user-policy \
--user-name your-ses-user \
--policy-arn arn:aws:iam::aws:policy/AmazonSESFullAccess
# Delete the SMTP credentials (IAM user with ses:SendRawEmail)
aws iam delete-access-key \
--user-name your-ses-smtp-user \
--access-key-id AKIAIOSFODNN7EXAMPLE
# Delete SNS topics used for SES notifications
aws sns delete-topic --topic-arn arn:aws:sns:us-east-1:123456789:ses-bounces
aws sns delete-topic --topic-arn arn:aws:sns:us-east-1:123456789:ses-complaints
Checklist
- Documented all verified SES identities/domains
- Exported SES templates via AWS CLI
- Exported account-level suppression list
- Documented SNS topic ARNs + Lambda handlers
- Added and verified sending domain in Sendbound
- Imported suppression list
- Imported templates into Sendbound template editor
- Replaced AWS SDK / SMTP with
fetchto Sendbound API - Set
SENDBOUND_API_KEYin environment - Added Sendbound webhook endpoint and updated handler (replacing SNS/SQS/Lambda)
- Ran parallel test (SES + Sendbound simultaneously)
- Cut over to Sendbound only
- Deleted SNS topics and SQS queues
- Removed IAM permissions and SMTP credentials
- Uninstalled
@aws-sdk/client-sesfrom package.json