Skip to main content

How to Send Transactional Emails with Resend

·APIScout Team
resendemail apitransactional emailtutorialapi integration

How to Send Transactional Emails with Resend

Resend is the modern developer-first email API. Built by the creators of React Email, it's designed for developers who want great deliverability without the complexity of legacy platforms. This guide covers everything from first email to production templates.

What You'll Build

  • Send transactional emails (welcome, receipt, password reset)
  • React Email templates with Resend
  • Domain verification for production sending
  • Webhook tracking for delivery events
  • Batch sending and attachments

Prerequisites: Node.js 18+, Resend account (free tier: 3,000 emails/month).

1. Setup

Install

npm install resend

Initialize

// lib/resend.ts
import { Resend } from 'resend';

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

Environment Variables

# .env.local
RESEND_API_KEY=re_...

2. Send Your First Email

const { data, error } = await resend.emails.send({
  from: 'Your App <hello@yourdomain.com>',
  to: 'user@example.com',
  subject: 'Welcome to Your App!',
  html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
});

if (error) {
  console.error('Failed to send:', error);
} else {
  console.log('Email sent:', data.id);
}

API Route (Next.js)

// app/api/send-email/route.ts
import { NextResponse } from 'next/server';
import { resend } from '@/lib/resend';

export async function POST(req: Request) {
  const { to, name } = await req.json();

  const { data, error } = await resend.emails.send({
    from: 'Your App <hello@yourdomain.com>',
    to,
    subject: `Welcome, ${name}!`,
    html: `
      <h1>Welcome to Your App, ${name}!</h1>
      <p>Your account is ready. Here's what to do next:</p>
      <ol>
        <li>Complete your profile</li>
        <li>Explore the dashboard</li>
        <li>Invite your team</li>
      </ol>
      <a href="https://yourapp.com/dashboard">Go to Dashboard →</a>
    `,
  });

  if (error) {
    return NextResponse.json({ error }, { status: 500 });
  }

  return NextResponse.json({ id: data!.id });
}

3. React Email Templates

React Email lets you build email templates with React components — version-controlled, testable, and type-safe.

Install React Email

npm install @react-email/components

Create a Template

// emails/WelcomeEmail.tsx
import {
  Body,
  Container,
  Head,
  Heading,
  Html,
  Link,
  Preview,
  Section,
  Text,
  Button,
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  dashboardUrl: string;
}

export function WelcomeEmail({ name, dashboardUrl }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Preview>Welcome to Your App, {name}!</Preview>
      <Body style={main}>
        <Container style={container}>
          <Heading style={heading}>Welcome, {name}!</Heading>
          <Text style={text}>
            Your account is ready. Click below to get started.
          </Text>
          <Section style={buttonSection}>
            <Button style={button} href={dashboardUrl}>
              Go to Dashboard
            </Button>
          </Section>
          <Text style={footer}>
            Questions? Reply to this email or visit our{' '}
            <Link href="https://yourapp.com/help">help center</Link>.
          </Text>
        </Container>
      </Body>
    </Html>
  );
}

const main = { backgroundColor: '#f6f9fc', fontFamily: 'sans-serif' };
const container = { margin: '0 auto', padding: '40px 20px', maxWidth: '560px' };
const heading = { fontSize: '24px', fontWeight: 'bold', color: '#1a1a1a' };
const text = { fontSize: '16px', color: '#4a4a4a', lineHeight: '24px' };
const buttonSection = { textAlign: 'center' as const, margin: '32px 0' };
const button = {
  backgroundColor: '#2563eb',
  color: '#ffffff',
  padding: '12px 24px',
  borderRadius: '6px',
  fontSize: '16px',
  textDecoration: 'none',
};
const footer = { fontSize: '14px', color: '#8a8a8a' };

Send with React Email Template

import { resend } from '@/lib/resend';
import { WelcomeEmail } from '@/emails/WelcomeEmail';

