resend-integration
Set up Resend email integration with newsletters, contact forms, and booking systems. Use when implementing email functionality with Resend Audiences, segments, topics, webhooks, and multi-domain accounts.
When & Why to Use This Skill
This Claude skill provides a comprehensive framework for integrating Resend email services into Next.js applications. It streamlines the implementation of automated newsletters, contact forms, and booking systems by leveraging Resend's Audiences, segments, and webhooks, ensuring scalable and organized email communication for developers.
Use Cases
- Automating newsletter subscription workflows with built-in duplicate checks and welcome email triggers.
- Managing multi-domain email communications by tagging contacts with custom properties and internal segments.
- Implementing inbound email forwarding via webhooks to route customer inquiries to team members.
- Integrating real-time email notifications for booking confirmations and lead generation forms with proper audience tracking.
| name | resend-integration |
|---|---|
| description | Set up Resend email integration with newsletters, contact forms, and booking systems. Use when implementing email functionality with Resend Audiences, segments, topics, webhooks, and multi-domain accounts. |
Resend Integration
Complete guide for integrating Resend email services into Next.js applications with proper Audiences setup.
When to Use
- Setting up newsletter signups
- Adding contact form email notifications
- Implementing booking/calendar email confirmations
- Configuring email forwarding via webhooks
- Managing multi-domain Resend accounts
Resend Audiences Architecture
Resend has ONE audience per account. Use these features to organize:
| Feature | Purpose | Visibility |
|---|---|---|
| Contacts | Individual subscribers | - |
| Properties | Custom data fields (domain, source, company) | Internal |
| Segments | Internal groupings for targeting | Internal |
| Topics | User-facing email preferences | User can manage |
| Broadcasts | Campaign sending with auto-unsubscribe | - |
Multi-Domain Strategy
For accounts with multiple domains, tag contacts with properties:
await resend.contacts.create({
email,
properties: {
domain: "example.com", // Which project
source: "newsletter", // How they signed up
},
segments: [{ id: SEGMENT_ID }],
topics: [{ id: TOPIC_ID, subscription: "opt_in" }],
});
Implementation
1. Shared Utility (lib/resend.ts)
import { Resend } from "resend";
export const resend = new Resend(process.env.RESEND_API_KEY);
const SEGMENT_NEWSLETTER = process.env.RESEND_SEGMENT_NEWSLETTER;
const SEGMENT_LEADS = process.env.RESEND_SEGMENT_LEADS;
const TOPIC_NEWSLETTER = process.env.RESEND_TOPIC_NEWSLETTER;
type ContactSource = "newsletter" | "booking" | "contact";
interface CreateContactOptions {
email: string;
firstName?: string;
lastName?: string;
company?: string;
source: ContactSource;
subscribeToNewsletter?: boolean;
}
export async function createContact({
email,
firstName,
lastName,
company,
source,
subscribeToNewsletter = false,
}: CreateContactOptions) {
const segments: { id: string }[] = [];
if (source === "newsletter" && SEGMENT_NEWSLETTER) {
segments.push({ id: SEGMENT_NEWSLETTER });
} else if ((source === "booking" || source === "contact") && SEGMENT_LEADS) {
segments.push({ id: SEGMENT_LEADS });
}
const topics: { id: string; subscription: "opt_in" | "opt_out" }[] = [];
if (subscribeToNewsletter && TOPIC_NEWSLETTER) {
topics.push({ id: TOPIC_NEWSLETTER, subscription: "opt_in" });
}
const properties: Record<string, string> = {
domain: "YOUR_DOMAIN.com", // Replace with actual domain
source,
};
if (company) properties.company = company;
const { data, error } = await resend.contacts.create({
email,
firstName: firstName || undefined,
lastName: lastName || undefined,
unsubscribed: false,
...(Object.keys(properties).length > 0 && { properties }),
...(segments.length > 0 && { segments }),
...(topics.length > 0 && { topics }),
});
if (error?.message?.includes("already exists")) {
return { exists: true, error: null };
}
return { data, exists: false, error };
}
export async function contactExists(email: string): Promise<boolean> {
try {
const { data } = await resend.contacts.get({ email });
return !!data;
} catch {
return false;
}
}
2. Newsletter Route (/api/newsletter)
import { NextResponse } from "next/server";
import { resend, createContact, contactExists } from "@/lib/resend";
export async function POST(request: Request) {
const { email } = await request.json();
if (!email) {
return NextResponse.json({ error: "Email is required" }, { status: 400 });
}
// Duplicate check
if (await contactExists(email)) {
return NextResponse.json(
{ error: "already_subscribed", message: "You're already subscribed!" },
{ status: 409 },
);
}
const { error } = await createContact({
email,
source: "newsletter",
subscribeToNewsletter: true,
});
if (error) {
// Return actual error, not generic 500
const message = typeof error === "object" && "message" in error
? (error as { message: string }).message
: "Failed to subscribe";
const statusCode = typeof error === "object" && "statusCode" in error
? (error as { statusCode: number }).statusCode
: 500;
return NextResponse.json({ error: message }, { status: statusCode });
}
// Send welcome email
await resend.emails.send({
from: "Company <noreply@example.com>",
to: [email],
subject: "Welcome to our Newsletter",
html: `<h2>Thanks for subscribing!</h2>...`,
});
return NextResponse.json({ success: true });
}
3. Frontend Duplicate Handling
const response = await fetch("/api/newsletter", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
const data = await response.json();
if (response.status === 409) {
toast.info("You're already subscribed!");
return;
}
if (!response.ok) {
throw new Error(data.error);
}
toast.success("Thanks for subscribing!");
4. Booking/Contact Form (Create Lead)
Add contact creation without blocking the main flow:
// In booking or contact form API route
createContact({
email,
firstName,
lastName,
company,
source: "booking", // or "contact"
}).catch((err) => console.error("Failed to create contact:", err));
5. Inbound Email Forwarding
For receiving emails via subdomain (e.g., mail.example.com):
Webhook handler (/api/webhooks/resend):
case "email.received":
const forwardTo = process.env.EMAIL_FORWARD_TO?.split(",").map(e => e.trim());
if (!forwardTo?.length) return;
await resend.emails.send({
from: "Forwarded <forwarded@example.com>",
to: forwardTo,
replyTo: event.data.from,
subject: `[Fwd] ${event.data.subject}`,
html: `
<div style="padding: 16px; background: #f5f5f5;">
<p><strong>From:</strong> ${event.data.from}</p>
<p><strong>To:</strong> ${event.data.to?.join(", ")}</p>
</div>
<hr/>
${event.data.html || event.data.text}
`,
attachments: event.data.attachments,
});
break;
Environment Variables
# Required
RESEND_API_KEY=re_xxxxx
# Optional - for Audiences integration
RESEND_SEGMENT_NEWSLETTER=seg_xxxxx
RESEND_SEGMENT_LEADS=seg_xxxxx
RESEND_TOPIC_NEWSLETTER=top_xxxxx
# Optional - for email forwarding
EMAIL_FORWARD_TO=email1@example.com,email2@example.com
Resend Dashboard Setup
IMPORTANT: Create these in the dashboard BEFORE deploying code that uses them.
Create Properties
Properties must exist before the API can use them.
- Go to Audiences → Properties tab
- Create these properties:
domain(text) - For multi-domain account filteringsource(text) - How contact signed up (newsletter, booking, contact)company(text) - Optional company name
Create Segments
- Go to Audiences → Segments
- Create "project-newsletter" segment
- Create "project-leads" segment
- Copy IDs to env vars
Create Topics
- Go to Audiences → Topics
- Create topic (e.g., "Project Newsletter")
- Defaults to: Opt-in (subscribers must explicitly opt in)
- Visibility: Public (visible on preference page) or Private
- Copy ID to env var
Email Receiving (Subdomain)
To receive emails without conflicting with existing email (e.g., Google Workspace):
DNS: Add MX record for subdomain
- Name:
mail - Content:
inbound-smtp.us-east-1.amazonaws.com - Priority: 10
- Name:
Resend: Enable receiving for
mail.yourdomain.comWebhook: Point to your
/api/webhooks/resendendpoint
Broadcasts
Use Resend dashboard for sending newsletters:
- Go to Broadcasts → Create
- Select segment to target
- Use personalization:
{{{FIRST_NAME|there}}} - Include unsubscribe:
{{{RESEND_UNSUBSCRIBE_URL}}} - Send or schedule
Common Patterns
Sender Addresses
Use consistent from addresses:
noreply@domain.com- Automated notificationscontact@domain.com- Contact formbooking@domain.com- Calendar invitesforwarded@domain.com- Forwarded inbound emails
Team Notifications
Send internal notifications to a subdomain address that forwards:
to: ["info@mail.domain.com"] // Forwards via webhook