Skip to main content
The Resend Go SDK allows you to schedule emails for future delivery, update scheduled send times, and cancel scheduled emails.

Quick Start

Here’s how to schedule an email for future delivery:
package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/resend/resend-go/v3"
)

func main() {
	ctx := context.Background()
	apiKey := os.Getenv("RESEND_API_KEY")

	client := resend.NewClient(apiKey)

	// Schedule email for 1 hour from now
	scheduledTime := time.Now().Add(1 * time.Hour)

	params := &resend.SendEmailRequest{
		To:          []string{"[email protected]"},
		From:        "[email protected]",
		Subject:     "Scheduled Email",
		Text:        "This email was scheduled for delivery.",
		ScheduledAt: scheduledTime.Format(time.RFC3339),
	}

	sent, err := client.Emails.SendWithContext(ctx, params)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Email scheduled with ID: %s\n", sent.Id)
}

Scheduling Emails

Using ScheduledAt Field

Add the ScheduledAt field to your SendEmailRequest with an ISO 8601 formatted datetime:
ctx := context.Background()

// Schedule for a specific date and time
params := &resend.SendEmailRequest{
	To:          []string{"[email protected]"},
	From:        "[email protected]",
	Subject:     "Hello from Golang",
	Text:        "hello world",
	ScheduledAt: "2024-09-05T11:52:01.858Z",
}

sent, err := client.Emails.SendWithContext(ctx, params)
if err != nil {
	panic(err)
}

fmt.Println("Scheduled email ID:", sent.Id)
Source: examples/schedule_email.go:17
scheduledAt
string
ISO 8601 datetime string (e.g., 2024-09-05T11:52:01.858Z) specifying when the email should be sent.Must be in the future. The maximum scheduling window may vary - check Resend’s documentation for current limits.
Source: emails.go:40

Formatting Schedule Times

Use Go’s time package to generate properly formatted ISO 8601 timestamps:
import "time"

// Schedule for 2 hours from now
scheduledTime := time.Now().Add(2 * time.Hour)

params := &resend.SendEmailRequest{
    // ... other fields
    ScheduledAt: scheduledTime.Format(time.RFC3339),
}
The ScheduledAt time must be in the future. Emails with past timestamps will be rejected.

Updating Scheduled Emails

You can reschedule an email before it’s sent:
ctx := context.Background()

// Update the scheduled time
updateParams := &resend.UpdateEmailRequest{
	Id:          "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794",
	ScheduledAt: "2024-11-05T11:52:01.858Z",
}

updatedEmail, err := client.Emails.UpdateWithContext(ctx, updateParams)
if err != nil {
	panic(err)
}

fmt.Printf("Updated email: %s\n", updatedEmail.Id)
fmt.Printf("Object: %s\n", updatedEmail.Object)
Source: examples/schedule_email.go:32

UpdateEmailRequest Fields

id
string
required
The ID of the scheduled email to update.
scheduledAt
string
required
New ISO 8601 datetime for when the email should be sent.
Source: emails.go:55

Update Methods

// Simple update without context
updated, err := client.Emails.Update(params)
if err != nil {
	panic(err)
}
Source: emails.go:230
You can only update emails that haven’t been sent yet. Once an email is sent, it cannot be modified.

Canceling Scheduled Emails

Cancel a scheduled email before it’s sent:
ctx := context.Background()

emailId := "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"

canceled, err := client.Emails.CancelWithContext(ctx, emailId)
if err != nil {
	panic(err)
}

fmt.Printf("Canceled email: %s\n", canceled.Id)
fmt.Printf("Object: %s\n", canceled.Object)
Source: examples/schedule_email.go:44

Cancel Methods

// Simple cancel without context
canceled, err := client.Emails.Cancel(emailId)
if err != nil {
	panic(err)
}
Source: emails.go:200

CancelScheduledEmailResponse

id
string
The ID of the canceled email.
object
string
Object type, typically "email".
Source: emails.go:44
You can only cancel emails that are still scheduled. Emails that have already been sent cannot be canceled.

Common Use Cases

// Schedule a reminder 24 hours before an event
eventTime, _ := time.Parse(time.RFC3339, "2024-12-25T14:00:00Z")
reminderTime := eventTime.Add(-24 * time.Hour)

params := &resend.SendEmailRequest{
    To:      []string{user.Email},
    From:    "[email protected]",
    Subject: "Event Reminder: Tomorrow at 2 PM",
    Html: fmt.Sprintf(
        "<h1>Reminder</h1><p>Your event is tomorrow at %s</p>",
        eventTime.Format("3:04 PM MST"),
    ),
    ScheduledAt: reminderTime.Format(time.RFC3339),
    Tags: []resend.Tag{
        {Name: "type", Value: "reminder"},
        {Name: "event_id", Value: eventID},
    },
}

