Resend vs Mailgun
Mailgun was one of the original developer email APIs and it still shows: flexible routing, inbound parsing, EU and US regions, and a message API that supports everything in the unified shape — CC, BCC, headers, attachments, tags, metadata, and scheduled delivery. It is a strong pick when you need inbound mail or regional data residency alongside outbound sends. Resend is younger and more focused: outbound transactional email with a polished developer experience, React Email templates, and a simpler pricing page, but no per-message metadata field and no inbound handling. If your application round-trips structured data on every message, Mailgun's metadata support matters; if your priority is shipping the first send this afternoon, Resend's onboarding is hard to beat, and the SDK keeps a later migration to a single-line change.
Message field support
Field support as encoded in Email SDK's own adapter capability matrix — the same data the SDK uses to reject sends that would silently drop fields.
| Message field | Resend | Mailgun |
|---|---|---|
| CC recipients | ✓ | ✓ |
| BCC recipients | ✓ | ✓ |
| Reply-To address | ✓ | ✓ |
| Custom headers | ✓ | ✓ |
| Attachments | ✓ | ✓ |
| Tags | ✓ | ✓ |
| Metadata | — | ✓ |
| Scheduled sending (sendAt) | ✓ | ✓ |
Fallback compatibility
Failing over from Mailgun to Resend loses: metadata.
Same code, either provider
With Email SDK the send call is identical for both providers — only the adapter import changes:
import { createEmailClient } from "@opencoredev/email-sdk";
import { resend } from "@opencoredev/email-sdk/resend";
const client = createEmailClient({
adapters: [resend({ apiKey: process.env.RESEND_API_KEY! })],
});
await client.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Welcome!",
html: "<p>It works.</p>",
});Or run both, with automatic fallback:
import { createEmailClient } from "@opencoredev/email-sdk";
import { resend } from "@opencoredev/email-sdk/resend";
import { mailgun } from "@opencoredev/email-sdk/mailgun";
const client = createEmailClient({
adapters: [
resend({ apiKey: process.env.RESEND_API_KEY! }),
mailgun({ apiKey: process.env.MAILGUN_API_KEY!, domain: "mg.yourdomain.com" }),
],
defaultAdapter: "resend",
fallback: ["mailgun"],
});FAQ
- Can I switch from Resend to Mailgun without rewriting my email code?
- Yes. With Email SDK both providers share one typed send() call and one message shape, so switching from Resend to Mailgun is a one-line adapter change plus an API key. The SDK fails fast if a message uses a field Mailgun does not support, so nothing is silently dropped.
- Which message fields does Resend support that Mailgun doesn't?
- Mailgun supports every message field that Resend supports, so nothing is lost moving a message from Resend to Mailgun.
- Can I use Mailgun as a fallback for Resend?
- Partially. Email SDK checks field support before every send: a fallback from Resend to Mailgun is rejected for messages using no fields, and from Mailgun to Resend for messages using metadata. Messages that avoid those fields fail over cleanly.
Adapter docs
Setup guides: Resend adapter and Mailgun adapter. Sending from your own domain? Verify SPF, DKIM, and DMARC with the free email DNS checker.