Migration illustration Migration guide

Migrate to Mailtrap from Amazon SES

A complete technical guide to switch from Amazon SES to Mailtrap. Most teams complete the migration in under an hour.

Migration Checklist

  1. Authenticate your domain
    Add and verify your domain before sending. Follow the Domain Setup article at docs.mailtrap.io.

Need some help?

Contact our support and our developers will help you with it.

Need some help?
  1. Get your API token
    Mailtrap auto-generates a token when you add a domain. Find it under Settings → API Tokens. Read more on API tokens.
  2. Update your integration
    Swap your Amazon SES endpoint and credentials for Mailtrap’s (API or SMTP). See the migration sections below.
  3. Migrate templates
    Both platforms use Handlebars, so content migrates directly. See the templates sections below
  4. Migrate suppressions
    Export SES suppressions and import them into Mailtrap via CSV or manually. Click here for more information.
  5. Migrate users
    Add users from the User Management tab and review permissions during migration.
  6. Set up webhooks
    Follow the Mailtrap Webhooks step-by-step guide.
  7. Security and compliance
    Visit the Trust Center page to review Mailtrap’s security practices and compliance standards.

Mailtrap tip

You can use ActionMailer Balancer Ruby gem to proportionally distribute the email sending load between two different sending services (e.g. 70% Amazon SES and 30% Mailtrap) to mitigate the sending risks.

 

Mailtrap tip

Concepts

Before going over the technical details, it’s important to clear up some key concepts.

Sending domains

SES equivalent: Verified Identities (Easy DKIM / BYODKIM)

Both platforms require DNS-based domain authentication with SPF, DKIM, and DMARC. SES calls these “verified identities” and supports:

  • Easy DKIM (AWS generates keys, you add three CNAME records) or 
  • BYODKIM (you supply your own signing keys). 
  • Custom MAIL FROM domain for SPF alignment.

Mailtrap’s domain setup uses the same DNS standards – SPF, DKIM, DMARC. DKIM records can be added alongside SES’s (different selectors, no conflict), and one DMARC record covers both. For SPF, however, you must merge Mailtrap’s include into your existing SPF record rather than adding a separate one.

Separate sending streams

SES equivalent: N/A – single service, single endpoint per region

SES routes all email through one regional endpoint. You can use Configuration Sets to separate tracking and event routing, but the underlying infrastructure is shared.

Mailtrap separates transactional and bulk email into distinct infrastructure:

  • Transactional Stream – For sending user-triggered emails like welcome emails and password resets.
  • Bulk Stream – For sending promotional, marketing emails like newsletters and product updates.

By keeping the sending infrastructures separate, you are able to:

  • Protect your transactional email reputation from the performance of your bulk campaigns
  • Ensure each stream routes through the right IP pools
  • Give mailbox providers the signals they need to categorize and deliver your emails correctly

Email categories

SES equivalent: Configuration Sets + Message Tags (EmailTags)

SES uses two mechanisms for categorizing email. Configuration Sets define where event data goes (CloudWatch, Kinesis Firehose, SNS) and apply sending rules. 

  • Message Tags (EmailTags) are name-value pairs attached to individual sends for filtering events and metrics.
  • Mailtrap uses Email Categories — set via the category field in the API or the X-MT-Category header in SMTP.

Key difference: SES’s tagging system is more granular (multiple key-value pairs per message, tied into AWS’s observability stack). Mailtrap assigns one category string per message, which keeps things simpler for most use cases.

Organization & sub-accounts

SES equivalent: IAM Users, Roles, Policies + Sending Authorization

SES inherits AWS’s IAM model for access control – fine-grained policies, cross-account sending authorization, and role-based access. This is powerful but adds operational complexity, especially for teams that only need email.

Mailtrap offers Organization and sub-account management, which lets you manage complex setups involving multiple teams, clients, environments, or products under a single Organization. To start using the feature, you first need to enable it under the Organization tab.

Note: The feature is available to from Business plan onward.

Terminology comparison

API migration

Authentication

SES and Mailtrap use fundamentally different auth models:

Amazon SESMailtrap
MethodAWS Signature Version 4 (SigV4)Bearer token in Authorization header
CredentialsIAM Access Key ID + Secret Access KeySingle API token
HeaderAuthorization: AWS4-HMAC-SHA256 Credential=… (auto-generated by AWS SDKs)Authorization: Bearer YOUR_API_KEY
ScopePer-regionGlobal

SES auth is tightly coupled to AWS IAM. When migrating, you replace the SigV4 signing logic (or AWS SDK calls) with a single Bearer token header. This significantly simplifies your sending code.

API mapping

