Manually sending welcome and follow-up emails to new users doesn't scale and loses engagement without proper segmentation and timing. Resend is an email API platform for developers that automates reliable transactional and marketing email delivery at scale. Use Resend's contacts, segments, and broadcast APIs to build event-driven onboarding sequences that segment users and track engagement. Combine contact creation, segmentation, and scheduled broadcasts to nurture new users through proven multi-step workflows without manual intervention.
What this tutorial covers
- •Outcome: You can design and deploy a fully automated 5-email onboarding sequence that segments users by lifecycle stage and sends time-delayed, personalized follow-ups.
- •Endpoints used: `POST /contacts`, `PATCH /contacts/520784e2-887d-4c25-b53c-4ad46ad38100`, `POST /contacts/e169aa45-1ecf-4183-9955-b1499d5701d3/segments/78261eea-8f8b-4381-83c6-79fa7120f1cf`, `POST /broadcasts`, `POST /broadcasts/559ac32e-9ef5-46fb-82a1-b76b840c0f7b/send`
- •Language: typescript
- •Auth: API key (Authorization header with Bearer token)
- •Estimated implementation time: ~18 minutes
Step 1: Create and track contacts in Resend during user signup
When users sign up, create a contact record in Resend to enable email tracking and segmentation throughout the onboarding journey. Resend contacts store email, name, and custom metadata that feed into segmentation logic and broadcast targeting.
Create contact on signup
1const createContact = async (email: string, firstName: string, lastName: string) => {
2 const response = await fetch('https://api.resend.com/contacts', {
3 method: 'POST',
4 headers: {
5 'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
6 'Content-Type': 'application/json',
7 },
8 body: JSON.stringify({
9 email,
10 first_name: firstName,
11 last_name: lastName,
12 unsubscribed: false,
13 }),
14 });
15
16 const data = await response.json();
17 console.log('Contact created:', data.id);
18 return data.id;
19};
20
21// Call on signup
22await createContact('alice@example.com', 'Alice', 'Johnson');Response:
1{
2 "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
3 "email": "alice@example.com",
4 "first_name": "Alice",
5 "last_name": "Johnson",
6 "unsubscribed": false,
7 "created_at": "2025-01-15T10:30:00Z"
8}Step 2: Segment contacts by onboarding stage to customize email content
Create predefined segments in Resend (e.g., 'Trial Started', 'Feature Activated', 'At Risk') and assign contacts as they progress. Segmentation allows you to send stage-specific emails rather than one generic sequence, improving engagement and relevance.
Add contact to segment
1const addContactToSegment = async (contactId: string, segmentId: string) => {
2 const response = await fetch(
3 `https://api.resend.com/contacts/${contactId}/segments/${segmentId}`,
4 {
5 method: 'POST',
6 headers: {
7 'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
8 'Content-Type': 'application/json',
9 },
10 }
11 );
12
13 const data = await response.json();
14 console.log('Contact added to segment:', data.segment_id);
15 return data;
16};
17
18// Add newly signed-up user to 'Trial Started' segment
19const contactId = 'e169aa45-1ecf-4183-9955-b1499d5701d3';
20const trialStartedSegmentId = '78261eea-8f8b-4381-83c6-79fa7120f1cf';
21await addContactToSegment(contactId, trialStartedSegmentId);Response:
1{
2 "contact_id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
3 "segment_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
4 "segment_name": "Trial Started",
5 "added_at": "2025-01-15T10:35:00Z"
6}Step 3: Update contact metadata to track engagement milestones in Resend
Store engagement signals (e.g., onboarding step completed, feature used, docs viewed) as contact fields to inform branching logic. Use metadata updates to mark progress through the onboarding funnel without moving contacts between segments manually.
Update contact engagement data
1const updateContactMetadata = async (contactId: string, metadata: Record<string, string>) => {
2 const response = await fetch(
3 `https://api.resend.com/contacts/${contactId}`,
4 {
5 method: 'PATCH',
6 headers: {
7 'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
8 'Content-Type': 'application/json',
9 },
10 body: JSON.stringify({
11 unsubscribed: false,
12 ...metadata,
13 }),
14 }
15 );
16
17 const data = await response.json();
18 console.log('Contact metadata updated:', data.id);
19 return data;
20};
21
22// Mark first login completed
23await updateContactMetadata('e169aa45-1ecf-4183-9955-b1499d5701d3', {
24 first_login_completed: 'true',
25 first_login_date: new Date().toISOString(),
26});Response:
1{
2 "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
3 "email": "alice@example.com",
4 "first_name": "Alice",
5 "last_name": "Johnson",
6 "first_login_completed": "true",
7 "first_login_date": "2025-01-15T11:00:00Z",
8 "updated_at": "2025-01-15T11:00:00Z"
9}Step 4: Create a broadcast email template for multi-step sequences in Resend
Build a broadcast (templated email) that contains personalization tokens and targets a specific segment via Resend. Broadcasts decouple content creation from sending logic, allowing you to reuse and schedule the same email for cohorts at different times.
Create broadcast template
1const createBroadcast = async (segmentId: string, emailStep: number) => {
2 const templates: Record<number, { subject: string; html: string }> = {
3 1: {
4 subject: 'Welcome to our platform, {{first_name}}!',
5 html: `<h1>Hello {{first_name}},</h1>
6 <p>We're excited to have you on board. Here's a quick guide to get started.</p>
7 <a href="https://docs.example.com/onboarding">View Onboarding Guide</a>`,
8 },
9 2: {
10 subject: 'Explore your first project setup',
11 html: `<p>Hi {{first_name}},</p>
12 <p>Ready to create your first project? It takes just 2 minutes.</p>
13 <a href="https://app.example.com/projects/new">Create Project</a>`,
14 },
15 3: {
16 subject: 'Top 5 tips for success with our platform',
17 html: `<p>Here are the features your peers love most.</p>
18 <ol><li>Real-time notifications</li><li>Team collaboration</li></ol>`,
19 },
20 };
21
22 const template = templates[emailStep];
23
24 const response = await fetch('https://api.resend.com/broadcasts', {
25 method: 'POST',
26 headers: {
27 'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
28 'Content-Type': 'application/json',
29 },
30 body: JSON.stringify({
31 name: `Onboarding Step ${emailStep}`,
32 subject: template.subject,
33 html: template.html,
34 segment_id: segmentId,
35 }),
36 });
37
38 const data = await response.json();
39 console.log('Broadcast created:', data.id);
40 return data.id;
41};
42
43const broadcastId = await createBroadcast('78261eea-8f8b-4381-83c6-79fa7120f1cf', 1);Response:
1{
2 "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b",
3 "name": "Onboarding Step 1",
4 "subject": "Welcome to our platform, {{first_name}}!",
5 "segment_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
6 "created_at": "2025-01-15T11:15:00Z"
7}Step 5: Send broadcasts on a schedule to automate multi-step email sequences
Schedule and send broadcasts to segment cohorts at timed intervals (e.g., Day 1, Day 3, Day 7) to nurture users through the funnel. Resend's send endpoint executes the broadcast immediately or at a scheduled delay, eliminating manual intervention between steps.
Send broadcast to segment
1const sendBroadcast = async (broadcastId: string, delayMinutes?: number) => {
2 const response = await fetch(
3 `https://api.resend.com/broadcasts/${broadcastId}/send`,
4 {
5 method: 'POST',
6 headers: {
7 'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
8 'Content-Type': 'application/json',
9 },
10 body: JSON.stringify({
11 scheduled_at: delayMinutes
12 ? new Date(Date.now() + delayMinutes * 60 * 1000).toISOString()
13 : undefined,
14 }),
15 }
16 );
17
18 const data = await response.json();
19 console.log('Broadcast sent:', data);
20 return data;
21};
22
23// Send welcome email immediately
24await sendBroadcast('559ac32e-9ef5-46fb-82a1-b76b840c0f7b');
25
26// Schedule next email for 3 days later
27const threeDaysInMinutes = 3 * 24 * 60;
28await sendBroadcast('broadcast-step-2-id', threeDaysInMinutes);Response:
1{
2 "id": "broadcast-send-001",
3 "broadcast_id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b",
4 "status": "queued",
5 "sent_count": 0,
6 "scheduled_at": null,
7 "created_at": "2025-01-15T11:20:00Z"
8}Step 6: Monitor engagement and re-segment based on email interactions
Pull engagement metrics from Resend (opens, clicks) and move contacts between segments to personalize downstream sequences. Use feedback loops to identify inactive users and trigger re-engagement campaigns or move active users to advanced onboarding paths.
Track and re-segment user progression
1const engagementLoop = async (contactId: string, engagement: { opened: boolean; clicked: boolean }) => {
2 // Update contact with engagement metrics
3 await updateContactMetadata(contactId, {
4 last_email_opened: engagement.opened ? new Date().toISOString() : 'never',
5 last_email_clicked: engagement.clicked ? new Date().toISOString() : 'never',
6 });
7
8 // Move to next segment based on engagement
9 if (engagement.clicked) {
10 console.log('User engaged: moving to advanced features segment');
11 await addContactToSegment(contactId, 'advanced-features-segment-id');
12 } else if (!engagement.opened) {
13 console.log('User disengaged: moving to re-engagement segment');
14 await addContactToSegment(contactId, 're-engagement-segment-id');
15 }
16};
17
18// Webhook listener for Resend engagement events
19// (In production, parse Resend webhooks and call engagementLoop for each event)
20await engagementLoop('e169aa45-1ecf-4183-9955-b1499d5701d3', {
21 opened: true,
22 clicked: true,
23});Contacts automatically advance or reset segments based on their email behavior, creating branching onboarding paths without manual curation.
Common pitfalls when using Resend
- •Timing delays between email steps. Real onboarding sequences span 4–6 weeks with 5–8 emails. Space sends by 2–3 days minimum to avoid fatigue; Resend's scheduled_at field handles this, but verify your application logic doesn't re-send the same broadcast.
- •Segment overlap and contact state. Contacts can belong to multiple segments simultaneously. Plan transitions (e.g., remove from 'Trial Started' when moving to 'Feature Activated') via manual updates or automation rules to prevent duplicate emails.
- •Personalization token matching. Template tokens like {{first_name}} must match contact field names exactly. Mismatches render blank placeholders. Always validate contact metadata structure before sending broadcasts.
- •Unsubscribe handling. Monitor and respect the unsubscribed flag in contact records. Resend does not automatically stop broadcasts to unsubscribed contacts; filter them server-side or use segment rules to exclude them.
Ready to launch automated onboarding sequences at scale? Start building with Resend and replace manual email workflows with reliable, developer-friendly APIs.
Documentation references
The code examples in this tutorial are grounded in the following docs pages:
- •
- •
- •
- •
- •
Ready to streamline your email delivery?
Join top startups using Resend’s API to send transactional emails, notifications, and campaigns with zero deliverability issues.

