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
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
Use Go’s time package to generate properly formatted ISO 8601 timestamps:
Relative Time
Specific Date
Business Hours
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 ),
}
import " time "
// Schedule for a specific date and time
location , _ := time . LoadLocation ( "America/New_York" )
scheduledTime := time . Date ( 2024 , 12 , 25 , 9 , 0 , 0 , 0 , location )
params := & resend . SendEmailRequest {
// ... other fields
ScheduledAt : scheduledTime . UTC (). Format ( time . RFC3339 ),
}
import " time "
// Schedule for next business day at 9 AM
now := time . Now ()
nextDay := now . AddDate ( 0 , 0 , 1 )
// Set to 9 AM
scheduledTime := time . Date (
nextDay . Year (), nextDay . Month (), nextDay . Day (),
9 , 0 , 0 , 0 , nextDay . Location (),
)
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
The ID of the scheduled email to update.
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
The ID of the canceled email.
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
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 )
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 )
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 )
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" )},
}
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 ),
}
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
}
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