API typeAmazon SESMailtrapNotes
Transactional emailPOST https://email.{region}.amazonaws.com/v2/email/outbound-emailsPOST https://send.api.mailtrap.io/api/sendSES is regional; Mailtrap is global. Completely different payload structures
Bulk emailPOST https://email.{region}.amazonaws.com/v2/email/outbound-bulk-emailsPOST https://bulk.api.mailtrap.io/api/sendSES caps at 50 destinations/call; Mailtrap supports 500/batch
Template sendingSame SendEmail with Content.TemplateSame send endpoint with template_uuidSES supports inline templates (no pre-storage needed) via TemplateContent
SuppressionsPUT/GET/DELETE /v2/email/suppression/addressesGET https://mailtrap.io/api/accounts/{account_id}/suppressionsSES also has an AWS-managed Global suppression list you can’t modify
StatsEvent Publishing → CloudWatch / Kinesis FirehoseGET /api/accounts/{account_id}/stats, /stats/domains, /stats/categories, /stats/email_service_providers, and /stats/dateSES requires external AWS services for analytics
Email logsEvent Destinations + Mail Manager ArchiveGET https://mailtrap.io/api/accounts/{account_id}/email_logsSES has no built-in email log viewer – requires CloudWatch, Kinesis, or Mail Manager

Outbound Sending API JSON Field Mapping

FieldAmazon SES (v2)Mailtrap
From emailFromEmailAddressfrom.email
From nameIncluded in FromEmailAddress (RFC 5322 format: “Name” <email>)from.name
To emailDestination.ToAddresses[]to[].email
To nameIncluded in address string (“Name” <email>)to[].name
CCDestination.CcAddresses[]cc[].email
BCCDestination.BccAddresses[]bcc[].email
SubjectContent.Simple.Subject.Datasubject
HTML bodyContent.Simple.Body.Html.Datahtml
Text bodyContent.Simple.Body.Text.Datatext
Character encodingContent.Simple.Subject.Charset, Body.Html.Charset, Body.Text.CharsetN/A – UTF-8 assumed
Category/TagEmailTags[].{Name, Value} (multiple key-value pairs)category (single string)
Configuration setConfigurationSetNameN/A
Custom variablesEmailTagscustom_variables
Reply-ToReplyToAddresses[]reply_to object
Custom headersContent.Simple.Headers[].{Name, Value}headers
Template identifierContent.Template.TemplateName or Content.Template.TemplateArntemplate_uuid
Template variablesContent.Template.TemplateData (escaped JSON string)template_variables (object)
Inline templateContent.Template.TemplateContent.{Subject, Html, Text}N/A – templates must be pre-stored
Open trackingVia Configuration Set event destinationVia account settings or headers
Click trackingVia Configuration Set event destinationVia account settings or headers
AttachmentsContent.Simple.Attachments[].{FileName, RawContent, ContentType}attachments:[{content, filename, type, disposition}]
Inline imagesContent.Simple.Attachments[] with ContentId setattachments:[{content, filename, type, disposition:”inline”,content_id}]
Bounce handlingFeedbackForwardingEmailAddressHandled via webhooks
Sending authorizationFromEmailAddressIdentityArn, FeedbackForwardingEmailAddressIdentityArnN/A – determined by API token scope
List managementListManagementOptions.{ContactListName, TopicName}N/A
Raw MIMEContent.Raw.Data (base64-encoded MIME)N/A – use structured fields
Sending streamN/A – single regional endpointDetermined by endpoint: send.api (transactional) vs bulk.api (bulk)

Code snippets

  • Mailtrap cURL SDK code snippet cURL
Amazon SES Amazon SES
aws sesv2 send-email \
  --from-email-address "Sender Name <sender@yourdomain.com>" \
  --destination '{"ToAddresses": ["recipient@example.com"]}' \
  --content '{
    "Simple": {
      "Subject": {"Data": "Welcome aboard"},
      "Body": {
        "Html": {"Data": "<h1>Hello</h1><p>Welcome to the platform.</p>"},
        "Text": {"Data": "Hello. Welcome to the platform."}
      }
    }
  }' \
  --email-tags '[{"Name": "category", "Value": "welcome"}]' \
  --region us-east-1
Mailtrap Mailtrap

Copy

curl -X POST https://send.api.mailtrap.io/api/send \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "from": {"email": "sender@example.com"},
    "to": [{"email": "recipient@example.com"}],
    "subject": "Hello from Mailtrap",
    "text": "Welcome to Mailtrap!"
  }'

Note: SES nests content deeply (Content.Simple.Body.Html.Data), requires regional endpoints, and uses AWS SigV4 auth. Mailtrap uses a flat payload structure with a Bearer token – fewer lines of code, no SDK dependency for signing.

SMTP migration

SettingAmazon SESMailtrap (Transactional)Mailtrap (Bulk)
Hostemail-smtp.{region}.amazonaws.comlive.smtp.mailtrap.iobulk.smtp.mailtrap.io
Port587, 2587 (STARTTLS); 465, 2465 (implicit TLS); 25587 (recommended), 25, 2525587 (recommended), 25, 2525
TLSRequired (STARTTLS or implicit TLS)Required (STARTTLS)Required (STARTTLS)
Auth methodPLAIN, LOGINPLAIN, LOGINPLAIN, LOGIN
UsernameIAM SMTP username (Access Key ID)api (literal string)api (literal string)
PasswordDerived from IAM Secret Access Key via HMAC-SHA256 (not the raw key)API tokenAPI token

