Resend Transactional Email Done Right in 2026

Production guide to Resend API v2: setup, error handling, templates, and patterns every indie hacker needs for reliable email delivery.

TL;DR

Resend ([official docs](https://resend.com/docs)) is a developer-first transactional email service built for modern applications. Verify current pricing and API version in official docs before deploying. This guide covers v2 API patterns, common errors, and production-ready code.

Why Resend Beats Traditional SMTP

If you've debugged SMTP authentication at 3 AM, you understand the appeal. Resend eliminates:

  • Server configuration complexity
  • Deliverability rate guessing
  • Bounce handling overhead
  • IP warming requirements
  • You get a REST API, event webhooks, and dashboard analytics. The tradeoff: you're trusting an external service. For most indie projects, this tradeoff wins.

    Getting Started: Installation & Auth

    ```bash npm install resend ```

    Verify you're installing the latest version (check npm for current release):

    ```bash npm view resend version ```

    Authenticate via environment variable:

    ```bash

    .env.local

    RESEND_API_KEY=re_xxxxxxxxxxxxx ```

    Production-Ready Patterns

    Pattern 1: Basic Email with Error Handling

    ```typescript import { Resend } from 'resend';

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

    export async function sendWelcomeEmail(userEmail: string, userName: string) { try { const response = await resend.emails.send({ from: 'onboarding@yourdomain.com', to: userEmail, subject: Welcome, ${userName}!, html: <h1>Hi ${userName}</h1><p>Thanks for joining.</p>, });

    if (response.error) { console.error('Resend API error:', response.error); throw new Error(Email send failed: ${response.error.message}); }

    return { success: true, id: response.data?.id }; } catch (error) { console.error('Email error:', error); // Log to your error tracking service (Sentry, etc) throw error; } } ```

    Pattern 2: Template-Based Emails

    Resend supports both inline HTML and template IDs. For maintainability, use templates:

    ```typescript export async function sendPasswordReset( email: string, resetToken: string ) { const resetUrl = ${process.env.APP_URL}/reset?token=${resetToken};

    const response = await resend.emails.send({ from: 'noreply@yourdomain.com', to: email, subject: 'Password Reset Request', html: ` <h2>Reset Your Password</h2> <p>Click the link below within 1 hour:</p> <a href="${resetUrl}" style="background:blue;color:white;padding:10px;text-decoration:none;border-radius:4px;"> Reset Password </a> <p style="color:#999;font-size:12px;margin-top:20px;"> If you didn't request this, ignore this email. </p> `, });

    return response; } ```

    Pattern 3: Batch Sending with Rate Limiting

    Sending to multiple users? Implement backpressure:

    ```typescript export async function sendBulkEmails( recipients: Array<{ email: string; name: string }> ) { const BATCH_SIZE = 10; const DELAY_MS = 100; const results = [];

    for (let i = 0; i < recipients.length; i += BATCH_SIZE) { const batch = recipients.slice(i, i + BATCH_SIZE); const promises = batch.map(({ email, name }) => resend.emails.send({ from: 'newsletter@yourdomain.com', to: email, subject: Monthly Update for ${name}, html: <p>Hi ${name}, here's what's new...</p>, }) );

    const batchResults = await Promise.all(promises); results.push(...batchResults);

    // Delay between batches to avoid rate limits if (i + BATCH_SIZE < recipients.length) { await new Promise(resolve => setTimeout(resolve, DELAY_MS)); } }

    return results; } ```

    Common Errors & Solutions

    Error 1: Missing or Invalid API Key

    ``` Error: Unauthorized - Invalid API Key ```

    Solution: Verify your RESEND_API_KEY environment variable is set and correct. Check the [Resend dashboard](https://resend.com) for your actual key. Never commit keys to version control.

    Error 2: Invalid 'from' Domain

    ``` Error: Email not verified - the 'from' address domain is not verified ```

    Solution: You must verify your sending domain in the Resend dashboard. Follow their DNS setup guide. Verify current verification process in official docs as this changes between releases.

    Error 3: Rate Limiting

    ``` Error: Too many requests - rate limit exceeded (429) ```

    Solution: Implement exponential backoff:

    ```typescript async function sendWithRetry( email: string, maxAttempts = 3 ) { for (let attempt = 0; attempt < maxAttempts; attempt++) { try { return await resend.emails.send(email); } catch (error: any) { if (error.status === 429 && attempt < maxAttempts - 1) { const delayMs = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s await new Promise(resolve => setTimeout(resolve, delayMs)); continue; } throw error; } } } ```

    Webhook Events for Delivery Tracking

    Monitor email lifecycle events:

    ```typescript // Your API endpoint export async function POST(req: Request) { const event = await req.json();

    switch (event.type) { case 'email.delivered': console.log(Email delivered: ${event.data.email_id}); // Update your database break; case 'email.bounced': console.warn(Email bounced: ${event.data.email}); // Flag user or remove from list break; case 'email.complained': console.warn(User complained about email: ${event.data.email}); // Unsubscribe user immediately break; }

    return new Response('OK', { status: 200 }); } ```

    Configure webhook URL in your Resend dashboard settings.

    Related Guides

    Before deploying email in production, understand [email authentication basics](/?guide=spf-dkim-dmarc) and [user preference management](/?guide=email-unsubscribe).

    Pricing & Scale Considerations

    Verify current pricing in [official docs](https://resend.com/pricing). Resend's model typically offers:

  • Free tier for development
  • Pay-per-email at scale
  • Volume discounts
  • Calculate your annual email volume before choosing a service. For indie projects sending <100k emails/month, Resend is often cheaper than maintaining your own SMTP infrastructure.

    Production Deployment Checklist

  • [ ] Verify domain ownership (DNS records)
  • [ ] Set up webhook endpoint and test delivery events
  • [ ] Implement retry logic with exponential backoff
  • [ ] Add email to your error tracking service alerts
  • [ ] Test with real emails before launching
  • [ ] Monitor bounce rates in dashboard weekly
  • [ ] Document your from addresses and templates
  • [ ] Set up unsubscribe mechanism per CAN-SPAM
  • What am I missing?

    Have you hit different errors? Built something interesting with Resend? Using alternative services and want to share trade-offs? Drop corrections and additions in the commentsβ€”this guide will be updated as the ecosystem evolves.

    πŸ”₯ 0d
    LIVE
    ↓ PlanetScale rage spiking β€’βš  Vercel pricing complaints ‒↑ Railway gaining fast ‒↑ Supabase happiness rising ‒↑ Resend loved by devs ‒↓ PlanetScale rage spiking β€’βš  Vercel pricing complaints ‒↑ Railway gaining fast ‒↑ Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today β€” and what opportunities that creates.

    β€”
    guides today
    β€”
    avg happiness
    πŸ”₯ Pain
    πŸ“– Guides
    πŸ”­ Explore
    πŸ‘€ Mine
    πŸ”₯ Pain Radar β€” rage scores today
    β†— share
    πŸ’‘ Opportunity Feed β€” pain = market gap
    πŸ“ˆ Tool Momentum
    all scores β†’
    πŸ“– Latest Guide
    all guides β†’
    πŸ“– All Guides
    πŸ“Š Tool Scores
    + Submit
    πŸ“° Hacker News
    βž• Submit a Tool
    ← back