JSV-CAPITAL

resend

"Integrate Resend email API for sending transactional and marketing emails with TypeScript/Node.js. Covers all Resend APIs: sending single/batch emails, React Email templates, domain setup and DNS verification, contact and audience management, broadcasts, webhooks, and API key management. Use when the user mentions 'resend,' 'send email,' 'email API,' 'transactional email,' 'react email,' 'email template,' or needs to integrate email sending into a TypeScript/Next.js application using Resend."

JSV-CAPITAL 0 Updated 3mo ago
GitHub

Install

npx skillscat add jsv-capital/resend-skill

Install via the SkillsCat registry.

SKILL.md

Resend

Email API integration for TypeScript/Node.js applications. Covers transactional email, React Email templates, domains, contacts, broadcasts, and webhooks.

Quick Start

npm install resend
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_API_KEY);

const { data, error } = await resend.emails.send({
  from: "App <hello@yourdomain.com>",
  to: "user@example.com",
  subject: "Hello",
  html: "<p>Hello world</p>",
});

Environment variable: RESEND_API_KEY (get from https://resend.com/api-keys).

Task Decision Tree

  1. Sending a single email → Use resend.emails.send(). See Send Email.
  2. Sending bulk emails (same API call) → Use resend.batch.send(). Max 100 per call. No attachments or scheduling.
  3. Building email templates → Use React Email components. See references/react_email.md.
  4. Setting up a domain → Use resend.domains.create(). Then add DNS records. See Domain Setup.
  5. Tracking email events → Configure webhooks in dashboard. Verify with svix. See Webhooks.
  6. Managing contacts/audiences → Use contacts API with audience IDs. See references/api_reference.md > Contacts.
  7. Sending marketing campaigns → Use broadcasts API. See references/api_reference.md > Broadcasts.
  8. Full API details for any endpoint → See references/api_reference.md.

Send Email

Single Email

const { data, error } = await resend.emails.send({
  from: "App <hello@yourdomain.com>",
  to: ["user@example.com"],
  subject: "Order Confirmed",
  html: "<h1>Your order is confirmed</h1>",
  // Optional:
  cc: ["cc@example.com"],
  bcc: ["bcc@example.com"],
  replyTo: "support@yourdomain.com",
  scheduledAt: "in 1 hour",           // or ISO 8601
  headers: { "X-Entity-Ref-ID": "123" },
  tags: [{ name: "category", value: "order" }],
  attachments: [{
    filename: "receipt.pdf",
    content: buffer,                   // Buffer or base64 string
  }],
});

With React Email

import { OrderConfirmation } from "@/emails/order-confirmation";

const { data, error } = await resend.emails.send({
  from: "App <hello@yourdomain.com>",
  to: "user@example.com",
  subject: "Order Confirmed",
  react: OrderConfirmation({ orderId: "12345", items }),
});

For React Email component patterns and the full template reference, see references/react_email.md.

Batch Send

const { data, error } = await resend.batch.send([
  { from: "App <hello@yourdomain.com>", to: "a@example.com", subject: "Hi A", html: "<p>Hello A</p>" },
  { from: "App <hello@yourdomain.com>", to: "b@example.com", subject: "Hi B", html: "<p>Hello B</p>" },
]);

Limits: max 100 emails per request, 50 recipients per email. No attachments or scheduledAt in batch.

Idempotency

Prevent duplicate sends with the Idempotency-Key header (expires 24h):

const { data, error } = await resend.emails.send(
  { from: "...", to: "...", subject: "...", html: "..." },
  { headers: { "Idempotency-Key": "order-12345-confirmation" } }
);

Check Email Status

const { data } = await resend.emails.get("email-id");
// data.last_event: "sent" | "delivered" | "bounced" | "opened" | "clicked" | "complained"

Domain Setup

1. Create Domain

const { data } = await resend.domains.create({
  name: "yourdomain.com",
  region: "us-east-1", // us-east-1 | eu-west-1 | sa-east-1 | ap-northeast-1
});

2. Add DNS Records

The response includes data.records — an array of DNS records to add:

Type Purpose
MX Mail routing
TXT (SPF) Authorize Resend to send
CNAME (DKIM) Cryptographic signing (3 records)

Add all records to your DNS provider. TTL: use Auto or the value from the response.

3. Verify

await resend.domains.verify("domain-id");

Verification can take minutes to hours depending on DNS propagation.

Domain Options

  • openTracking: true — track email opens (adds tracking pixel)
  • clickTracking: true — track link clicks (rewrites links)
  • tlsMode: "enforced" — require TLS (blocks delivery to servers without TLS)
  • capabilities: { sending: "enabled", receiving: "enabled" } — enable inbound email

Webhooks

Event Types

Event Description
email.sent Email sent to recipient server
email.delivered Accepted by recipient server
email.delivery_delayed Temporary delivery failure
email.bounced Permanent delivery failure
email.complained Marked as spam
email.opened Opened (requires open tracking)
email.clicked Link clicked (requires click tracking)

Webhook Handler (Next.js)

// app/api/webhooks/resend/route.ts
import { Webhook } from "svix";

const webhookSecret = process.env.RESEND_WEBHOOK_SECRET;

export async function POST(request: Request) {
  const body = await request.text();
  const headers = Object.fromEntries(request.headers);

  const wh = new Webhook(webhookSecret);
  let payload: any;

  try {
    payload = wh.verify(body, {
      "svix-id": headers["svix-id"],
      "svix-timestamp": headers["svix-timestamp"],
      "svix-signature": headers["svix-signature"],
    });
  } catch {
    return new Response("Invalid signature", { status: 400 });
  }

  switch (payload.type) {
    case "email.delivered":
      // Handle delivery confirmation
      break;
    case "email.bounced":
      // Handle bounce — remove/flag email address
      break;
    case "email.complained":
      // Handle spam complaint — unsubscribe user
      break;
  }

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

Install svix: npm install svix

Next.js Integration Pattern

app/
├── api/
│   ├── send/route.ts          # Email send endpoint
│   └── webhooks/resend/route.ts  # Webhook handler
├── emails/                     # React Email templates
│   ├── welcome.tsx
│   ├── password-reset.tsx
│   └── order-confirmation.tsx
└── lib/
    └── resend.ts              # Shared Resend client

Shared client (lib/resend.ts):

import { Resend } from "resend";

if (!process.env.RESEND_API_KEY) {
  throw new Error("RESEND_API_KEY is required");
}

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

Key Constraints

  • to field: max 50 addresses per email
  • Batch send: max 100 emails per request
  • Tags: max 256 chars per name/value
  • Idempotency keys: expire after 24 hours
  • react param: Node.js SDK only (not available via REST/cURL)
  • Batch does not support attachments or scheduledAt
  • Domain must be verified before sending (use onboarding@resend.dev for testing)
  • Broadcasts can only be updated/deleted while in draft status

References