Email SDK
Adapters

SDK field support

See which EmailMessage fields Email SDK maps for each adapter.

Email SDK keeps one message shape, but email APIs do not all expose the same features. This page documents what each Email SDK adapter maps today. Stable adapters either map a field or reject it with a validation error. They should not silently drop message data.

How to read this page

  • Yes means the adapter maps that EmailMessage field.
  • No means the adapter rejects that field before calling the provider.
  • Values means Email SDK sends each tag's value to the provider.
  • One tag means only one tag can be represented by that provider API. Sending more than one tag fails before the provider request.

Choose routes by message shape

Fallback routes are only safe when the backup adapter can represent the same message fields that matter to your app. Choose primary and backup routes from the fields your production messages use, not just from provider popularity.

NeedStart with
Most complete API field mappingPostmark, SendGrid, AWS SES, Mailgun, Brevo
Resend-style DX with attachmentsResend
Cheap or self-managed deliverySMTP
Product/event emailLoops, Plunk
Provider fallback for productionResend plus SMTP, or one primary API plus Postmark
Attachment-heavy transactionalResend, Postmark, SendGrid, Mailgun, Unosend, MailerSend

If you are unsure, start with Resend for the first send, then choose fallback routes from this table based on the exact fields your app sends. Do not add a fallback adapter until it can represent the same message shape as your primary route.

Fallback compatibility checklist

  • Does the backup adapter support every EmailMessage field your message uses?
  • Does the backup preserve attachments when receipts, exports, or files matter?
  • Does the backup preserve metadata or tags if your app uses them for provider dashboards, analytics, or routing?
  • Does the backup support reply-to and headers if support workflows depend on them?
  • Has the backup provider account been live-verified in the target environment?

API adapters

These adapters cover most transactional email fields. They are the best fit when your app needs CC, BCC, reply-to, custom headers, tags, metadata, or attachments.

AdapterCCBCCReply-ToHeadersMetadataTagsAttachments
ResendYesYesYesYesNoYesYes
PostmarkYesYesYesYesYesOne tagYes
SendGridYesYesYesYesYesValuesYes
CloudflareYesYesYesYesNoNoYes
UnosendYesYesYesYesNoValuesYes
AWS SESYesYesYesYesNoYesYes
MailgunYesYesYesYesYesValuesYes
MailerSendYesYesYesYesNoValuesYes
BrevoYesYesYesYesYesValuesYes
Mailchimp TransactionalYesYesNoYesYesValuesYes
MailtrapYesYesYesYesYesOne tagYes

Narrow adapters

These adapters are useful, but their public APIs do not match the full EmailMessage shape. Email SDK keeps them stable by rejecting fields it cannot represent.

AdapterCCBCCReply-ToHeadersMetadataTagsAttachments
SparkPostNoNoYesYesYesValuesYes
LoopsNoNoNoNoYesNoYes
PlunkNoNoYesYesYesNoYes
ScalewayYesYesYesYesNoNoYes
ZeptoMailYesYesYesNoNoNoYes
MailPaceYesYesYesNoNoNoNo

SMTP transport

SMTP is built in and does not use Nodemailer. It maps address fields and headers directly to the SMTP message, but it does not map provider-only concepts like tags or metadata.

AdapterCCBCCReply-ToHeadersMetadataTagsAttachments
SMTPYesYesYesYesNoNoNo

Validation behavior

Unsupported fields fail fast. For example, Resend rejects metadata, Mailchimp Transactional rejects replyTo, and SMTP rejects attachments. Fail-fast validation protects fallback routes from silent data loss: if a field matters, you find out before the provider request.

Loops attachments map to the provider's transactional attachments payload. Loops documents that transactional attachments require support enablement on the account, so accounts without that enablement should omit attachments.

The SDK's local checks are not a live deliverability guarantee. Provider accounts still need verified senders or domains, the correct region, API scopes, and any sandbox or recipient allow-list configuration required by that service.

Attachment rules

Attachment content is treated as raw content by default and encoded to Base64 for API adapters that require Base64.

attachments: [
  {
    filename: "receipt.txt",
    content: "Thanks for your order.",
    contentType: "text/plain",
  },
];

If you already have Base64 content, mark it:

attachments: [
  {
    filename: "receipt.pdf",
    content: base64Pdf,
    contentEncoding: "base64",
    contentType: "application/pdf",
  },
];

Adapters that support attachments can also read a local path:

attachments: [
  {
    filename: "receipt.pdf",
    path: "./receipt.pdf",
    contentType: "application/pdf",
  },
];

On this page