All skills
resend avatar

/email-best-practices

@376d1c3 official
by resendresend/resend-skills193 stars
28

Use when building email features, emails going to spam, high bounce rates, setting up SPF/DKIM/DMARC authentication, implementing email capture, ensuring compliance (CAN-SPAM, GDPR, CASL), handling webhooks, retry logic, making emails accessible (alt text, headings, contrast, screen readers), or deciding transactional vs marketing.

Use this Skill: https://skilld.dev/gh/resend/resend-skills/email-best-practices

This session only. Nothing lands on disk.

referencessending-reliability.md

≈1.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Sending Reliability

Ensuring emails are sent exactly once and handling failures gracefully.

Idempotency

Prevent duplicate emails when retrying failed requests.

The Problem

Network issues, timeouts, or server errors can leave you uncertain if an email was sent. Retrying without idempotency risks sending duplicates.

Solution: Idempotency Keys

Send a unique key with each request. If the same key is sent again, the server returns the original response instead of sending another email.

// Generate deterministic key based on the business event
const idempotencyKey = `password-reset-${userId}-${resetRequestId}`;

await resend.emails.send({
  from: 'noreply@example.com',
  to: user.email,
  subject: 'Reset your password',
  html: emailHtml,
}, {
  headers: {
    'Idempotency-Key': idempotencyKey
  }
});

Key Generation Strategies

Strategy Example Use When
Event-based order-confirm-${orderId} One email per event (recommended)
Request-scoped reset-${userId}-${resetRequestId} Retries within same request
UUID crypto.randomUUID() No natural key (generate once, reuse on retry)

Best practice: Use deterministic keys based on the business event. If you retry the same logical send, the same key must be generated. Avoid Date.now() or random values generated fresh on each attempt.

Key expiration: Idempotency keys are typically cached for 24 hours. Retries within this window return the original response. After expiration, the same key triggers a new send—so complete your retry logic well within 24 hours.

Retry Logic

Handle transient failures with exponential backoff.

When to Retry

Error Type Retry? Notes
5xx (server error) ✅ Yes Transient, likely to resolve
429 (rate limit) ✅ Yes Wait for rate limit window
4xx (client error) ❌ No Fix the request first
Network timeout ✅ Yes Transient
DNS failure ✅ Yes May be transient

Exponential Backoff

async function sendWithRetry(emailData, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await resend.emails.send(emailData);
    } catch (error) {
      if (!isRetryable(error) || attempt === maxRetries - 1) {
        throw error;
      }
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await sleep(delay + Math.random() * 1000); // Add jitter
    }
  }
}

function isRetryable(error) {
  return error.statusCode >= 500 ||
         error.statusCode === 429 ||
         error.code === 'ETIMEDOUT';
}

Backoff schedule: 1s → 2s → 4s → 8s (with jitter to prevent thundering herd)

Error Handling

Common Error Codes

Code Meaning Action
400 Bad request Fix payload (invalid email, missing field)
401 Unauthorized Check API key
403 Forbidden Check permissions, domain verification
404 Not found Check endpoint URL
422 Validation error Fix request data
429 Rate limited Back off, retry after delay
500 Server error Retry with backoff
503 Service unavailable Retry with backoff

Error Handling Pattern

try {
  const result = await resend.emails.send(emailData);
  await logSuccess(result.id, emailData);
} catch (error) {
  if (error.statusCode === 429) {
    await queueForRetry(emailData, error.retryAfter);
  } else if (error.statusCode >= 500) {
    await queueForRetry(emailData);
  } else {
    await logFailure(error, emailData);
    await alertOnCriticalEmail(emailData); // For password resets, etc.
  }
}

Queuing for Reliability

For critical emails, use a queue to ensure delivery even if the initial send fails.

Benefits:

  • Survives application restarts
  • Automatic retry handling
  • Rate limit management
  • Audit trail

Simple pattern:

  1. Write email to queue/database with "pending" status
  2. Process queue, attempt send
  3. On success: mark "sent", store message ID
  4. On retryable failure: increment retry count, schedule retry
  5. On permanent failure: mark "failed", alert

Timeouts

Set appropriate timeouts to avoid hanging requests.

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);

try {
  await resend.emails.send(emailData, { signal: controller.signal });
} finally {
  clearTimeout(timeout);
}

Recommended: 10-30 seconds for email API calls.

Related

Source: SKILL.md on GitHub

No alerts16d4 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill is a comprehensive knowledge base for email best practices, including deliverability, accessibility, compliance, and reliability. It contains educational content and code examples for developers. No malicious patterns or security risks were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 376d1c3. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 4 months ago
Other metadata
metadata
{
  "author": "Resend",
  "version": "1.0.2",
  "homepage": "https://resend.com/agent-skills",
  "source": "https://github.com/resend/email-best-practices",
  "openclaw": {
    "links": {
      "repository": "https://github.com/resend/email-best-practices",
      "documentation": "https://resend.com/docs/email-best-practices-skill"
    }
  }
}
  • email
  • transactional
  • marketing
  • deliverability
  • spf-dkim-dmarc
  • compliance
  • webhooks
  • accessibility
  • list-management
  • resend

README badge

README badge for resend/resend-skills/email-best-practices

Provides decision trees and reference guides for deliverability (SPF/DKIM/DMARC), compliance (CAN-SPAM/GDPR/CASL), transactional vs marketing email classification, webhook handling, list management, and accessibility (alt text, headings, contrast). Targets teams building email features with Resend or similar transactional email APIs.

Generated from the current SKILL.md.

Does this skill cover setting up SPF, DKIM, and DMARC?
Yes. The Deliverability section covers authentication setup and is the first step recommended for fixing spam issues, as unauthenticated emails are commonly rejected by Gmail and Yahoo.
What compliance standards does this skill address?
It covers CAN-SPAM, GDPR, and CASL requirements, with a dedicated Compliance section for legal obligations around email capture and marketing sends.
Does this skill include guidance on making emails accessible?
Yes. The Accessibility section covers screen reader support, alt text, headings, contrast requirements, and proper HTML structure for inclusive email design.
How does this skill handle bounces and list hygiene?
The List Management section covers bounce handling, suppression list updates, and list hygiene jobs. Webhooks & Events section covers processing delivery events to track bounces and complaints.
Does this skill cover retry logic and idempotent sending?
Yes. The Sending Reliability section covers retry strategy, idempotency, and error handling for production-ready email sending.

Generated from the current SKILL.md. These answers refresh after source changes.