Skip to main content
Inbound provides a Resend-compatible API, making migration straightforward. This guide covers everything you need to know to switch from Resend to Inbound.

Why Migrate to Inbound?

Two-Way Email

Not just sending - receive emails and trigger webhooks in your app

Email Threading

Automatic conversation tracking with full thread support

MCP Integration

Connect AI assistants directly to your email infrastructure

Better Auth Plugin

Built-in authentication email support with React templates

Migration Steps

1

Install Inbound SDK

Replace the Resend package with Inbound:
npm
npm uninstall resend
npm install inboundemail
bun
bun remove resend
bun add inboundemail
pnpm
pnpm remove resend
pnpm add inboundemail
2

Get Your Inbound API Key

Sign up at inbound.new and generate an API key from your dashboard.

Dashboard

Get your API key
3

Update Your Code

Update your imports and initialization:Before (Resend):
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
After (Inbound):
import { Inbound } from 'inboundemail';
const inbound = new Inbound(process.env.INBOUND_API_KEY);
4

Update API Calls

Update your email sending calls (minimal changes required):Before (Resend):
await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Hello',
  html: '<p>World</p>'
});
After (Inbound):
await inbound.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Hello',
  html: '<p>World</p>'
});
5

Verify Domain DNS

Add your domain to Inbound and update DNS records:
const domain = await inbound.domains.create({
  domain: 'example.com'
});

console.log('Add these DNS records:', domain.dnsRecords);
You’ll need to update your DNS records from Resend’s to Inbound’s. Both use similar SPF, DKIM, and DMARC records.
6

Test Your Integration

Send a test email to verify everything works:
const { data, error } = await inbound.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Test Email',
  html: '<p>If you receive this, migration is successful!</p>'
});

if (error) {
  console.error('Error:', error);
} else {
  console.log('Success! Email ID:', data?.id);
}

API Compatibility Comparison

Sending Emails

Inbound’s API is intentionally designed to be compatible with Resend for easy migration.
FeatureResendInboundNotes
Basic sendIdentical API
HTML contentIdentical API
Plain textIdentical API
Multiple recipientsIdentical API
CC/BCCIdentical API
AttachmentsIdentical API
Custom headersIdentical API
TagsIdentical API
Scheduled emailsIdentical API
React EmailSame react prop

Code Examples: Side by Side

import { Resend } from 'resend';

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

// Basic send
await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Hello',
  html: '<p>World</p>'
});

// With attachments
await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Invoice',
  html: '<p>See attached</p>',
  attachments: [
    {
      filename: 'invoice.pdf',
      content: base64Content
    }
  ]
});

// With React Email
await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome',
  react: <WelcomeEmail name="John" />
});

Feature Parity

Features Available in Both

Transactional Emails

Send one-off emails with full HTML/text support

Attachments

Support for file attachments with base64 or URL

React Email

Use React components as email templates

Email Scheduling

Schedule emails for future delivery

Custom Headers

Add custom SMTP headers

Tags & Metadata

Tag emails for analytics and filtering

Domain Verification

SPF, DKIM, DMARC support

Webhooks

Delivery and bounce notifications

Inbound-Only Features

These features are unique to Inbound:

Receive Emails

Create email addresses that trigger webhooks when messages arrive

Email Threading

Automatic conversation tracking with thread IDs

Reply to Emails

Use inbound.reply() to respond to received emails

Email Forwarding

Forward incoming emails to other addresses

MCP Server

Connect AI assistants to your email infrastructure

Better Auth Plugin

Built-in authentication emails with templates

Email Parsing

Automatic HTML/text extraction and attachment handling

Subdomains

Automatic subdomain support for verified root domains

DNS Record Migration

When migrating, you’ll need to update your DNS records from Resend to Inbound.

Record Types

Both platforms use similar DNS records:
Record TypePurposeMigration
SPF (TXT)Sender authorizationReplace Resend’s SPF with Inbound’s
DKIM (TXT)Email signingAdd new DKIM keys from Inbound
DMARC (TXT)Email policyUpdate if specific to Resend
MX (for receiving)Incoming emailInbound only (new feature)

Example DNS Update

1

Get Inbound DNS Records

const domain = await inbound.domains.create({ domain: 'example.com' });
console.log(domain.dnsRecords);
2

Remove Old Resend Records

Remove these from your DNS:
  • resend._domainkey.example.com (DKIM)
  • SPF record containing include:resend.io
3

Add Inbound Records

Add the DNS records provided by Inbound:
TXT  _dkim.example.com  "v=DKIM1; k=rsa; p=..."
TXT  example.com        "v=spf1 include:inbound.new ~all"
MX   example.com        "inbound-in.net" (priority 10)
4

Verify Domain

const domain = await inbound.domains.retrieve('dom_xxx');
console.log('Status:', domain.status); // Should be 'verified'
Important: Update one domain at a time and verify it works before migrating others. Keep Resend active until all domains are verified on Inbound.

Code Changes Needed

Import Statements

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

// After
import { Inbound } from 'inboundemail';

Client Initialization

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

// After  
const inbound = new Inbound(process.env.INBOUND_API_KEY);

Sending Emails

// Before
await resend.emails.send({ /* ... */ });

