Skip to content
Docs

Guide

Move from Resend to Emailit. The APIs are close, so changes are small. Map fields, SMTP settings and webhook events, verify signatures and move contacts.

Updated Oct 1, 2026

This guide maps Resend concepts, API calls, webhooks and templates to their Emailit equivalents. The two APIs have a similar shape, so most code changes are small. Read Migrate to Emailit first for the overall order and how to run both providers in parallel.

Concepts

Resend Emailit
Team Workspace. Each workspace has its own domains, keys, members and credits.
API key with full or sending access API key: Full Access, or Sending Only optionally restricted to one domain
Domains Sending domains
Audiences and contacts Contacts and audiences
Broadcasts Campaigns
Webhooks Webhooks
Emails and logs Email APIEmails and Email APILogs
tags meta
React Email components HTML you render in your code, or templates designed in the dashboard

Update your API calls

Resend’s POST /emails becomes Emailit’s POST /v2/emails, with almost the same fields. In Node.js:

Before: Resend
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

await resend.emails.send({
  from: 'Acme <hello@acme.com>',
  to: ['ada@example.com'],
  subject: 'Your receipt',
  html: '<p>Thanks for your order.</p>',
});
After: Emailit
import { Emailit } from '@emailit/node';

const emailit = new Emailit(process.env.EMAILIT_API_KEY);

await emailit.emails.send({
  from: 'Acme <hello@acme.com>',
  to: ['ada@example.com'],
  subject: 'Your receipt',
  html: '<p>Thanks for your order.</p>',
});
Resend Emailit
Authorization: Bearer re_… Authorization: Bearer secret_…
from, subject, html, text, headers The same
to, cc, bcc The same, up to 50 each
reply_to (replyTo in the Node.js SDK) reply_to
react (Node.js SDK) Not available. Render the component to HTML and pass html.
attachments[] with filename and content or path attachments[] with filename and base64 content or a url. content_type is required with content.
tags: [{ "name": "…", "value": "…" }] meta: { "name": "value" }, returned in webhook events
scheduled_at scheduled_at (ISO 8601, Unix time or plain English)
Idempotency-Key header Idempotency-Key header, kept for 24 hours
POST /emails/batch Not available. Send one request per message.
Response { "id": "…" } 200 with id (em_…), status: "accepted", and ids with one ID per recipient

Each recipient in an Emailit request becomes its own email with its own ID. When you send to several recipients, the response’s ids maps each address to its ID. Emailit also tracks opens and clicks per email with tracking: { "loads": true, "clicks": true }, once the domain has a verified tracking subdomain. See Send an email.

React Email

If you build emails with React Email, keep your components and render them yourself before you send:

JSX
import { render } from '@react-email/render';
import Receipt from './emails/receipt';

const html = await render(<Receipt order={order} />);
const text = await render(<Receipt order={order} />, { plainText: true });

await emailit.emails.send({ from, to, subject, html, text });

Switch SMTP settings

Setting Resend Emailit
Host smtp.resend.com smtp.emailit.com
Port 587, 2587 or 25 (STARTTLS), 465 or 2465 (TLS) 587, 2587, 2525 or 25 (STARTTLS), 465 (TLS)
Username resend emailit
Password Your Resend API key Your Emailit API key

See SMTP settings.

Map webhook events

Resend event Emailit event
email.sent email.accepted (API only)
email.delivered email.delivered
email.delivery_delayed email.attempted
email.bounced email.bounced
email.complained email.complained
email.opened email.loaded
email.clicked email.clicked
contact.created, contact.updated, contact.deleted The same names
domain.created, domain.updated, domain.deleted The same names

The request format changes:

  • Resend posts one event per request. Emailit posts a JSON array of up to 100 events, so loop over the body.
  • The email is in data.object. Use data.object.id, the em_ ID from the send response, instead of data.email_id. Your meta values are in data.object.meta.
  • Signatures work differently. Resend signs webhooks through Svix, with svix-* headers. Emailit sends X-Emailit-Signature, the hex HMAC-SHA256 of <timestamp>.<raw body>, keyed with the whole secret including the whsec_ prefix. A Svix verification library won’t work, even though both secrets start with whsec_. See Request signature.
JavaScript
for (const event of req.body) {
  const email = event.data.object;
  if (event.type === 'email.bounced') markBounced(email.to, email.id);
  if (event.type === 'email.complained') unsubscribe(email.to);
}

Move suppressions and contacts

  1. Collect every address that bounced or complained while you used Resend, from Resend’s dashboard or from the events your webhooks stored.

  2. Build one CSV with the columns email,type,reason:

    CSV
    email,type,reason
    old-address@example.com,recipient,resend bounce
    angry@example.com,recipient,resend complaint

    Use the type recipient for addresses that must never receive email. It blocks API, SMTP and campaign sends.

  3. In Email APISuppressions, select Import and upload the file. Each file can have up to 10,000 rows and can be at most 8 MB. Duplicates are skipped.

If you send broadcasts, export each Resend audience as a CSV and import it in Email MarketingContacts with Import, choosing the matching Emailit audience. Map the unsubscribed column so people who opted out stay opted out. See Import and export contacts.

Move templates

If you render emails in code, with React Email or anything else, nothing changes: send the HTML. If you keep templates in Resend, recreate them in Email MarketingTemplates or with the Templates API, give each one an alias, and send it with template and variables.

Emailit templates use Temple: {{name}}, nested values such as {{user.name}}, defaults such as {{name|"there"}}, and {{#if plan}}…{{else}}…{{/if}}. There are no loops, so render lists in your code. Temple never escapes HTML, so escape user input before you pass it.

Change DNS

Add your domain in Email APIDomains and publish the Emailit records. They use their own names (emailit._domainkey, emailit.<domain>, and optionally go and inbound), so they don’t conflict with Resend’s records, which typically live on resend._domainkey and a send subdomain. Keep your DMARC record. After the cutover, remove the Resend records. See DNS records.

Next steps

Was this page helpful?

Thanks for the feedback.

Thanks, we read every message.