sent, err := client.Emails.Send(params)
// Store sent.Id to cancel/update if event changes
// Schedule a series of onboarding emails
signupTime := time.Now()

emails := []struct {
    delay   time.Duration
    subject string
    content string
}{
    {24 * time.Hour, "Welcome!", "Day 1 welcome content"},
    {72 * time.Hour, "Getting Started", "Day 3 getting started guide"},
    {168 * time.Hour, "Tips & Tricks", "Day 7 advanced tips"},
}

var scheduledIds []string

for i, email := range emails {
    scheduledTime := signupTime.Add(email.delay)
    
    params := &resend.SendEmailRequest{
        To:          []string{user.Email},
        From:        "[email protected]",
        Subject:     email.subject,
        Html:        email.content,
        ScheduledAt: scheduledTime.Format(time.RFC3339),
        Tags: []resend.Tag{
            {Name: "campaign", Value: "onboarding"},
            {Name: "sequence", Value: fmt.Sprintf("%d", i+1)},
            {Name: "user_id", Value: user.ID},
        },
    }
    
    sent, err := client.Emails.Send(params)
    if err != nil {
        // Handle error
        continue
    }
    
    scheduledIds = append(scheduledIds, sent.Id)
}

// Store scheduledIds to cancel campaign if user unsubscribes
// Schedule weekly report every Monday at 9 AM
func scheduleWeeklyReport(ctx context.Context, client *resend.Client) (string, error) {
    now := time.Now()
    
    // Calculate next Monday
    daysUntilMonday := (8 - int(now.Weekday())) % 7
    if daysUntilMonday == 0 {
        daysUntilMonday = 7 // If today is Monday, schedule for next Monday
    }
    
    nextMonday := now.AddDate(0, 0, daysUntilMonday)
    scheduledTime := time.Date(
        nextMonday.Year(), nextMonday.Month(), nextMonday.Day(),
        9, 0, 0, 0, now.Location(),
    )
    
    params := &resend.SendEmailRequest{
        To:          []string{"[email protected]"},
        From:        "[email protected]",
        Subject:     fmt.Sprintf("Weekly Report - %s", scheduledTime.Format("Jan 2")),
        Html:        "<p>Your weekly report will be generated and sent.</p>",
        ScheduledAt: scheduledTime.Format(time.RFC3339),
        Tags: []resend.Tag{
            {Name: "type", Value: "weekly_report"},
        },
    }
    
    sent, err := client.Emails.SendWithContext(ctx, params)
    if err != nil {
        return "", err
    }
    
    return sent.Id, nil
}
// Cancel scheduled email if user takes action
func handleUserConfirmation(ctx context.Context, client *resend.Client, scheduledEmailId string) error {
    // User confirmed attendance, cancel the reminder
    canceled, err := client.Emails.CancelWithContext(ctx, scheduledEmailId)
    if err != nil {
        return fmt.Errorf("failed to cancel reminder: %w", err)
    }
    
    log.Printf("Canceled reminder email: %s", canceled.Id)
    
    // Send immediate confirmation instead
    confirmParams := &resend.SendEmailRequest{
        To:      []string{user.Email},
        From:    "[email protected]",
        Subject: "Attendance Confirmed",
        Html:    "<p>Thanks for confirming! See you there.</p>",
    }
    
    _, err = client.Emails.SendWithContext(ctx, confirmParams)
    return err
}

Scheduling with Templates

You can schedule template-based emails:
ctx := context.Background()

// Schedule a templated email
scheduledTime := time.Now().Add(24 * time.Hour)

params := &resend.SendEmailRequest{
	To: []string{"[email protected]"},
	Template: &resend.EmailTemplate{
		Id: "welcome-email",
		Variables: map[string]any{
			"userName":    "Alice",
			"companyName": "Acme Corp",
		},
	},
	ScheduledAt: scheduledTime.Format(time.RFC3339),
	Tags: []resend.Tag{
		{Name: "campaign", Value: "welcome"},
	},
}

sent, err := client.Emails.SendWithContext(ctx, params)
if err != nil {
	panic(err)
}

fmt.Printf("Scheduled templated email: %s\n", sent.Id)

Scheduling Batch Emails

Schedule multiple emails at once using batch send:
ctx := context.Background()

// Schedule for tomorrow at 10 AM
tomorrow := time.Now().AddDate(0, 0, 1)
scheduledTime := time.Date(
	tomorrow.Year(), tomorrow.Month(), tomorrow.Day(),
	10, 0, 0, 0, tomorrow.Location(),
)