Migration notes:

  • Host: Replace email-smtp.{region}.amazonaws.com with the appropriate Mailtrap host. If you send both transactional and bulk email over SMTP, you now need two separate SMTP configurations.
  • Username: SES uses your IAM Access Key ID. Mailtrap requires the literal string api.
  • Password: This is the biggest gotcha. SES SMTP passwords are not your IAM Secret Access Key – they’re derived from it via a signing algorithm. Mailtrap just uses the API token directly. No derivation step needed.
  • Regional vs global: SES SMTP is regional (credentials are region-specific). Mailtrap SMTP is global.
  • Ports 2587/2465: SES offers additional high-numbered ports. Mailtrap uses 587 (recommended), 25, and 2525.

For more information on migrating your SMTP configuration, click this link. ⬅️

Rate limits & quotas

LimitAmazon SESMailtrap
API rate limitAccount-dependent (starts at 1/sec in sandbox, scales with reputation)150 requests per 10 seconds per API token
Batch size50 destinations per SendBulkEmail call500 emails per batch call
Message size40 MB (v2 API/SMTP)10 MB default (extendable to 30 MB on request)
Max recipients per send50 (To + CC + BCC combined)Single send ( /api/send ) → 1 email, up to 1,000 recipients per field ( to / cc / bcc )
Batch send ( /api/batch ) → up to 500 separate emails per API call
Daily sending quotaAccount-dependent (200/day in sandbox; request increase for production)Plan-dependent
Non-sending API rate1 request/sec for management APIs
Stats API: 10 requests / 60 seconds
Suppressions API: 10 requests / 60 seconds
Contacts API: 200 requests / 60 seconds
Email Logs API: 60/min for list, 1000/min for get-by-ID

Key differences:

  • SES counts recipients, not messages, against your quota. One email to 10 recipients counts as 10.
  • SES quotas are per-region. You can send from multiple regions to multiply your effective quota, but each requires separate configuration.
  • SES starts in sandbox mode (200 emails/day, 1/sec) — you must request production access. Mailtrap’s free plan starts at 1,000 emails/month with no sandbox restrictions.
  • SES has more several non-sending API calls (IAM roles, Lambda functions, etc.) and they follow the same rate as expressed above.

Email templates

Both platforms use Handlebars. If your SES templates use {{variable}} syntax, the markup migrates directly.

Syntax comparison

PatternAmazon SESMailtrap
SyntaxHandlebarsHandlebars
Variable insertion{{variable_name}}{{variable_name}}
Conditionals{{#if var}}…{{else}}…{{/if}}{{#if var}}…{{else}}…{{/if}}
Iteration{{#each items}}…{{/each}}{{#each items}}…{{/each}}
Default/fallback{{#if var}}{{var}}{{else}}default{{/if}}{{#if var}}{{var}}{{else}}default{{/if}}
Date formattingNot supported – format before passingNot supported – format before passing
Pass variables via APITemplateData (escaped JSON string)template_variables (JSON object)
Template identifierTemplateName (string, up to 64 chars) or TemplateArntemplate_uuid
Inline templatesSupported via TemplateContent (no pre-storage needed)N/A – templates must be pre-stored
Max templates20,000 per region200
Inline partials{{#* inline “name”}}…{{/inline}}+ {{> name}}Standard Handlebars support

Migration notes: SES requires TemplateData as an escaped JSON string ("{"name": "John"}"). Mailtrap accepts template_variables as a regular JSON object ({"name": "John"}). This simplifies your code – no double-encoding.

Question icon

Frequently Asked Questions

  • Do I need to re-verify my domain if I’ve already set it up in Amazon SES?

    Yes, you’ll need to re-verify your domain. See the Mailtrap Knowledge Base for up-to-date guidance.

  • Why does Mailtrap have two separate SMTP hosts?

    Mailtrap separates transactional and bulk streams to protect your sender reputation and ensure proper delivery routing for each email type.

  • Does Mailtrap offer migration assistance?

    Yes, Mailtrap offers migration assistance from Business plan onwards.

  • How do I migrate templates from SES?

    Both platforms use Handlebars, so template markup copies over directly. Update your API calls to use template_uuid instead of TemplateName, and pass variables via template_variables (a JSON object) instead of TemplateData (an escaped JSON string). If you use SES inline templates (TemplateContent), you’ll need to pre-store them in Mailtrap first.

  • Do I need to change my SMTP password?

    Yes. SES SMTP passwords are derived from your IAM Secret Access Key via a signing algorithm. Mailtrap uses the API token directly as the password – no derivation needed.

  • What about SES sandbox restrictions?

    SES starts in sandbox mode (200 emails/day, verified recipients only). Mailtrap has no sandbox; the free plan gives you 1,000 emails/month to any recipient immediately.

  • Can I migrate gradually?

    Yes. If you’re on Rails, Mailtrap’s ActionMailer Balancer lets you split traffic proportionally (e.g., 80% SES / 20% Mailtrap) and shift over time. For other frameworks, you can route by email type; e.g., move transactional to Mailtrap first, then bulk.