Migration guide
Migrate to Mailtrap from Mailgun
A complete technical guide to switching from Mailgun to Mailtrap. Most teams complete the migration in under an hour.
Migration Checklist
- 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.
- 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. - Update your integration
Swap your Mailgun endpoints and credentials for Mailtrap’s (API or SMTP). See the migration sections below. - Migrate templates
Both platforms use Handlebars, so content migrates directly. Updatet:variables→template_variablesand string names →template_uuid. - Migrate suppressions
Export Mailgun suppressions and import them into Mailtrap via CSV or manually. Click here for more information. - Migrate users
Add users from the User Management tab. Mailgun’s 5 role types don’t map 1:1 to Mailtrap roles, so review permissions during migration. - Set up webhooks
Follow the Mailtrap Webhooks step-by-step guide. - 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% Mailgun and 30% Mailtrap) to mitigate the sending risks.
Concepts
Before going over the technical details, it’s important to clear up some key concepts.
Sending domains
Mailgun equivalent: Domain Verification (with Automatic Sender Security for managed DKIM)
Before being able to send emails with Mailtrap, you first need to add and verify your sending domain. For domain verification, Mailtrap provides DNS records, which are the same standards Mailgun relies on.
DKIM records can be added alongside Mailgun’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.
To learn how to verify your domain, you can read our step-by-step Knowledge base article, or watch the video we prepared for you. The whole process takes ~15 minutes.
Note: Even if your domain is already authenticated in Mailgun, you’ll need to add it to Mailtrap separately. Existing DNS records may need to be replaced or consolidated; you can’t use them as-is.
Separate sending streams
Mailgun equivalent: N/A
Mailtrap provides two separate email infrastructures, or sending streams:
- 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
Mailgun equivalent: Tags (o:tag parameter)
Like Mailgun’s Tags, Mailtrap offers an Email Categories feature that lets you track the performance of different types of emails (e.g., welcome emails, password resets, etc.).
- Using Email Categories with Mailtrap: Insert a category name into the X-MT-Category header (SMTP) or pass the category name in the category field (API).
- Using Tags with Mailgun: Pass tags via the o:tag form parameter (API), or use the X-Mailgun-Tag header (SMTP). Mailgun supports multiple tags per message.

Organization & sub-accounts
Mailgun equivalent: Subaccounts + Multi-User Access with Roles
Mailtrap offers its Organization & Sub-accounts feature from Business plan onwards. It 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.

