Resend logo

resend

Email delivery service for developers

$ npx docs2skills add resend-email
SKILL.md

Resend

Modern email delivery service for developers with focus on deliverability and developer experience

What this skill does

Resend is a transactional email service that provides programmatic email sending with high deliverability rates. It's designed for developers who need reliable email delivery for user notifications, password resets, marketing campaigns, and other automated communications.

Unlike traditional SMTP services, Resend offers a modern API-first approach with built-in analytics, webhook notifications, and domain authentication features. It supports multiple programming languages and provides detailed delivery tracking to ensure your emails reach their destination.

Prerequisites

  • Resend account and API key from https://resend.com/api-keys
  • Verified domain for production use (optional for testing)
  • Node.js 16+, Python 3.7+, PHP 7.4+, or other supported runtime
  • Valid "from" email address

Quick start

npm install resend
import { Resend } from 'resend';

const resend = new Resend('re_123456789');

const { data, error } = await resend.emails.send({
  from: 'onboarding@resend.dev',
  to: ['user@example.com'],
  subject: 'Hello World',
  html: '<strong>Welcome to our app!</strong>',
});

if (error) {
  console.error(error);
} else {
  console.log(data); // { id: 'email_id' }
}

Core concepts

Email Objects: Core unit containing from, to, subject, and content (HTML/text) Domains: Authenticated sending domains that improve deliverability Webhooks: Real-time notifications for email events (delivered, bounced, complained) Batching: Send multiple emails in single API call for efficiency Templates: Reusable email layouts with dynamic content variables

Key API surface

// Send single email
resend.emails.send({
  from: string,
  to: string | string[],
  subject: string,
  html?: string,
  text?: string,
  cc?: string[],
  bcc?: string[],
  reply_to?: string[],
  attachments?: Array<{filename: string, content: Buffer}>
})

// Send batch emails
resend.batch.send([emailObject1, emailObject2])

// Get email details
resend.emails.get(id)

// List emails
resend.emails.list({ limit?: number, offset?: number })

// Domain management
resend.domains.create({ name: string })
resend.domains.verify(id)
resend.domains.list()

// API key management  
resend.apiKeys.create({ name: string, permission?: string })
resend.apiKeys.list()

Common patterns

Transactional emails with templates:

await resend.emails.send({
  from: 'noreply@myapp.com',
  to: user.email,
  subject: 'Welcome to MyApp!',
  html: `
    <h1>Welcome ${user.name}!</h1>
    <p>Thanks for signing up. <a href="${confirmUrl}">Confirm your email</a></p>
  `
});

Bulk notifications:

const emails = users.map(user => ({
  from: 'newsletter@myapp.com',
  to: user.email,
  subject: 'Weekly Update',
  html: generateNewsletterHtml(user)
}));

await resend.batch.send(emails);

Error handling with retry logic:

async function sendWithRetry(emailData, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const { data, error } = await resend.emails.send(emailData);
    if (!error) return data;
    if (error.name === 'rate_limit_exceeded') {
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    } else {
      throw error;
    }
  }
}

Webhook handling for delivery tracking:

app.post('/webhooks/resend', (req, res) => {
  const { type, data } = req.body;
  
  switch (type) {
    case 'email.delivered':
      console.log(`Email ${data.email_id} delivered to ${data.to}`);
      break;
    case 'email.bounced':
      console.log(`Email bounced: ${data.bounce_reason}`);
      break;
  }
  
  res.status(200).send('OK');
});

File attachments:

import fs from 'fs';

await resend.emails.send({
  from: 'docs@myapp.com',
  to: 'user@example.com',
  subject: 'Your Invoice',
  html: '<p>Please find your invoice attached.</p>',
  attachments: [{
    filename: 'invoice.pdf',
    content: fs.readFileSync('./invoice.pdf')
  }]
});

Configuration

Environment variables:

RESEND_API_KEY=re_your_api_key_here

Domain setup for production:

  1. Add domain in Resend dashboard
  2. Configure DNS records (SPF, DKIM, DMARC)
  3. Wait for verification (can take up to 72 hours)

Rate limits:

  • Free tier: 100 emails/day, 3,000 emails/month
  • Paid tiers: Higher limits based on plan

Best practices

  • Always use verified domains in production for better deliverability
  • Include both HTML and text versions of emails
  • Set up webhook endpoints to track delivery status
  • Use batch sending for multiple emails to same recipients
  • Implement exponential backoff for rate limit errors
  • Keep "from" addresses consistent to build sender reputation
  • Use descriptive subject lines to avoid spam filters
  • Test email rendering across different clients
  • Monitor bounce and complaint rates regularly
  • Store email IDs for tracking and debugging purposes

Gotchas and common mistakes

API key scope: Create API keys with minimum required permissions. Full access keys in client-side code are security risks.

Domain verification delay: New domains can take up to 72 hours to verify. Plan accordingly for production launches.

Rate limiting: Free tier has strict limits. Implement proper error handling for rate_limit_exceeded errors.

Email content encoding: HTML content must be properly escaped. Raw user input can break email rendering.

Attachment size limits: Maximum 40MB total per email including all attachments.

Webhook reliability: Implement idempotency in webhook handlers - Resend may send duplicate events.

"From" address restrictions: Must use verified domain or resend.dev subdomain. Random email addresses will be rejected.

Batch size limits: Maximum 100 emails per batch request.

Reply-to configuration: Set reply_to addresses for emails that expect responses - emails from noreply addresses often get flagged.

Testing vs production: Use different API keys and domains for development and production environments.