const { data, error } = await resend.emails.send({
  from: 'Your App <hello@yourdomain.com>',
  to: 'user@example.com',
  subject: 'Welcome to Your App!',
  react: WelcomeEmail({
    name: 'Alex',
    dashboardUrl: 'https://yourapp.com/dashboard',
  }),
});

4. Domain Verification

To send from your own domain (not onboarding@resend.dev), verify your domain:

Add DNS Records

Resend requires these DNS records:

TypeNameValuePurpose
TXT@v=spf1 include:resend.com ~allSPF (sender authorization)
CNAMEresend._domainkeyresend.domainkey...DKIM (email signing)
TXT_dmarcv=DMARC1; p=none;DMARC (email policy)

Verify via API

// Check domain verification status
const domains = await resend.domains.list();
console.log(domains);

// Or add a new domain
const domain = await resend.domains.create({
  name: 'yourdomain.com',
});

5. Common Email Types

Password Reset

await resend.emails.send({
  from: 'Security <security@yourdomain.com>',
  to: userEmail,
  subject: 'Reset your password',
  react: PasswordResetEmail({
    resetUrl: `https://yourapp.com/reset?token=${token}`,
    expiresIn: '1 hour',
  }),
});

Order Confirmation

await resend.emails.send({
  from: 'Orders <orders@yourdomain.com>',
  to: customerEmail,
  subject: `Order #${orderId} confirmed`,
  react: OrderConfirmationEmail({
    orderId,
    items,
    total,
    estimatedDelivery,
  }),
});

Team Invitation

await resend.emails.send({
  from: 'Your App <team@yourdomain.com>',
  to: inviteeEmail,
  subject: `${inviterName} invited you to join ${teamName}`,
  react: TeamInviteEmail({
    inviterName,
    teamName,
    inviteUrl: `https://yourapp.com/invite?token=${token}`,
  }),
});

6. Batch Sending

Send multiple emails in one API call:

const { data, error } = await resend.batch.send([
  {
    from: 'App <hello@yourdomain.com>',
    to: 'user1@example.com',
    subject: 'Your weekly summary',
    react: WeeklySummaryEmail({ userId: '1' }),
  },
  {
    from: 'App <hello@yourdomain.com>',
    to: 'user2@example.com',
    subject: 'Your weekly summary',
    react: WeeklySummaryEmail({ userId: '2' }),
  },
]);

Limits: Up to 100 emails per batch call.

7. Webhooks

Track email delivery events:

Register Webhook

// In Resend dashboard or via API
await resend.webhooks.create({
  url: 'https://yourapp.com/api/webhooks/resend',
  events: [
    'email.sent',
    'email.delivered',
    'email.bounced',
    'email.complained',
    'email.opened',
    'email.clicked',
  ],
});

Handle Webhook Events

// app/api/webhooks/resend/route.ts
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const event = await req.json();

  switch (event.type) {
    case 'email.delivered':
      // Update delivery status in database
      await markEmailDelivered(event.data.email_id);
      break;

    case 'email.bounced':
      // Mark email as invalid, stop sending
      await handleBounce(event.data.to, event.data.bounce_type);
      break;

    case 'email.complained':
      // User marked as spam — unsubscribe immediately
      await unsubscribeUser(event.data.to);
      break;
  }

  return NextResponse.json({ received: true });
}

Pricing

TierEmails/MonthPrice
Free3,000$0
Pro50,000$20/month
Scale100,000$90/month
EnterpriseCustomCustom

No per-email overage charges on free tier — sending simply stops at the limit.

Common Mistakes

MistakeImpactFix
Sending from unverified domainEmails go to spamVerify domain with SPF/DKIM/DMARC
No bounce handlingHurts sender reputationHandle email.bounced webhooks
HTML emails without plain text fallbackSome clients show blankProvide text alongside html
Hardcoding "from" addressCan't change sender easilyUse environment variable
Not testing email renderingLooks broken in OutlookPreview in React Email dev server

Choosing an email API? Compare Resend vs SendGrid vs Postmark on APIScout — pricing, deliverability, and developer experience.

Comments