// After
await inbound.emails.send({ /* ... */ }); // Same parameters!

Environment Variables

# Before
RESEND_API_KEY=re_xxxx

# After
INBOUND_API_KEY=inbound_xxxx

Response Format

The response format is slightly different:
// Resend
const { id } = await resend.emails.send({ /* ... */ });

// Inbound
const { data } = await inbound.emails.send({ /* ... */ });
const id = data?.id;

Field Name Differences

Inbound uses snake_case for API fields to match OpenAPI standards, but the SDK accepts both formats for compatibility.
Resend (camelCase)Inbound API (snake_case)SDK Support
replyToreply_toBoth accepted
scheduledAtscheduled_atBoth accepted
contentTypecontent_typeBoth accepted
The SDK automatically handles conversion, so you can use either format.

Testing Your Migration

Use this checklist to verify your migration:
const { data, error } = await inbound.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Test',
  html: '<p>Test email</p>'
});

console.log('Success:', data?.id);
const { data, error } = await inbound.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Test with Attachment',
  html: '<p>See attached</p>',
  attachments: [{
    filename: 'test.txt',
    content: Buffer.from('Hello World').toString('base64')
  }]
});
import { WelcomeEmail } from './emails/welcome';

const { data, error } = await inbound.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome!',
  react: <WelcomeEmail name="John" />
});
const domains = await inbound.domains.list();
console.log('Domains:', domains.data);

for (const domain of domains.data) {
  console.log(`${domain.domain}: ${domain.status}`);
}

Common Migration Issues

Problem: Emails fail with “domain not verified” error.Solution:
  1. Check DNS records are properly set
  2. Wait 24-48 hours for DNS propagation
  3. Use inbound.domains.retrieve(id) to check status
Problem: Getting rate limit errors.Solution:
  • Check your plan limits in the dashboard
  • Implement exponential backoff
  • Upgrade your plan if needed
Problem: Attachments not working.Solution:
  • Ensure content is base64 encoded
  • Check file size limits (10MB per attachment)
  • Verify content_type is specified
Problem: Authentication failing.Solution:
  • Verify API key is correct
  • Check environment variable name changed from RESEND_API_KEY to INBOUND_API_KEY
  • Regenerate API key if needed

Migration Checklist

Use this checklist to ensure a smooth migration:
  • Sign up for Inbound account
  • Generate API key
  • Install inboundemail package
  • Update import statements
  • Update client initialization
  • Update environment variables
  • Add domains to Inbound
  • Update DNS records
  • Verify domains
  • Test basic email sending
  • Test with attachments
  • Test React Email templates
  • Update error handling
  • Test in staging environment
  • Monitor first production emails
  • Remove Resend package
  • Clean up old DNS records

Gradual Migration Strategy

For large applications, consider a gradual migration:
// Feature flag approach
const USE_INBOUND = process.env.USE_INBOUND === 'true';

const emailClient = USE_INBOUND
  ? new Inbound(process.env.INBOUND_API_KEY)
  : new Resend(process.env.RESEND_API_KEY);

await emailClient.emails.send({
  // Same API for both!
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Hello',
  html: '<p>World</p>'
});
This allows you to:
  1. Test Inbound with a percentage of traffic
  2. Roll back quickly if issues arise
  3. Compare delivery metrics side-by-side

Beyond Migration: New Capabilities

Once migrated, explore Inbound’s unique features:

Receive Emails

// Create an email address that triggers your webhook
const emailAddress = await inbound.emailAddresses.create({
  address: '[email protected]',
  webhookUrl: 'https://yourapp.com/webhook/email'
});

// Your webhook receives:
// POST https://yourapp.com/webhook/email
// {
//   "email": {
//     "id": "email_xxx",
//     "from": { "address": "[email protected]", "name": "John" },
//     "to": [{ "address": "[email protected]" }],
//     "subject": "Help request",
//     "text": "I need help with...",
//     "html": "<p>I need help with...</p>"
//   }
// }

Reply to Emails

// In your webhook handler
export async function POST(request: Request) {
  const payload = await request.json();
  
  // Automatically reply to incoming emails
  await inbound.reply(payload.email, {
    from: '[email protected]',
    text: 'Thanks for reaching out! We\'ll respond within 24 hours.'
  });
  
  return new Response('OK');
}

Email Threads

// Get all emails in a conversation
const thread = await inbound.mail.retrieve('thread_xxx');

console.log('Conversation:', thread.emails);
// Returns all emails in the thread, sorted chronologically

MCP Integration

// .cursor/mcp.json or opencode.json
{
  "mcpServers": {
    "inbound": {
      "type": "http",
      "url": "https://mcp.inbound.new/mcp",
      "headers": {
        "x-inbound-api-key": "your-api-key"
      }
    }
  }
}
Now your AI assistant can manage your emails!

Getting Help

If you run into issues during migration:

Documentation

Read the full API reference

Discord Community

Join our Discord for support

GitHub Issues

Report bugs or request features

Email Support

Contact our support team

Next Steps

After completing your migration:

Webhooks Guide

Learn about webhook payloads and security

Better Auth Plugin

Add authentication emails

MCP Integration

Connect AI assistants to your emails

Best Practices

Email deliverability tips

Build docs developers (and LLMs) love