Why Engineering Teams Are Migrating Away from Mailgun
Mailgun played a vital role in pioneering developer-friendly transactional email in 2010. Over the past decade, however, successive acquisitions and corporate restructuring have resulted in steep price increases, complicated domain hierarchy requirements, and persistent SDK technical debt.
Specifically, the legacy mailgun.js SDK forces developers to install heavyweight external dependencies (form-data, stream polyfills) just to send a basic JSON payload over multipart HTTP POST. In modern serverless and edge architectures (Vercel, AWS Lambda, Cloudflare Workers), these extra dependencies increase bundle size, slow cold starts, and complicate simple deployments.
SadaSend was architected from the ground up as a pure JSON Anycast REST API and managed SMTP relay, purpose-built for modern cloud runtimes and autonomous AI agents with per-key recipient allowlists.
Architecture & Protocol Comparison Matrix
| Operational Feature | Legacy Mailgun | Modern SadaSend |
|---|---|---|
| Wire Protocol | Multipart form-data via SDK | Native application/json REST API & ESMTP 587 |
| Client Dependencies | mailgun.js + form-data (~2.1 MB) | 0 Dependencies (Global native fetch) |
| Authentication Scheme | Basic Auth (api:key-...) or API key | Standard Bearer Token (Authorization header) |
| Subdomain Routing | Requires rigid domain URL paths | Domain resolved automatically from From header |
| AI Agent Guardrails | None (Unconstrained blast radius) | Hardware allowlists & Approval holds |
| Global Edge Ingestion | Regional US/EU silos | Anycast edge nodes with low latency |
1. Side-by-Side Code Migration: Replacing mailgun.js with Fetch
In your application code, you can eliminate the entire Mailgun client initialization and replace it with standard, dependency-free TypeScript fetch calls.
// ==========================================
// BEFORE: Legacy Mailgun Implementation
// ==========================================
// import Mailgun from 'mailgun.js';
// import formData from 'form-data';
//
// const mailgun = new Mailgun(formData);
// const mg = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY! });
//
// const result = await mg.messages.create('mg.yourdomain.com', {
// from: 'Alex <alex@yourdomain.com>',
// to: ['customer@enterprise.com'],
// subject: 'Quarterly Audit Report',
// text: 'Your security audit report is ready for download.',
// });
// ==========================================
// AFTER: Modern SadaSend Native Fetch
// ==========================================
export async function sendAuditReport(to: string, reportUrl: string) {
const response = await fetch('https://api.sadasend.com/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SADASEND_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': `report-${to}-${Date.now()}`,
},
body: JSON.stringify({
from: 'Alex <alex@yourdomain.com>',
to,
subject: 'Quarterly Audit Report',
html: `<p>Your security report is ready: <a href="${reportUrl}">Download PDF</a></p>`,
text: `Your security report is ready: ${reportUrl}`,
}),
});
if (!response.ok) {
const errorBody = await response.json().catch(() => ({}));
throw new Error(`SadaSend dispatch failed (${response.status}): ${errorBody.message}`);
}
const { id } = await response.json();
return { success: true, messageId: id };
}2. Zero-Downtime DNS & Domain Verification Protocol
Migrating production email sending requires careful DNS staging to prevent deliverability drops or SPF authentication failures during the transition.
- Step 1 (Add Domain): Register your sending domain in the SadaSend dashboard (/app/domains/new). SadaSend provisions 2048-bit RSA and Ed25519 DKIM keys.
- Step 2 (Merge SPF Records): Do NOT delete your Mailgun SPF record immediately. Merge both mechanisms into a single DNS TXT record: "v=spf1 include:mailgun.org include:_spf.sadasend.com ~all". (Note: Never publish two separate SPF records).
- Step 3 (Publish DKIM Selectors): Add the SadaSend DKIM CNAME/TXT records alongside your existing Mailgun selectors. Multiple DKIM selectors happily coexist on the same domain.
- Step 4 (Switch Traffic): Deploy the updated code with SADASEND_API_KEY in staging, verify delivery, and roll out to production.
- Step 5 (Cleanup): Keep Mailgun active for 48 hours to handle any queued retries, then prune the mailgun.org SPF mechanism and cancel the legacy subscription.
Building AI agents that send email?
Scoped API keys, per-key recipient allowlists, approval mode and a hosted MCP server with ten tools — on the free plan, without a card.