Notes:
- Mailgun provides Subaccounts (Enterprise-only) for fully isolated child environments with separate domains, IP pools, and API keys. Multi-user access uses Role-Based Access Control with 4 role types – Admin, Developer, Analyst, and Support – also available on Enterprise and select Business plans.
- With Mailtrap the feature is available to from Business plan onward.
Terminology comparison
- Sending Domains
- Email Logs
- User Management
- Email Templates
- Email Categories
- Headers
- X-MT-Custom-Variables
- Audit Logs
- Domain Verification
- Logs API*
- Multi-User Access / Roles
- Templates (with Versions)
- Tags New (o:tag)**
- Custom Headers (h: prefix)
- Custom Variables (v: prefix)
- Activity / Events
*Mailgun’s Events API has been deprecated.
**Mailgun’s old Tags API has been deprecated.
API migration
Authentication
Both Mailtrap and Mailgun authenticate API requests using an API key passed in the Authorization header. The key difference is the method:
| Mailgun | Mailtrap | |
| Method | HTTP Basic Auth | API token (Bearer token) |
| Header format | Authorization: Basic base64(“api:YOUR\_API\_KEY”) | Authorization: Bearer YOUR\_MAILTRAP\_API\_KEY |
When migrating, change your authentication from Basic Auth to Bearer token format.
Note: Mailtrap automatically creates a token when you add a domain. By default, the token has Domain Admin access permission. Add or remove token permissions in the API Tokens menu under Settings.
API mapping
| API type | Mailgun | Mailtrap | Notes |
| Transactional email | POST /v3/{domain}/messages(api.mailgun.net) | /api/send(send.api.mailtrap.io) | Mailtrap uses JSON body; Mailgun uses multipart/form-data. |
| Bulk email | POST /v3/{domain}/messages(with recipient-variables) | /api/send(bulk.api.mailtrap.io) | Mailtrap uses a separate bulk endpoint; Mailgun uses batch sending. |
| Templates | Templates API (template string name) | POST https\://send.api.mailtrap.io/api/send (with template\_uuid) | Mailgun identifies templates by name; Mailtrap uses UUID. |
| Suppressions | Three separate APIs: Bounces, Complaints, Unsubscribes | GET https\://mailtrap.io/api/accounts/{account\_id}/suppressions | Mailtrap consolidates; Mailgun splits into three endpoint groups. |
| Stats | Metrics API POST /v1/analytics/metrics |GET /api/accounts/{account_id}/stats,/stats/domains,/stats/categories,/stats/email_service_providers, and/stats/date` | Mailgun deprecated its legacy Stats API in favor of Metrics API. | |
| Email logs | Events API GET /v3/{domain}/events | GET https\://mailtrap.io/api/accounts/{account\_id}/email\_logs | Mailgun retains data for 30 days on paid plans, 2 days on free. |
Outbound Sending API JSON Field Mapping
Mailgun: POST /v3/{domain}/messages with multipart/form-data
Mailtrap: POST /api/send with application/json
| Field | Mailgun (form parameter) | Mailtrap (JSON field) |
| Sender | from | from (object: email, name) |
| Recipients | to | to (array of objects: email, name) |
| CC | cc | cc (array of objects: email, name) |
| BCC | bcc | bcc (array of objects: email, name) |
| Reply-To | h:Reply-To | reply\_to (object: email, name) |
| Subject | subject | subject |
| Plain text body | text | text |
| HTML body | html | html |
| AMP HTML body | amp-html | – |
| Tag / Category | o:tag (multiple allowed) | category (single string) |
| Custom variables | v:my-var (prefix-based) | custom\_variables (object) |
| Custom headers | h:X-My-Header (prefix-based) | headers (object) |
| Recipient variables (batch) | recipient-variables (JSON string) | – |
| Template name / ID | template (name string) | template\_uuid (UUID string) |
| Template variables | t:variables (JSON string) | template\_variables (object) |
| Template version | t:version | – |
| Template render text | t:text (yes/no) | – |
| File attachment | attachment (file upload, multiple) | attachments (array: content, filename, type, disposition, content_id) |
| Inline / embedded image | inline (file upload) | attachments with disposition: “inline” and content_id |
| Enable tracking | o:tracking (yes/no) | – |
| Track clicks | o:tracking-clicks (yes/no/htmlonly) | – |
| Track opens | o:tracking-opens (yes/no) | – |
| Require TLS | o:require-tls (yes/no) | – |
| Skip TLS verification | o:skip-verification (yes/no) | – |
| DKIM toggle | o:dkim (yes/no) | – |
| Secondary DKIM | o:secondary-dkim | – |
| Scheduled delivery | o:deliverytime (RFC 2822 / Unix) | – |
| Send time optimization | o:deliverytime-optimize-period | – |
| Timezone localize | o:time-zone-localize | – |
| Test mode | o:testmode (yes/no) | – |
| Sending IP | o:sending-ip | – |
| Sending IP pool | o:sending-ip-pool | – |
| Tracking pixel position | o:tracking-pixel-location-top | – |
| Suppress headers | o:suppress-headers | – |
| Sending stream | – | send.api.mailtrap.io (transactional) or bulk.api.mailtrap.io (bulk) |
| Batch send | – | POST /api/batch (up to 500 emails) |
Code snippets
-
cURL
curl -s --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v3/YOUR_DOMAIN/messages \
-F from='Sender <sender@YOUR_DOMAIN>' \
-F to='recipient@example.com' \
-F subject='Hello from Mailgun' \
-F text='Welcome to Mailgun!'
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:
- Content type: Mailtrap accepts application/json; Mailgun uses multipart/form-data.
- Auth method: Mailtrap uses Bearer token; Mailgun uses Basic Auth (–user).
- Domain in URL: Mailgun requires the sending domain in the API path (/v3/{domain}/messages); Mailtrap does not.
- Regional endpoints: Mailgun has separate API hosts for US and EU. Mailtrap uses a single global endpoint.
SMTP migration
| Setting | Mailgun | Mailtrap (Transactional) | Mailtrap (Bulk) |
| Host | smtp.mailgun.org | live.smtp.mailtrap.io | bulk.smtp.mailtrap.io |
| Port | 587 (recommended), 465, 25, 2525 | 587 (recommended), 25, 2525 | 587 (recommended), 25, 2525 |
| TLS | STARTTLS on 587/25/2525; implicit SSL on 465 | Required | Required |
| Authentication | PLAIN, LOGIN | PLAIN, LOGIN | PLAIN, LOGIN |
| Username | Full email address | api string | api string |
| Password | SMTP-specific password | API token | API token |
Migration notes:
- Mailtrap uses separate SMTP hosts for transactional (live.smtp.mailtrap.io) and bulk (bulk.smtp.mailtrap.io) streams. Mailgun uses a single host (smtp.mailgun.org) for all sending.
- The SMTP username differs: Mailtrap uses the literal string api, while Mailgun uses a full email address credential (e.g., postmaster@yourdomain.com).
- Mailgun’s SMTP credentials are managed per-domain and are separate from API keys. Mailtrap uses the same API token for both API and SMTP authentication.

For more information on migrating your SMTP configuration, click this link. ⬅️
Rate limits & quotas
| Limit | Mailgun | Mailtrap |
| API rate limit (mail send endpoint) | ~500 requests per 10 seconds | 150 requests per 10 seconds per API token |
| Batch size | 1,000 recipients per API call | 500 emails per batch call |
| Message size limit (incl. attachments) | 25 MB | 10 MB default (extendable to 30 MB on request) |
| Max recipients per send | 1,000 (via batch sending) | 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 |
Email templates
Both Mailtrap and Mailgun use Handlebars syntax, so variable insertion ({{variable}}), conditionals, loops, and nested objects work identically and require no changes when migrating.
The differences only appear in a few specific cases:
Syntax comparison
| Pattern | Mailgun | Mailtrap |
| Default/fallback value | Same approach – no built-in default helper | {{#if firstName}}{{firstName}}{{else}}there{{/if}} |
| Date formatting | Not supported – format before passing | Not supported – format before passing |
| Pass variables via API | t:variables (form parameter, JSON string) | template_variables |
| Template identifier | template (string name, e.g., my-template) | template_uuid |
| Template versioning | Supported via t:version parameter | N/A |
Migration notes: Mailgun has a separate variable system for batch sending that uses %recipient.varname% syntax (percent-delimited), distinct from its Handlebars template engine. When migrating batch sends, convert these to Mailtrap’s approach.
Frequently Asked Questions
-
Do I need to re-verify my domain if I’ve already set it up in Mailgun?
Yes, you’ll need to re-verify your domain. See the Mailtrap Knowledge Base for up-to-date guidance.
-
Mailgun uses Basic Auth. Does Mailtrap work the same way?
No. Mailgun authenticates with HTTP Basic Auth (
api:YOUR_API_KEY). Mailtrap uses a Bearer token:Authorization: Bearer YOUR_API_KEY. You’ll need to update the auth header, not just swap the key. -
Mailgun’s send API uses form data. Does Mailtrap accept the same format?
Mailtrap expects a JSON request body (
Content-Type: application/json), notmultipart/form-data. You’ll need to restructure the request – but the field names are close. Mailgun’shtml,text,subject,to,cc, andbccall have direct Mailtrap equivalents at the root of the JSON body. Thefromfield changes from a flat string ("Name <email>") to a{"email": "...", "name": "..."}object. -
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.
-
My Mailgun SMTP username is “postmaster@mydomain.com”. What do I use in Mailtrap?
Mailtrap uses the literal string
apias the SMTP username for both transactional and bulk streams, with your API token as the password. No per-domain SMTP credentials to manage. -
I use multiple Mailgun “o:tag” values per email. How do I handle that in Mailtrap?
Mailgun allows up to 3 tags per message via repeated
o:tagfields. Mailtrap accepts a singlecategorystring. Pick your primary tag for thecategoryfield and move secondary tags intocustom_variablesas key-value metadata. -
How do I migrate Mailgun’s “v:” custom variables?
Mailgun’s
v:my-varform fields map to Mailtrap’scustom_variablesJSON object. Instead of-F v:campaign-id="abc123", pass"custom_variables": {"campaign-id": "abc123"}in the JSON body. These will appear in webhook payloads the same way Mailgun’suser-variablesdo. -
Does Mailtrap support Mailgun’s Recipient Variables for batch sending?
Not with the same
%recipient.var%syntax. Mailgun lets you send to up to 1,000 recipients in one call, personalizing each withrecipient-variablesand%recipient.name%placeholders. In Mailtrap, use the bulk endpoint (bulk.api.mailtrap.io/api/send) and send up to 500 emails per batch call, each as a separate object with its own recipients andtemplate_variables. -
Does Mailtrap offer migration assistance?
Yes, Mailtrap offers migration assistance from Business plan onwards.
-
How do I migrate templates?
Both platforms use Handlebars syntax, so template content migrates directly. Update API calls to use
template_uuidinstead of string names, and pass variables viatemplate_variablesinstead oft:variables.