Migrate from Amazon SES
Migrating from Amazon SES to Sendpository
Everything that changes when you move your email from Amazon SES, in the order you'll do it. A day if you also retire SNS topics, SQS queues and bounce-processing Lambdas; an hour for the send call alone.
In short
Migrating from Amazon SES to Sendpository replaces the AWS SDK, SigV4 signing and IAM credentials with one HTTPS request and a Bearer key. Bounce and complaint events arrive as signed webhooks instead of going through configuration sets and SNS, and suppression, logs and a dashboard come built in. You trade SES's lowest-in-class per-email price for far less infrastructure to run.Or let your coding agent do the whole migration
Paste it in. It finds every Amazon SES call, rewrites it, updates your webhook handler and switches over behind a flag you can flip back.
Show the promptHide the prompt
Switch this project's email from Amazon SES to Sendpository. First run `npx sendpository@latest init` - it opens my browser for me to approve, saves the API key and installs the Sendpository skill - then follow the skill's migration guide (references/migrate.md). If you can't run commands, read https://www.sendpository.com/agents/sendpository/references/migrate.md instead.
1. API differences
| Amazon SES | Sendpository | |
|---|---|---|
| Endpoint | SESv2 SendEmail (POST /v2/email/outbound-emails), usually through the AWS SDK | POST https://api.sendpository.com/v1/emails |
| Body | FromEmailAddress, Destination.ToAddresses, Content.Simple.Subject.Data, Content.Simple.Body.Html.Data | from, to, subject, html, text |
| Attachments | Content.Simple.Attachments, or a raw MIME message | attachments: [{ filename, content (base64), content_type }] |
| Metadata | EmailTags: [{ Name, Value }] with a configuration set | tags: a string key/value object |
| Events | Configuration set → SNS, EventBridge, Firehose or CloudWatch | Signed webhooks to your URL, plus searchable logs |
| Response | { MessageId } | { "id": "..." } |
2. Authentication differences
| Amazon SES | Sendpository | |
|---|---|---|
| API key | AWS SigV4 with IAM credentials and an ses:SendEmail policy | Authorization: Bearer sp_... - no IAM, no region |
Create the key under API keys in the dashboard. It is shown once, so put it straight into SENDPOSITORY_API_KEY in your secrets manager. Keep the Amazon SES key until the switch is finished, then revoke it.
3. Domain setup
Add your sending domain in Sendpository and publish the records it shows: a 2048-bit DKIM key signed as your own domain, plus SPF and a bounce MX on a sp. subdomain so your existing records are never edited. DMARC is recommended; if you already publish one, leave it as it is.
- SES Easy DKIM publishes three CNAMEs. Sendpository publishes its own DKIM TXT record on a different selector, so they coexist.
- There is no sandbox or production-access request: sending opens once your domain verifies and your account email is confirmed.
Not sure what’s published already? Run the domain through the email DNS checker.
4. Webhooks
| Amazon SES | Sendpository | |
|---|---|---|
| Verification | SNS notifications, verified with the SNS message signature and certificate | HTTPS POST with Sendpository-Signature: t=<unix>,v1=<HMAC-SHA256> |
Event names
| Amazon SES event | Sendpository event |
|---|---|
| Send | email.sent |
| Delivery | email.delivered |
| DeliveryDelay | email.delayed |
| Bounce | email.bounced |
| Complaint | email.complained |
| Reject | email.failed |
| Open | email.opened |
| Click | email.clicked |
| Subscription | email.unsubscribed |
No SNS subscription confirmation step - add the URL and events arrive.
Full payloads and verification code are in the webhooks guide.
5. Error handling
| Amazon SES | Sendpository | |
|---|---|---|
| Error shape | SDK exceptions such as MessageRejected, MailFromDomainNotVerifiedException and TooManyRequestsException | { "error": { "type": "domain_not_verified", "message": "..." } } |
These map to validation_error, domain_not_verified and rate_limit_exceeded. rate_limit_exceeded includes a Retry-After header. Only rate_limit_exceeded (wait for Retry-After) and internal_error are worth retrying automatically; the errors reference lists every type.
6. Code migration
import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";
const ses = new SESv2Client({ region: "us-east-1" });
await ses.send(
new SendEmailCommand({
FromEmailAddress: "Acme <billing@yourdomain.com>",
Destination: { ToAddresses: ["customer@example.com"] },
Content: {
Simple: {
Subject: { Data: "Your receipt" },
Body: { Html: { Data: "<p>Thanks for your order.</p>" } },
},
},
EmailTags: [{ Name: "type", Value: "receipt" }],
ConfigurationSetName: "transactional",
}),
);import { Sendpository, SendpositoryError } from "sendpository";
const sendpository = new Sendpository(process.env.SENDPOSITORY_API_KEY);
try {
const { id } = await sendpository.emails.send(
{
from: "Acme <billing@mail.yourdomain.com>",
to: ["customer@example.com"],
subject: "Your receipt",
html: "<p>Thanks for your order.</p>",
text: "Thanks for your order.",
tags: { type: "receipt" },
},
// A retried job returns the first message's id instead of sending twice.
{ idempotencyKey: "receipt-10428" },
);
console.log(id);
} catch (err) {
if (err instanceof SendpositoryError) console.error(err.type, err.message);
throw err;
}Watch out for
- Your bounce-processing Lambda or queue consumer becomes a webhook handler - and suppression already happens for you, so it may only need to update your own user records.
- Raw MIME sends need to be split into from, to, subject, html, text and attachments.
- Remove the IAM user or role once SES is no longer called, so unused credentials don't linger.
7. Pricing
| Amazon SES | Sendpository | |
|---|---|---|
| Published pricing | $0.10 per 1,000 emails sent, and $0.10 per 1,000 received plus charges per 256 KB chunk. New AWS accounts get free-tier credits. | 6,000 emails a month (up to 200 a day), free forever. Current plans |
Amazon SES pricing from their own pages, checked 2026-09-23. Sendpository has a permanent free plan (6,000 emails a month); Amazon SES does not.
8. Things to test before switching
Specific to leaving Amazon SES:
- Replace IAM and SigV4 with a bearer token. There is no request signing here and no role to assume - a key in an environment variable is the whole auth model, so anything using an instance profile or STS needs a different secret path.
- Rebuild what Configuration Sets were feeding. SES routes events to SNS, Kinesis Firehose or CloudWatch; ours posts signed webhooks and keeps a searchable log, so any Lambda consuming an SNS topic has nothing to subscribe to after the move.
- Check whether you were in the sandbox. SES accounts start restricted to verified recipients and need a support request to leave; there is no equivalent gate here, which means a test that passed only because SES refused to send will now actually send.
True of any migration:
- Send to a real Gmail and a real Outlook address and open Show original: SPF, DKIM and DMARC should all say PASS, with DKIM signed as your own domain.
- Send a message with an attachment and one with an inline image, and check both render.
- Trigger a bounce (an address at a domain you control with no mailbox) and confirm your webhook handler receives email.bounced and marks the address.
- Send the same request twice with one Idempotency-Key and confirm only one email arrives.
- Point a staging environment at the new API key first, and watch the Logs page for 4xx responses before switching production.
- Keep the old provider's DNS records until the new domain has verified and a day of production mail has been delivered - removing them first breaks mail still in flight.
Questions
Is Sendpository cheaper than Amazon SES?
Not per email at high volume - SES's price is hard to beat. Teams move to save engineering time: logs, signed webhooks, suppression, receiving and a dashboard work without building on SNS, SQS and Lambda.
Do I need an AWS account to use Sendpository?
No. It is one HTTPS API with a Bearer key. There are no IAM policies, regions, sandbox requests or configuration sets.
Amazon SES API details are from their documentation, checked 2026-09-25: docs.aws.amazon.com, docs.aws.amazon.com.
Keep reading
Sendpository vs Amazon SES
Where each is the better choice, side by side.
Migrate from Resend
Usually under an hour for the code, plus DNS propagation.
Migrate from SendGrid
An afternoon for most codebases - the request shape is the main rewrite.
Migrate from Postmark
An hour or two - the request maps field for field.
Send your first email in minutes
6,000 emails a month (up to 200 a day), free forever. No card needed.