# Resend vs Mailtrap

> Resend vs Mailtrap for transactional email: message-field support compared side by side (attachments, scheduling, metadata, and more), with code for both via one TypeScript SDK.

Canonical page: https://email-sdk.dev/compare/resend-vs-mailtrap

Mailtrap grew up as an email testing product: its Email Sandbox is a fake SMTP server that captures outgoing mail in dev, staging, and QA so nothing reaches real inboxes, and its Email Sending API handles production delivery from the same account. Resend skips the sandbox story and focuses on a polished transactional API with fast onboarding and React Email templates. In the unified message shape the two trade exactly one field each way: Mailtrap carries per-message metadata (sent as custom_variables) and Resend does not, while Resend supports native scheduled sending and Mailtrap has none. Two Mailtrap edges matter when it sits behind another adapter: a message may carry at most one tag, which Mailtrap stores as its category, and at most one replyTo, and the SDK rejects a second of either before any request. With Email SDK the swap is a one-line adapter change, so testing on Mailtrap's sandbox and sending through either provider can share the same send call.

## 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 | Mailtrap |
| --- | --- | --- |
| CC recipients | Yes | Yes |
| BCC recipients | Yes | Yes |
| Reply-To address | Yes | Yes |
| Custom headers | Yes | Yes |
| Attachments | Yes | Yes |
| Tags | Yes | Yes |
| Metadata | No | Yes |
| Scheduled sending (sendAt) | Yes | No |

## Fallback compatibility

Failing over from Resend to Mailtrap loses: Scheduled sending (sendAt). Email SDK rejects the fallback send for messages that use these fields instead of dropping them silently.

Failing over from Mailtrap to Resend loses: Metadata.

## Same code, either provider

With Email SDK the send call is identical for both providers; only the adapter import changes.

```ts
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:

```ts
import { createEmailClient } from "@opencoredev/email-sdk";
import { resend } from "@opencoredev/email-sdk/resend";
import { mailtrap } from "@opencoredev/email-sdk/mailtrap";

const client = createEmailClient({
  adapters: [
    resend({ apiKey: process.env.RESEND_API_KEY! }),
    mailtrap({ apiKey: process.env.MAILTRAP_API_KEY! }),
  ],
  defaultAdapter: "resend",
  fallback: ["mailtrap"],
});
```

## Frequently asked questions

### Can I switch from Resend to Mailtrap 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 Mailtrap is a one-line adapter change plus an API key. The SDK fails fast if a message uses a field Mailtrap does not support, so nothing is silently dropped.

### Which message fields does Resend support that Mailtrap doesn't?

Resend supports Scheduled sending (sendAt) in the unified message shape, which Mailtrap does not.

### Can I use Mailtrap as a fallback for Resend?

Partially. Email SDK checks field support before every send: a fallback from Resend to Mailtrap is rejected for messages using Scheduled sending (sendAt), and from Mailtrap to Resend for messages using Metadata. Messages that avoid those fields fail over cleanly.

## Adapter docs

- [Resend adapter](https://email-sdk.dev/docs/adapters/resend)
- [Mailtrap adapter](https://email-sdk.dev/docs/adapters/mailtrap)
- [Email DNS checker](https://email-sdk.dev/tools/email-dns-checker): verify SPF, DKIM, and DMARC for a sending domain.
