"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."
Install
npx skillscat add jsv-capital/resend-skill Install via the SkillsCat registry.
Resend
Email API integration for TypeScript/Node.js applications. Covers transactional email, React Email templates, domains, contacts, broadcasts, and webhooks.
Quick Start
npm install resendimport { 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
- Sending a single email → Use
resend.emails.send(). See Send Email. - Sending bulk emails (same API call) → Use
resend.batch.send(). Max 100 per call. No attachments or scheduling. - Building email templates → Use React Email components. See references/react_email.md.
- Setting up a domain → Use
resend.domains.create(). Then add DNS records. See Domain Setup. - Tracking email events → Configure webhooks in dashboard. Verify with
svix. See Webhooks. - Managing contacts/audiences → Use contacts API with audience IDs. See references/api_reference.md > Contacts.
- Sending marketing campaigns → Use broadcasts API. See references/api_reference.md > Broadcasts.
- 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 clientShared 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
tofield: 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
reactparam: Node.js SDK only (not available via REST/cURL)- Batch does not support
attachmentsorscheduledAt - Domain must be verified before sending (use
onboarding@resend.devfor testing) - Broadcasts can only be updated/deleted while in draft status
References
- Full API reference (all endpoints, params, response formats): references/api_reference.md
- React Email templates (components, patterns, styling): references/react_email.md