Elastic Email to AWS SES Migration
Migrating from Elastic Email to AWS SES
Elastic Email is one of the lowest-priced multi-purpose email platforms — transactional API, marketing builder, contact lists, and automation in a single product. AWS SES is a focused infrastructure primitive that costs less per message and gives you full control over reputation, IP isolation, and event tooling. The step-by-step migration plan — including the parts that are not obvious if you used the marketing builder, the automation flows, or the contact list manager.
> **TL;DR.** If you are migrating only transactional, plan 1–2 weeks on the SES side. If you are also moving the marketing builder, contact lists, and automation flows, plan 4–6 weeks plus a marketing-platform decision (own the builder in code, or pair SES with a marketing-only tool like Customer.io or Klaviyo).
Elastic Email occupies a different niche than SendGrid, Mailgun, or Postmark. It is a multi-purpose platform — transactional API, marketing builder, contact manager, automation engine, and SMTP relay all bundled together — priced aggressively at the low end and aimed at small-to-medium businesses that want one tool for every email job. AWS SES is the opposite: a focused, API-only sending primitive priced at infrastructure cost. The migration between them is therefore not just a credentials swap. It is a decision about how much of the email stack you want to own in code versus consume as a managed product.
This guide is for the engineering lead running that decision and the platform team executing it.
## Where Elastic Email and SES Sit in the Market
Elastic Email started as a low-cost SMTP relay and has expanded over the years into a marketing-suite competitor. The product surface today includes:
- **Email API** — transactional REST API and SMTP relay
- **Marketing app** — drag-and-drop email builder, contact lists, segments, A/B testing
- **Automation** — visual workflow editor for drip campaigns and behavior triggers
- **Verifications** — list cleaning and address validation
- **Landing pages and forms** — basic web capture surfaces
The breadth is part of the appeal for teams that want one bill, one login, and one vendor. The breadth is also why migration is more involved than it looks — each capability you used has to be replaced, replicated, or retired.
AWS SES is a single-purpose service: send mail, receive mail, surface events. There is no UI for non-engineers, no marketing builder, no contact list, no automation engine. Anything beyond raw send-and-event capability is something you build, integrate, or replace with another tool.
## The Pricing Comparison Is Plan-Specific
Elastic Email's pricing fragments across product surfaces, which makes the comparison less direct than other ESPs.
**Email API (transactional / SMTP relay):**
| Volume | Elastic Email Email API | AWS SES | Monthly Difference |
| ---------------- | ------------------------ | ------------- | ------------------- |
| 10,000 emails | $9/month (Pay-as-you-go) | $1.00/month | $8.00 SES savings |
| 50,000 emails | $40/month (volume tier) | $5.00/month | $35.00 SES savings |
| 100,000 emails | $75/month (volume tier) | $10.00/month | $65.00 SES savings |
| 500,000 emails | ~$250/month | $50.00/month | $200.00 SES savings |
| 1,000,000 emails | ~$450/month | $100.00/month | $350.00 SES savings |
**Marketing plans (builder + contacts + automation):**
| Plan tier | Elastic Email Marketing | Equivalent on AWS |
| --------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
| Up to 10,000 contacts, ~50K sends/month | $15–25/month | SES $5/month + DynamoDB pennies + Step Functions free tier — but engineering build required |
| 50,000 contacts, ~250K sends/month | $75–120/month | SES $25/month + small DynamoDB cost + custom builder UI |
| 100,000+ contacts, automation flows | $200+/month | SES + Step Functions + custom contact admin UI |
The Email API comparison is straightforward — SES wins on per-message cost at every tier. The Marketing comparison is not a direct cost question; it is a build-versus-buy question. Elastic Email Marketing bundles a product surface (the visual builder, the contact admin, the campaign reports) that has no SES equivalent. The honest framing for the marketing side is: how much engineering investment is your team willing to make to own that surface?
## API Migration: Elastic Email REST → AWS SES
Elastic Email's REST API is straightforward; the migration to SES is a conceptual remap rather than a major refactor.
| Elastic Email API | AWS SES Equivalent | Notes |
| --------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
| `POST /v4/emails` | `SendEmailCommand` / `SendBulkEmailCommand` | Core send operation |
| `Recipients.To[]` | `Destination.ToAddresses[]` | Direct field mapping |
| `Content.From`, `Content.Subject` | `Source`, `Message.Subject` | Same fields |
| `Content.Body[]` with `ContentType: HTML/PlainText` | `Message.Body.Html` / `Message.Body.Text` | Both formats supported; SES requires explicit subobjects |
| `MergeData[]` (template variables) | Application-rendered HTML or SES template variables | Move template rendering to app layer |
| `Tracking.Open`, `Tracking.Click` | Configuration Set with tracking enabled | Same capability, configured at the Configuration Set level |
| `Tags[]` (campaign tags) | Configuration Set tags + `MessageTag` array | For event filtering and CloudWatch breakdowns |
| API key in `X-ElasticEmail-ApiKey` header | IAM credentials or SES SMTP credentials | Prefer IAM in production |
| SMTP (smtp.elasticemail.com, port 2525) | `email-smtp.[region].amazonaws.com`, port 587 | Drop-in SMTP credential swap |
| `POST /v4/emails/transactional` | Same `SendEmailCommand` with transactional Configuration Set | Stream isolation in SES is per-Configuration Set |
For teams using Elastic Email's SMTP relay, the migration is a credentials swap. Update the SMTP host, port (SES uses 587 with STARTTLS or 465 with TLS), and credentials. No code changes. For teams using the REST API, the migration is a refactor of the send call and any helper code that depends on Elastic Email-specific JSON fields.
## Templates: Elastic Email Builder → Code-Owned Templates
Elastic Email's drag-and-drop builder produces HTML templates stored in the Elastic Email account. These templates use a proprietary merge-tag syntax (`{merge_field}`) and are managed through the UI. SES has no template builder — its template feature supports only basic `{{variable}}` substitution with no conditionals or layouts, and most teams skip the SES template feature entirely.
The recommended pattern is to move template rendering to your application code using one of three libraries:
- **React Email** — modern React-component-based templating, used heavily by JavaScript-shop teams
- **MJML** — markup language that compiles to responsive HTML, framework-agnostic
- **Handlebars or Mustache** — minimal templating layer that works for simple variable substitution
Migration steps for an Elastic Email-builder template:
1. Open the Elastic Email template in the builder. Export or copy the rendered HTML.
2. Translate the merge-tag syntax (`{first_name}` → `{{firstName}}` for Handlebars, or `props.firstName` for React Email).
3. Re-test the rendered output across major email clients (Apple Mail, Gmail web, Outlook on Windows, Outlook for iOS). Outlook on Windows is the client most likely to break a builder-exported template because of its idiosyncratic CSS rendering.
4. Add the template to version control next to the code that triggers it.
For organizations with a marketing team that cannot read code, splitting transactional (code-owned templates) from marketing (kept on a marketing-builder platform) is a defensible architecture. Customer.io, Loops, Bento, and Resend all maintain a builder UI while sending through SES under the hood — you get the cost benefit of SES on the marketing side without forcing marketing operations into a code-only workflow.
## Contact Lists, Segments, and Subscriber Management
This is the largest hidden lift in the migration if you used Elastic Email's marketing surface. Elastic Email's contact and segment manager handles subscribe/unsubscribe state, custom fields, segment queries, and double-opt-in flows. SES has none of this.
The replacement architecture for an in-house contact store on AWS:
- **DynamoDB** — primary contact table. Partition key `email`, attributes for `status` (subscribed, unsubscribed, bounced, complained), `lists[]`, `custom_fields`, `subscribed_at`, `last_engaged_at`, `tags[]`. GSIs on `status` and on `last_engaged_at` for segment queries.
- **API Gateway + Lambda** — a small API for the marketing-facing admin surface. Endpoints for subscribe, unsubscribe, list-membership changes, segment evaluation.
- **List-Unsubscribe handler** — Lambda fronted by API Gateway that processes the one-click unsubscribe required by Gmail and Yahoo for any sender above 5,000 messages per day. The unsubscribe URL goes in the `List-Unsubscribe` header on every marketing send.
- **Bounce/complaint sync** — SES SNS events feed into the same table; hard bounces and complaints flip status automatically.
- **Segment query layer** — for simple segments, DynamoDB GSI queries are sufficient. For complex segments (event-based, behavioral), back the segment layer with Athena over event history in S3.
Migration data flow for an existing Elastic Email contact list:
1. Export the contact list via Elastic Email's REST API (`GET /v4/contacts`)
2. Capture all custom fields, list memberships, and subscription status
3. Bulk-load into DynamoDB with status preserved
4. Add hard-bounced and complained addresses to the SES account-level suppression list via `PutSuppressedDestination`
5. Cut over send code to read recipients from the new contact store
The engineering effort for the contact store, admin Lambda, and segment query layer is typically two to four weeks for one engineer. Add another two to four weeks if you need a marketing-team-friendly admin UI.
## Automation Flows: Elastic Email Workflows → Step Functions + EventBridge
Elastic Email's visual automation editor is the marketing surface most expensive to replace. There is no SES equivalent and no off-the-shelf AWS-native tool with the same drag-and-drop UX.
The native AWS pattern for replicating drip campaigns and behavior-triggered email flows:
```
Trigger source (signup, purchase, behavior event)
↓
EventBridge custom event bus
↓ rule matches event pattern
↓
Step Functions state machine (one per automation flow)
↓ Wait state (delay, e.g., "wait 3 days")
↓ Choice state (branch on contact status)
↓ Task state — invoke send Lambda
↓
Lambda — render template, check suppression, call SES
SES → recipient
```
Key practices:
- **One Step Functions state machine per automation flow.** Welcome series, abandoned cart, re-engagement — each gets its own state machine with its own execution history. Visual replay of any execution is built in.
- **EventBridge as the trigger bus.** Application emits events; EventBridge routes to the right state machine. New automations are new EventBridge rules, not application code changes.
- **Per-step suppression check.** Before any Task state that calls SES, check the contact's status, list-membership, and frequency cap. Anyone unsubscribed since the flow started should fall out of the flow at the next checkpoint.
- **DynamoDB for flow-level state.** Beyond what Step Functions tracks per execution, store flow-completion timestamps and per-contact flow history for reporting.
For teams without the engineering capacity to rebuild automation flows in Step Functions, splitting the architecture is sensible — keep automations on a marketing automation platform that integrates with SES (Customer.io, Loops, Beehiiv) and use SES directly only for transactional traffic.
## Domain Verification, DKIM, DMARC, and the Authentication Stack
Elastic Email walks new customers through DKIM and SPF setup with a domain verification UI. SES asks for the same records but exposes more knobs. In 2026 — Gmail, Yahoo, and Microsoft now treat unauthenticated mail from any sender above 5,000 messages per day as effectively undeliverable — getting authentication right before the first production send is non-negotiable.
**SPF.** Add `include:amazonses.com` to your sending domain's TXT record. If you keep Elastic Email running during the cutover window, your record looks like `v=spf1 include:_spf.elasticemail.com include:amazonses.com -all` until you fully cut over. Watch the 10-DNS-lookup limit; chained `include:` directives silently break SPF.
**DKIM.** Verify the domain in SES, enable Easy DKIM, and publish the three CNAME records SES generates. Easy DKIM rotates keys automatically. During parallel sending, Elastic Email's DKIM selector and SES's three CNAMEs coexist without conflict — both signatures validate independently because they use different selectors.
**DMARC.** Publish `_dmarc.yourdomain.com` with `v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com` from day one to start collecting aggregate reports. Move to `p=quarantine` after two to four weeks of clean reports, then to `p=reject` once every legitimate sender — transactional, marketing, calendar invites, vendor notifications, internal tooling — is aligned.
**Custom MAIL FROM domain.** Set up a custom MAIL FROM subdomain (e.g., `mail.acme.com`) and publish the SES-provided MX and SPF records for it. This makes SPF alignment with your visible From address explicit, which removes a class of subtle DMARC failures that surface only after `p=reject` enforcement.
**BIMI.** Once enforcement is stable and you have a Verified Mark Certificate from Entrust or DigiCert, publish a BIMI record so your logo renders next to messages in Gmail, Apple Mail, and Yahoo.
**Common Elastic Email DNS cleanup mistakes after cutover:**
- Leaving the Elastic Email tracking subdomain CNAME (`track.yourdomain.com`) live after sending stops — orphaned tracking causes dead links in archived emails and pollutes DMARC reports
- Leaving Elastic Email return-path records on the domain when the new MAIL FROM domain is on SES — bounce signal can split between the two infrastructures
- Forgetting to update unsubscribe links in evergreen automation flows that customers may receive after the Elastic Email cutover
## Reputation: The Reset You Are Doing on Purpose
Elastic Email customers send from shared IP pools that include a wide range of senders. The reputation built on those IPs is not yours; it is the pool's. After cutover, you start from zero on either the SES shared pool or your own dedicated IPs. For most senders this is good news — you trade an inherited reputation (whatever it was) for one you control.
**Migrating to SES shared IPs.** Suitable for transactional senders under 500,000 emails per month. There is no formal warming required, but mailbox providers still observe sending velocity per domain. Ramp gradually over 7–10 days rather than spiking from zero to full volume on day one.
**Migrating to SES dedicated IPs.** SES dedicated IPs cost $24.95 per IP per month — substantially cheaper than the dedicated-IP add-on on Elastic Email Pro plans. The trade-off is that you handle warming yourself.
| Day Range | Daily Volume Cap | Notes |
| ---------- | ------------------ | ---------------------------------------- |
| Days 1–3 | 200 emails/day | Send to most engaged segment only |
| Days 4–7 | 1,000 emails/day | Watch bounce rate (target <2%) |
| Days 8–14 | 10,000 emails/day | Add second engagement tier |
| Days 15–21 | 50,000 emails/day | Monitor complaint rate (target <0.1%) |
| Days 22–30 | Full target volume | Reputation established |
Keep Elastic Email running in parallel for non-engaged segments during the warming window. The dual-vendor month is the cost of insurance against a deliverability dip that can take 60+ days to recover from.
**List hygiene before cutover.** This is the single highest-leverage activity in the migration. Elastic Email's broader sender base means some senders on the shared pool ride looser hygiene than you would; don't carry that habit into your own SES reputation.
- Run an address validation pass on the active list. Elastic Email's own Verifications product will do this; if you used it recently, the list is already clean. If not, validate before importing.
- Suppress addresses that have not engaged in the last 6–12 months for marketing streams. Re-engagement campaigns belong on the legacy provider, not on a fresh SES IP.
- Block role addresses (`info@`, `support@`, `sales@`) and disposable domains (Mailinator, 10minutemail, Guerrilla Mail) from marketing streams.
- Bulk-import all hard-bounced and complained addresses from Elastic Email to the SES account-level suppression list before the first production send.
## Step-by-Step Migration Plan
A clean Elastic Email-to-SES cutover for a transactional product domain takes one to two weeks. A full migration including the marketing builder, contact lists, and automation flows is six to twelve weeks depending on the breadth of marketing surface in use.
**Phase 1 — Inventory and decision (Week 1)**
1. Catalog every Elastic Email feature in active use — Email API, SMTP relay, marketing builder, contact lists, segments, automations, landing pages, verifications.
2. Decide for each feature: migrate to AWS-native equivalent, replace with a third-party tool, or retire.
3. The honest decision tree: transactional always migrates to SES; marketing builder either gets replaced with code-owned templates (engineering team) or stays on a marketing-specific platform (marketing team); automations either get rebuilt in Step Functions or stay on a marketing automation platform.
**Phase 2 — Domain and SES setup (Week 1)**
1. Verify the sending domain in SES. Publish DKIM, SPF, DMARC, and custom MAIL FROM records. Leave Elastic Email records in place.
2. Move out of the SES sandbox by submitting a production access request. Expect 24–48 hours.
3. Create one Configuration Set per send category — `transactional`, `notifications`, `marketing`. Wire each to a Kinesis Firehose event destination writing to S3.
**Phase 3 — Transactional code refactor (Week 1–2)**
1. Replace Elastic Email API client with AWS SDK SES client.
2. Wrap every send site behind a `sendEmail()` helper that accepts a template, recipient, subject, category. Add a feature flag to route by category for phased rollout.
3. Move template rendering to React Email, MJML, or Handlebars. Verify rendered output matches across major clients.
4. Implement per-send checks: account-level suppression cache, frequency cap, idempotency key, quiet-hours preference.
**Phase 4 — Event pipeline (Week 2)**
1. Subscribe each Configuration Set to Firehose; partition S3 by `year/month/day/category`.
2. Build a small Lambda/Node.js API that queries Athena for activity logs, per-category metrics, bounce and complaint detail.
3. Add CloudWatch alarms on bounce rate (>2%), complaint rate (>0.1%), send-rate anomalies (10x deviation from 7-day rolling average).
**Phase 5 — Marketing surface (Week 3+, optional)**
1. Stand up the contact store (DynamoDB + admin Lambda) if you decided to rebuild internally. Otherwise, sign up for the marketing platform you chose and start the parallel migration.
2. Migrate contact and list data via Elastic Email API export.
3. Rebuild the highest-volume two or three automation flows in Step Functions. Lower-volume flows can be retired or rewritten later.
4. Run a small broadcast (1,000 recipients) end-to-end to validate sending, tracking, and unsubscribe handling.
**Phase 6 — Cutover and decommission (Final week)**
1. Flip the feature flag for transactional traffic. Keep Elastic Email running for 24–48 hours as fast rollback.
2. Watch CloudWatch dashboards and Elastic Email reports in parallel. Investigate any divergence immediately.
3. After 7 clean days, scale down Elastic Email plan, rotate API keys, remove Elastic Email's SPF include from DNS.
4. After 30 clean days, close the Elastic Email account.
## Common Migration Challenges
**Plan-tier confusion at export time.** Elastic Email's API access varies by plan. Pay-as-you-go and lower marketing tiers have rate limits on contact and event exports that make a same-day bulk migration impossible. Either upgrade temporarily for the export window or paginate exports across multiple days. Plan for this in the migration timeline.
**Automation re-modeling.** Automations built in Elastic Email's visual editor often hide branching logic and time-based conditions that are not obvious from a screenshot. Document the full state machine of each automation before rebuilding. The most common failure mode is rebuilding the happy path and missing the "user unsubscribed mid-flow" edge case.
**Unsubscribe link rewriting in evergreen automations.** If a 30-day drip series has emails already in flight when you cut over, recipients receiving day-21 of the sequence may click an unsubscribe link that points to the old Elastic Email handler. Either keep Elastic Email's unsubscribe handler live for 60+ days or pre-rewrite all in-flight sends to point to the new handler.
**Mixed shared-pool reputation residue.** If your domain's previous reputation was poor on Elastic Email's pool (high complaint rate, sustained list-quality issues), starting fresh on SES does not guarantee clean placement at major mailbox providers. Gmail and Outlook track reputation by sending domain across IPs. Fix list hygiene first; the IP change is downstream of the underlying reputation problem.
**Apple MPP open inflation.** Elastic Email's reports already over-count opens because Apple Mail Privacy Protection prefetches every image. After migration to SES with raw event capture, the over-counting becomes more visible because you see every individual prefetch. Filter MPP opens (identifiable by the `User-Agent: Mail/MPP` and Apple-owned IP ranges) before feeding engagement data into segmentation logic.
**SMTP port differences.** Elastic Email defaults to port 2525 (and supports 25, 587, 465). SES SMTP uses 587 with STARTTLS or 465 with TLS. If your application allowed outbound port 2525 only, expand the egress rules before cutover.
**Custom field mapping at template render time.** Templates exported from the Elastic Email builder may use merge fields with names that do not match your code's variable names. Build a translation layer in the `sendEmail()` helper that maps internal contact-record field names to the variable names templates expect.
## Deliverability Discipline After the Cutover
SES does not paper over reputation problems. Three operational practices separate teams that maintain or improve placement after migration from teams that watch placement decay over six months.
**Stream isolation through Configuration Sets and IP pools.** Run separate Configuration Sets — and ideally separate dedicated IP pools — for transactional, product activity, and marketing traffic. A complaint spike on a marketing broadcast cannot reach password-reset deliverability if the IPs are isolated. Application code selects the correct Configuration Set per send category; tag every send with `category` for downstream filtering.
**Engagement-based send eligibility.** Maintain a `last_engaged_at` timestamp per recipient updated nightly from open and click events. Suppress recipients with no engagement in 90 days from marketing streams (180 days for transactional). Gmail and Microsoft weight recent positive engagement heavily. A smaller, hotter list lifts placement for the entire domain — typically from the mid-80s to the mid-90s within 30 days.
**Per-domain placement testing.** Send to a small panel of monitored seed inboxes (Gmail, Outlook, Yahoo, iCloud) on every major broadcast. Inbox vs. promotions vs. spam folder placement is your earliest warning system — much earlier than the bounce rate metric, which only spikes after reputation damage is already done.
**Frequency caps and quiet hours.** Build per-recipient frequency caps into the send Lambda — no more than N marketing emails per week, no transactional emails outside business hours unless the message is genuinely time-critical. Recipients who feel oversent unsubscribe; recipients who unsubscribe damage reputation for everyone else on your domain.
## Production Event Pipeline: SES → Kinesis Firehose → S3 → Node.js API
Elastic Email's hosted dashboard gives you per-email activity logs, basic charts, and campaign reports. After cutover you replicate that surface with the SES event firehose. The architecture most production SES senders converge on:
```
SES Configuration Set
↓ (event destination)
Kinesis Data Firehose
↓ (60-second buffer or 5 MB)
S3 (Parquet, partitioned by year/month/day/category)
↓
Athena ← Node.js API ← Dashboard / suppression service / alerts
```
Subscribe each Configuration Set to a Firehose delivery stream. Enable dynamic partitioning so events split by category, sending IP, or recipient domain at write time. Lifecycle the bucket: Standard for 30 days (hot analytics window), Standard-IA at 30 days, Glacier Flexible Retrieval at 180 days. Raw events are the cheapest part of the stack and the most useful during deliverability investigations.
The Node.js API layer is small — a Fastify or Hono service, an Athena query helper, and four to six endpoints:
- `GET /messages?recipient=foo@bar.com` — Elastic-Email-style activity log
- `GET /campaigns/:id/metrics` — open, click, bounce, complaint by campaign tag
- `GET /deliverability?domain=gmail.com&days=7` — per-receiver placement signals
- `GET /bounces?subType=Suppressed&days=1` — operational alerting feed
- `POST /webhooks/slack` — bounce/complaint fan-out
- `POST /webhooks/replay/:event_id` — re-process a single event for debugging
**Filtering bot, prefetch, and security-scanner traffic.** Apple MPP, Microsoft Defender link scanning, Gmail image proxies, and corporate security gateways all generate engagement events that have nothing to do with a human reading the message. Without filtering, your "open rate" double-counts machine activity and engagement-based suppression suppresses real subscribers whose mail clients prefetch links. Tools that score SES events for human vs. automated activity sit naturally between the Firehose stream and the Node.js API; teams that skip this layer often discover the gap only after a quarter of degraded marketing placement.
## Related Comparisons
Explore other technical comparisons:
- [SendGrid to AWS SES](/compare/sendgrid-to-aws-ses/)
- [Mailgun to AWS SES](/compare/mailgun-to-aws-ses/)
- [Postmark to AWS SES](/compare/postmark-to-aws-ses/)
- [SparkPost to AWS SES](/compare/sparkpost-to-aws-ses/)
- [Resend to AWS SES](/compare/resend-to-aws-ses/)
## Why Choose FactualMinds for Your Email Migration
FactualMinds is an **AWS Select Tier Consulting Partner** specializing in email infrastructure migration. We have executed SendGrid, Mailgun, Postmark, SparkPost, Resend, and Elastic Email to AWS SES migrations and know exactly where teams get stuck.
- **Email migration experts** — we handle domain verification, DKIM, bounce architecture, IP warming
- **Assessment-first approach** — we map your current state before writing a line of infrastructure code
- **Zero-downtime cutover planning included** — no failed deliveries during migration
- **AWS Select Tier Partner** — [verified on AWS Partner Network](https://partners.amazonaws.com/partners/001aq000008su2EAAQ/Factual%20Minds)
---TL;DR. If you are migrating only transactional, plan 1–2 weeks on the SES side. If you are also moving the marketing builder, contact lists, and automation flows, plan 4–6 weeks plus a marketing-platform decision (own the builder in code, or pair SES with a marketing-only tool like Customer.io or Klaviyo).
Elastic Email occupies a different niche than SendGrid, Mailgun, or Postmark. It is a multi-purpose platform — transactional API, marketing builder, contact manager, automation engine, and SMTP relay all bundled together — priced aggressively at the low end and aimed at small-to-medium businesses that want one tool for every email job. AWS SES is the opposite: a focused, API-only sending primitive priced at infrastructure cost. The migration between them is therefore not just a credentials swap. It is a decision about how much of the email stack you want to own in code versus consume as a managed product.
This guide is for the engineering lead running that decision and the platform team executing it.
Where Elastic Email and SES Sit in the Market
Elastic Email started as a low-cost SMTP relay and has expanded over the years into a marketing-suite competitor. The product surface today includes:
- Email API — transactional REST API and SMTP relay
- Marketing app — drag-and-drop email builder, contact lists, segments, A/B testing
- Automation — visual workflow editor for drip campaigns and behavior triggers
- Verifications — list cleaning and address validation
- Landing pages and forms — basic web capture surfaces
The breadth is part of the appeal for teams that want one bill, one login, and one vendor. The breadth is also why migration is more involved than it looks — each capability you used has to be replaced, replicated, or retired.
AWS SES is a single-purpose service: send mail, receive mail, surface events. There is no UI for non-engineers, no marketing builder, no contact list, no automation engine. Anything beyond raw send-and-event capability is something you build, integrate, or replace with another tool.
The Pricing Comparison Is Plan-Specific
Elastic Email’s pricing fragments across product surfaces, which makes the comparison less direct than other ESPs.
Email API (transactional / SMTP relay):
| Volume | Elastic Email Email API | AWS SES | Monthly Difference |
|---|---|---|---|
| 10,000 emails | $9/month (Pay-as-you-go) | $1.00/month | $8.00 SES savings |
| 50,000 emails | $40/month (volume tier) | $5.00/month | $35.00 SES savings |
| 100,000 emails | $75/month (volume tier) | $10.00/month | $65.00 SES savings |
| 500,000 emails | ~$250/month | $50.00/month | $200.00 SES savings |
| 1,000,000 emails | ~$450/month | $100.00/month | $350.00 SES savings |
Marketing plans (builder + contacts + automation):
| Plan tier | Elastic Email Marketing | Equivalent on AWS |
|---|---|---|
| Up to 10,000 contacts, ~50K sends/month | $15–25/month | SES $5/month + DynamoDB pennies + Step Functions free tier — but engineering build required |
| 50,000 contacts, ~250K sends/month | $75–120/month | SES $25/month + small DynamoDB cost + custom builder UI |
| 100,000+ contacts, automation flows | $200+/month | SES + Step Functions + custom contact admin UI |
The Email API comparison is straightforward — SES wins on per-message cost at every tier. The Marketing comparison is not a direct cost question; it is a build-versus-buy question. Elastic Email Marketing bundles a product surface (the visual builder, the contact admin, the campaign reports) that has no SES equivalent. The honest framing for the marketing side is: how much engineering investment is your team willing to make to own that surface?
API Migration: Elastic Email REST → AWS SES
Elastic Email’s REST API is straightforward; the migration to SES is a conceptual remap rather than a major refactor.
| Elastic Email API | AWS SES Equivalent | Notes |
|---|---|---|
POST /v4/emails | SendEmailCommand / SendBulkEmailCommand | Core send operation |
Recipients.To[] | Destination.ToAddresses[] | Direct field mapping |
Content.From, Content.Subject | Source, Message.Subject | Same fields |
Content.Body[] with ContentType: HTML/PlainText | Message.Body.Html / Message.Body.Text | Both formats supported; SES requires explicit subobjects |
MergeData[] (template variables) | Application-rendered HTML or SES template variables | Move template rendering to app layer |
Tracking.Open, Tracking.Click | Configuration Set with tracking enabled | Same capability, configured at the Configuration Set level |
Tags[] (campaign tags) | Configuration Set tags + MessageTag array | For event filtering and CloudWatch breakdowns |
API key in X-ElasticEmail-ApiKey header | IAM credentials or SES SMTP credentials | Prefer IAM in production |
| SMTP (smtp.elasticemail.com, port 2525) | email-smtp.[region].amazonaws.com, port 587 | Drop-in SMTP credential swap |
POST /v4/emails/transactional | Same SendEmailCommand with transactional Configuration Set | Stream isolation in SES is per-Configuration Set |
For teams using Elastic Email’s SMTP relay, the migration is a credentials swap. Update the SMTP host, port (SES uses 587 with STARTTLS or 465 with TLS), and credentials. No code changes. For teams using the REST API, the migration is a refactor of the send call and any helper code that depends on Elastic Email-specific JSON fields.
Templates: Elastic Email Builder → Code-Owned Templates
Elastic Email’s drag-and-drop builder produces HTML templates stored in the Elastic Email account. These templates use a proprietary merge-tag syntax ({merge_field}) and are managed through the UI. SES has no template builder — its template feature supports only basic {{variable}} substitution with no conditionals or layouts, and most teams skip the SES template feature entirely.
The recommended pattern is to move template rendering to your application code using one of three libraries:
- React Email — modern React-component-based templating, used heavily by JavaScript-shop teams
- MJML — markup language that compiles to responsive HTML, framework-agnostic
- Handlebars or Mustache — minimal templating layer that works for simple variable substitution
Migration steps for an Elastic Email-builder template:
- Open the Elastic Email template in the builder. Export or copy the rendered HTML.
- Translate the merge-tag syntax (
{first_name}→{{firstName}}for Handlebars, orprops.firstNamefor React Email). - Re-test the rendered output across major email clients (Apple Mail, Gmail web, Outlook on Windows, Outlook for iOS). Outlook on Windows is the client most likely to break a builder-exported template because of its idiosyncratic CSS rendering.
- Add the template to version control next to the code that triggers it.
For organizations with a marketing team that cannot read code, splitting transactional (code-owned templates) from marketing (kept on a marketing-builder platform) is a defensible architecture. Customer.io, Loops, Bento, and Resend all maintain a builder UI while sending through SES under the hood — you get the cost benefit of SES on the marketing side without forcing marketing operations into a code-only workflow.
Contact Lists, Segments, and Subscriber Management
This is the largest hidden lift in the migration if you used Elastic Email’s marketing surface. Elastic Email’s contact and segment manager handles subscribe/unsubscribe state, custom fields, segment queries, and double-opt-in flows. SES has none of this.
The replacement architecture for an in-house contact store on AWS:
- DynamoDB — primary contact table. Partition key
email, attributes forstatus(subscribed, unsubscribed, bounced, complained),lists[],custom_fields,subscribed_at,last_engaged_at,tags[]. GSIs onstatusand onlast_engaged_atfor segment queries. - API Gateway + Lambda — a small API for the marketing-facing admin surface. Endpoints for subscribe, unsubscribe, list-membership changes, segment evaluation.
- List-Unsubscribe handler — Lambda fronted by API Gateway that processes the one-click unsubscribe required by Gmail and Yahoo for any sender above 5,000 messages per day. The unsubscribe URL goes in the
List-Unsubscribeheader on every marketing send. - Bounce/complaint sync — SES SNS events feed into the same table; hard bounces and complaints flip status automatically.
- Segment query layer — for simple segments, DynamoDB GSI queries are sufficient. For complex segments (event-based, behavioral), back the segment layer with Athena over event history in S3.
Migration data flow for an existing Elastic Email contact list:
- Export the contact list via Elastic Email’s REST API (
GET /v4/contacts) - Capture all custom fields, list memberships, and subscription status
- Bulk-load into DynamoDB with status preserved
- Add hard-bounced and complained addresses to the SES account-level suppression list via
PutSuppressedDestination - Cut over send code to read recipients from the new contact store
The engineering effort for the contact store, admin Lambda, and segment query layer is typically two to four weeks for one engineer. Add another two to four weeks if you need a marketing-team-friendly admin UI.
Automation Flows: Elastic Email Workflows → Step Functions + EventBridge
Elastic Email’s visual automation editor is the marketing surface most expensive to replace. There is no SES equivalent and no off-the-shelf AWS-native tool with the same drag-and-drop UX.
The native AWS pattern for replicating drip campaigns and behavior-triggered email flows:
Trigger source (signup, purchase, behavior event)
↓
EventBridge custom event bus
↓ rule matches event pattern
↓
Step Functions state machine (one per automation flow)
↓ Wait state (delay, e.g., "wait 3 days")
↓ Choice state (branch on contact status)
↓ Task state — invoke send Lambda
↓
Lambda — render template, check suppression, call SES
SES → recipientKey practices:
- One Step Functions state machine per automation flow. Welcome series, abandoned cart, re-engagement — each gets its own state machine with its own execution history. Visual replay of any execution is built in.
- EventBridge as the trigger bus. Application emits events; EventBridge routes to the right state machine. New automations are new EventBridge rules, not application code changes.
- Per-step suppression check. Before any Task state that calls SES, check the contact’s status, list-membership, and frequency cap. Anyone unsubscribed since the flow started should fall out of the flow at the next checkpoint.
- DynamoDB for flow-level state. Beyond what Step Functions tracks per execution, store flow-completion timestamps and per-contact flow history for reporting.
For teams without the engineering capacity to rebuild automation flows in Step Functions, splitting the architecture is sensible — keep automations on a marketing automation platform that integrates with SES (Customer.io, Loops, Beehiiv) and use SES directly only for transactional traffic.
Domain Verification, DKIM, DMARC, and the Authentication Stack
Elastic Email walks new customers through DKIM and SPF setup with a domain verification UI. SES asks for the same records but exposes more knobs. In 2026 — Gmail, Yahoo, and Microsoft now treat unauthenticated mail from any sender above 5,000 messages per day as effectively undeliverable — getting authentication right before the first production send is non-negotiable.
SPF. Add include:amazonses.com to your sending domain’s TXT record. If you keep Elastic Email running during the cutover window, your record looks like v=spf1 include:_spf.elasticemail.com include:amazonses.com -all until you fully cut over. Watch the 10-DNS-lookup limit; chained include: directives silently break SPF.
DKIM. Verify the domain in SES, enable Easy DKIM, and publish the three CNAME records SES generates. Easy DKIM rotates keys automatically. During parallel sending, Elastic Email’s DKIM selector and SES’s three CNAMEs coexist without conflict — both signatures validate independently because they use different selectors.
DMARC. Publish _dmarc.yourdomain.com with v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com from day one to start collecting aggregate reports. Move to p=quarantine after two to four weeks of clean reports, then to p=reject once every legitimate sender — transactional, marketing, calendar invites, vendor notifications, internal tooling — is aligned.
Custom MAIL FROM domain. Set up a custom MAIL FROM subdomain (e.g., mail.acme.com) and publish the SES-provided MX and SPF records for it. This makes SPF alignment with your visible From address explicit, which removes a class of subtle DMARC failures that surface only after p=reject enforcement.
BIMI. Once enforcement is stable and you have a Verified Mark Certificate from Entrust or DigiCert, publish a BIMI record so your logo renders next to messages in Gmail, Apple Mail, and Yahoo.
Common Elastic Email DNS cleanup mistakes after cutover:
- Leaving the Elastic Email tracking subdomain CNAME (
track.yourdomain.com) live after sending stops — orphaned tracking causes dead links in archived emails and pollutes DMARC reports - Leaving Elastic Email return-path records on the domain when the new MAIL FROM domain is on SES — bounce signal can split between the two infrastructures
- Forgetting to update unsubscribe links in evergreen automation flows that customers may receive after the Elastic Email cutover
Reputation: The Reset You Are Doing on Purpose
Elastic Email customers send from shared IP pools that include a wide range of senders. The reputation built on those IPs is not yours; it is the pool’s. After cutover, you start from zero on either the SES shared pool or your own dedicated IPs. For most senders this is good news — you trade an inherited reputation (whatever it was) for one you control.
Migrating to SES shared IPs. Suitable for transactional senders under 500,000 emails per month. There is no formal warming required, but mailbox providers still observe sending velocity per domain. Ramp gradually over 7–10 days rather than spiking from zero to full volume on day one.
Migrating to SES dedicated IPs. SES dedicated IPs cost $24.95 per IP per month — substantially cheaper than the dedicated-IP add-on on Elastic Email Pro plans. The trade-off is that you handle warming yourself.
| Day Range | Daily Volume Cap | Notes |
|---|---|---|
| Days 1–3 | 200 emails/day | Send to most engaged segment only |
| Days 4–7 | 1,000 emails/day | Watch bounce rate (target <2%) |
| Days 8–14 | 10,000 emails/day | Add second engagement tier |
| Days 15–21 | 50,000 emails/day | Monitor complaint rate (target <0.1%) |
| Days 22–30 | Full target volume | Reputation established |
Keep Elastic Email running in parallel for non-engaged segments during the warming window. The dual-vendor month is the cost of insurance against a deliverability dip that can take 60+ days to recover from.
List hygiene before cutover. This is the single highest-leverage activity in the migration. Elastic Email’s broader sender base means some senders on the shared pool ride looser hygiene than you would; don’t carry that habit into your own SES reputation.
- Run an address validation pass on the active list. Elastic Email’s own Verifications product will do this; if you used it recently, the list is already clean. If not, validate before importing.
- Suppress addresses that have not engaged in the last 6–12 months for marketing streams. Re-engagement campaigns belong on the legacy provider, not on a fresh SES IP.
- Block role addresses (
info@,support@,sales@) and disposable domains (Mailinator, 10minutemail, Guerrilla Mail) from marketing streams. - Bulk-import all hard-bounced and complained addresses from Elastic Email to the SES account-level suppression list before the first production send.
Step-by-Step Migration Plan
A clean Elastic Email-to-SES cutover for a transactional product domain takes one to two weeks. A full migration including the marketing builder, contact lists, and automation flows is six to twelve weeks depending on the breadth of marketing surface in use.
Phase 1 — Inventory and decision (Week 1)
- Catalog every Elastic Email feature in active use — Email API, SMTP relay, marketing builder, contact lists, segments, automations, landing pages, verifications.
- Decide for each feature: migrate to AWS-native equivalent, replace with a third-party tool, or retire.
- The honest decision tree: transactional always migrates to SES; marketing builder either gets replaced with code-owned templates (engineering team) or stays on a marketing-specific platform (marketing team); automations either get rebuilt in Step Functions or stay on a marketing automation platform.
Phase 2 — Domain and SES setup (Week 1)
- Verify the sending domain in SES. Publish DKIM, SPF, DMARC, and custom MAIL FROM records. Leave Elastic Email records in place.
- Move out of the SES sandbox by submitting a production access request. Expect 24–48 hours.
- Create one Configuration Set per send category —
transactional,notifications,marketing. Wire each to a Kinesis Firehose event destination writing to S3.
Phase 3 — Transactional code refactor (Week 1–2)
- Replace Elastic Email API client with AWS SDK SES client.
- Wrap every send site behind a
sendEmail()helper that accepts a template, recipient, subject, category. Add a feature flag to route by category for phased rollout. - Move template rendering to React Email, MJML, or Handlebars. Verify rendered output matches across major clients.
- Implement per-send checks: account-level suppression cache, frequency cap, idempotency key, quiet-hours preference.
Phase 4 — Event pipeline (Week 2)
- Subscribe each Configuration Set to Firehose; partition S3 by
year/month/day/category. - Build a small Lambda/Node.js API that queries Athena for activity logs, per-category metrics, bounce and complaint detail.
- Add CloudWatch alarms on bounce rate (>2%), complaint rate (>0.1%), send-rate anomalies (10x deviation from 7-day rolling average).
Phase 5 — Marketing surface (Week 3+, optional)
- Stand up the contact store (DynamoDB + admin Lambda) if you decided to rebuild internally. Otherwise, sign up for the marketing platform you chose and start the parallel migration.
- Migrate contact and list data via Elastic Email API export.
- Rebuild the highest-volume two or three automation flows in Step Functions. Lower-volume flows can be retired or rewritten later.
- Run a small broadcast (1,000 recipients) end-to-end to validate sending, tracking, and unsubscribe handling.
Phase 6 — Cutover and decommission (Final week)
- Flip the feature flag for transactional traffic. Keep Elastic Email running for 24–48 hours as fast rollback.
- Watch CloudWatch dashboards and Elastic Email reports in parallel. Investigate any divergence immediately.
- After 7 clean days, scale down Elastic Email plan, rotate API keys, remove Elastic Email’s SPF include from DNS.
- After 30 clean days, close the Elastic Email account.
Common Migration Challenges
Plan-tier confusion at export time. Elastic Email’s API access varies by plan. Pay-as-you-go and lower marketing tiers have rate limits on contact and event exports that make a same-day bulk migration impossible. Either upgrade temporarily for the export window or paginate exports across multiple days. Plan for this in the migration timeline.
Automation re-modeling. Automations built in Elastic Email’s visual editor often hide branching logic and time-based conditions that are not obvious from a screenshot. Document the full state machine of each automation before rebuilding. The most common failure mode is rebuilding the happy path and missing the “user unsubscribed mid-flow” edge case.
Unsubscribe link rewriting in evergreen automations. If a 30-day drip series has emails already in flight when you cut over, recipients receiving day-21 of the sequence may click an unsubscribe link that points to the old Elastic Email handler. Either keep Elastic Email’s unsubscribe handler live for 60+ days or pre-rewrite all in-flight sends to point to the new handler.
Mixed shared-pool reputation residue. If your domain’s previous reputation was poor on Elastic Email’s pool (high complaint rate, sustained list-quality issues), starting fresh on SES does not guarantee clean placement at major mailbox providers. Gmail and Outlook track reputation by sending domain across IPs. Fix list hygiene first; the IP change is downstream of the underlying reputation problem.
Apple MPP open inflation. Elastic Email’s reports already over-count opens because Apple Mail Privacy Protection prefetches every image. After migration to SES with raw event capture, the over-counting becomes more visible because you see every individual prefetch. Filter MPP opens (identifiable by the User-Agent: Mail/MPP and Apple-owned IP ranges) before feeding engagement data into segmentation logic.
SMTP port differences. Elastic Email defaults to port 2525 (and supports 25, 587, 465). SES SMTP uses 587 with STARTTLS or 465 with TLS. If your application allowed outbound port 2525 only, expand the egress rules before cutover.
Custom field mapping at template render time. Templates exported from the Elastic Email builder may use merge fields with names that do not match your code’s variable names. Build a translation layer in the sendEmail() helper that maps internal contact-record field names to the variable names templates expect.
Deliverability Discipline After the Cutover
SES does not paper over reputation problems. Three operational practices separate teams that maintain or improve placement after migration from teams that watch placement decay over six months.
Stream isolation through Configuration Sets and IP pools. Run separate Configuration Sets — and ideally separate dedicated IP pools — for transactional, product activity, and marketing traffic. A complaint spike on a marketing broadcast cannot reach password-reset deliverability if the IPs are isolated. Application code selects the correct Configuration Set per send category; tag every send with category for downstream filtering.
Engagement-based send eligibility. Maintain a last_engaged_at timestamp per recipient updated nightly from open and click events. Suppress recipients with no engagement in 90 days from marketing streams (180 days for transactional). Gmail and Microsoft weight recent positive engagement heavily. A smaller, hotter list lifts placement for the entire domain — typically from the mid-80s to the mid-90s within 30 days.
Per-domain placement testing. Send to a small panel of monitored seed inboxes (Gmail, Outlook, Yahoo, iCloud) on every major broadcast. Inbox vs. promotions vs. spam folder placement is your earliest warning system — much earlier than the bounce rate metric, which only spikes after reputation damage is already done.
Frequency caps and quiet hours. Build per-recipient frequency caps into the send Lambda — no more than N marketing emails per week, no transactional emails outside business hours unless the message is genuinely time-critical. Recipients who feel oversent unsubscribe; recipients who unsubscribe damage reputation for everyone else on your domain.
Production Event Pipeline: SES → Kinesis Firehose → S3 → Node.js API
Elastic Email’s hosted dashboard gives you per-email activity logs, basic charts, and campaign reports. After cutover you replicate that surface with the SES event firehose. The architecture most production SES senders converge on:
SES Configuration Set
↓ (event destination)
Kinesis Data Firehose
↓ (60-second buffer or 5 MB)
S3 (Parquet, partitioned by year/month/day/category)
↓
Athena ← Node.js API ← Dashboard / suppression service / alertsSubscribe each Configuration Set to a Firehose delivery stream. Enable dynamic partitioning so events split by category, sending IP, or recipient domain at write time. Lifecycle the bucket: Standard for 30 days (hot analytics window), Standard-IA at 30 days, Glacier Flexible Retrieval at 180 days. Raw events are the cheapest part of the stack and the most useful during deliverability investigations.
The Node.js API layer is small — a Fastify or Hono service, an Athena query helper, and four to six endpoints:
GET /messages?recipient=foo@bar.com— Elastic-Email-style activity logGET /campaigns/:id/metrics— open, click, bounce, complaint by campaign tagGET /deliverability?domain=gmail.com&days=7— per-receiver placement signalsGET /bounces?subType=Suppressed&days=1— operational alerting feedPOST /webhooks/slack— bounce/complaint fan-outPOST /webhooks/replay/:event_id— re-process a single event for debugging
Filtering bot, prefetch, and security-scanner traffic. Apple MPP, Microsoft Defender link scanning, Gmail image proxies, and corporate security gateways all generate engagement events that have nothing to do with a human reading the message. Without filtering, your “open rate” double-counts machine activity and engagement-based suppression suppresses real subscribers whose mail clients prefetch links. Tools that score SES events for human vs. automated activity sit naturally between the Firehose stream and the Node.js API; teams that skip this layer often discover the gap only after a quarter of degraded marketing placement.
Related Comparisons
Explore other technical comparisons:
Why Choose FactualMinds for Your Email Migration
FactualMinds is an AWS Select Tier Consulting Partner specializing in email infrastructure migration. We have executed SendGrid, Mailgun, Postmark, SparkPost, Resend, and Elastic Email to AWS SES migrations and know exactly where teams get stuck.
- Email migration experts — we handle domain verification, DKIM, bounce architecture, IP warming
- Assessment-first approach — we map your current state before writing a line of infrastructure code
- Zero-downtime cutover planning included — no failed deliveries during migration
- AWS Select Tier Partner — verified on AWS Partner Network
Frequently Asked Questions
Is AWS SES cheaper than Elastic Email?
Why do teams migrate off Elastic Email?
How do I migrate Elastic Email automation flows to AWS?
Will my deliverability improve after moving to AWS SES?
Can AWS SES replace Elastic Email contact lists and segments?
Need Help Migrating to AWS SES?
FactualMinds is an AWS Select Tier Partner specializing in email infrastructure migration. We handle domain verification, Configuration Set architecture, bounce handling, IP warming, and cutover.