batchEmails := []*resend.SendEmailRequest{
	{
		To:          []string{"[email protected]"},
		From:        "[email protected]",
		Subject:     "Monthly Newsletter",
		Html:        "<p>Newsletter content...</p>",
		ScheduledAt: scheduledTime.Format(time.RFC3339),
	},
	{
		To:          []string{"[email protected]"},
		From:        "[email protected]",
		Subject:     "Monthly Newsletter",
		Html:        "<p>Newsletter content...</p>",
		ScheduledAt: scheduledTime.Format(time.RFC3339),
	},
}

sent, err := client.Batch.SendWithContext(ctx, batchEmails)
if err != nil {
	panic(err)
}

fmt.Printf("Scheduled %d emails\n", len(sent.Data))
Batch scheduling is useful for sending newsletters or announcements to multiple recipients at a specific time.

Error Handling

Handle errors when scheduling, updating, or canceling emails:
// Scheduling error handling
params := &resend.SendEmailRequest{
	To:          []string{"[email protected]"},
	From:        "[email protected]",
	Subject:     "Test",
	Text:        "Test",
	ScheduledAt: "invalid-date", // Invalid format
}

sent, err := client.Emails.Send(params)
if err != nil {
	// Handle invalid date format error
	fmt.Printf("Failed to schedule: %v\n", err)
	return
}

// Update error handling
updateParams := &resend.UpdateEmailRequest{
	Id:          "non-existent-id",
	ScheduledAt: time.Now().Add(1 * time.Hour).Format(time.RFC3339),
}

updated, err := client.Emails.Update(updateParams)
if err != nil {
	// Handle email not found or already sent
	fmt.Printf("Failed to update: %v\n", err)
	return
}

// Cancel error handling
canceled, err := client.Emails.Cancel("already-sent-id")
if err != nil {
	// Handle email already sent or not found
	fmt.Printf("Failed to cancel: %v\n", err)
	return
}

Best Practices

1

Use UTC for Scheduling

Always convert to UTC when scheduling to avoid timezone issues:
localTime := time.Date(2024, 12, 25, 9, 0, 0, 0, time.Local)
scheduledTime := localTime.UTC()

ScheduledAt: scheduledTime.Format(time.RFC3339)
2

Store Email IDs

Save scheduled email IDs to enable updates or cancellations:
sent, err := client.Emails.Send(params)
if err != nil {
    return err
}

// Store in database for later reference
db.SaveScheduledEmail(user.ID, sent.Id, scheduledTime)
3

Add Scheduling Buffer

Schedule emails with a buffer to account for processing time:
// Add 5 minutes buffer
desiredTime := time.Date(2024, 12, 25, 9, 0, 0, 0, time.UTC)
scheduledTime := desiredTime.Add(-5 * time.Minute)
4

Tag Scheduled Emails

Use tags to identify and track scheduled campaigns:
Tags: []resend.Tag{
    {Name: "scheduled", Value: "true"},
    {Name: "campaign_id", Value: campaignID},
    {Name: "send_date", Value: scheduledTime.Format("2006-01-02")},
}
5

Handle Timezone Conversions

When working with user timezones, convert properly:
userLocation, _ := time.LoadLocation(user.Timezone)
localTime := time.Date(2024, 12, 25, 9, 0, 0, 0, userLocation)

params := &resend.SendEmailRequest{
    // ...
    ScheduledAt: localTime.UTC().Format(time.RFC3339),
}
6

Implement Cancellation Logic

Build logic to cancel scheduled emails when events change:
func cancelScheduledReminders(ctx context.Context, eventID string) error {
    emailIDs := db.GetScheduledEmailsByEvent(eventID)
    
    for _, emailID := range emailIDs {
        _, err := client.Emails.CancelWithContext(ctx, emailID)
        if err != nil {
            log.Printf("Failed to cancel %s: %v", emailID, err)
            continue
        }
    }
    
    return nil
}

Time Format Reference

The SDK expects ISO 8601 format (RFC3339). Here are common formatting examples:
import "time"

// RFC3339 format (recommended)
scheduledAt := time.Now().Format(time.RFC3339)
// Output: "2024-09-05T11:52:01Z"

// With timezone
scheduledAt := time.Now().Format(time.RFC3339)
// Output: "2024-09-05T11:52:01-05:00"

// With milliseconds
scheduledAt := time.Now().Format("2006-01-02T15:04:05.000Z07:00")
// Output: "2024-09-05T11:52:01.858Z"

// Custom format (ensure ISO 8601 compliance)
scheduledAt := time.Now().UTC().Format("2006-01-02T15:04:05Z")
// Output: "2024-09-05T11:52:01Z"
Always use time.RFC3339 or a compatible ISO 8601 format. Other formats may be rejected by the API.

Next Steps

Sending Emails

Learn the basics of sending emails

Email Templates

Schedule template-based emails

Batch Emails

Schedule multiple emails at once

Attachments

Add attachments to scheduled emails

Build docs developers (and LLMs) love