Schedule email messages to send in the future
The Nylas Email API allows you to schedule email messages to be sent in the future. This means you can draft an email message in advance and schedule it to send when the time is just right — for example, when you want to announce a new version of your product.
This page explains how to schedule email messages with Nylas.
How scheduled send works
When you make a request to the Send Message endpoint, you can define your preferred send time (in Unix timestamp format) in the sent_at
field.
If the end user's provider is Google or Microsoft, you can choose to store the scheduled email message in their Drafts folder on the provider. In this case, you can schedule it to send any time in the future.
You can also choose to have Nylas store the email message until its send time, which must be between 2 minutes and 30 days in the future.
💡 Tip! You can use the message.send_success
and message.send_failed
notification triggers to monitor the status of scheduled email messages. For more information, see the list of notification schemas.
Before you begin
Before you start scheduling email messages, you need the following prerequisites:
- A v3 Nylas application.
- A working authentication configuration. Either...
- A Google or Microsoft grant with at least the following scopes:
- Google:
gmail.send
- Microsoft:
Mail.ReadWrite
andMail.Send
- Google:
Schedule an email message
To schedule an email message to be sent in the future, make a Send Message request that includes the sent_at
field, and provide the time (in Unix timestamp format) when you want the email message to be sent. Nylas returns a schedule_id
that you can use to reference the scheduled message.
curl --request POST \
--url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"subject": "Reaching out with Nylas",
"body": "Hey! This is testing scheduled emails.",
"to": [{
"name": "John Doe",
"email": "john.doe@example.com"
}],
"send_at": 1714478400
}'
{
"request_id": "1",
"grant_id": "<NYLAS_GRANT_ID>",
"data": {
"body": "Hey! This is testing scheduled emails.",
"from": [{
"email": "nyla@example.com"
}],
"subject": "Reaching out with Nylas",
"to": [{
"name": "John Doe",
"email": "john.doe@example.com"
}],
"reply_to_message_id": "",
"send_at": 1714478400,
"use_draft": false,
"schedule_id": "8cd56334-6d95-432c-86d1-c5dab0ce98be"
}
}
If you're storing the email message on Nylas, you can schedule it to be sent between 2 minutes and 30 days in the future. If you're storing it on the provider, you can schedule it to be sent any time in the future.
To store the email message as a draft on the provider, instead of storing it with Nylas, set use_draft
to true
.
You can also use the Nylas SDKs to schedule an email message, as in the examples below.
import 'dotenv/config'
import Nylas from 'nylas'
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}
const nylas = new Nylas(NylasConfig)
async function scheduleSendEmail() {
try {
const sentMessage = await nylas.messages.send({
identifier: process.env.GRANT_ID,
requestBody: {
to: [{ name: "Ram", email: process.env.EMAIL}],
replyTo: [{ name: "Ram", email: process.env.EMAIL}],
subject: "Your Subject Here",
body: "Your email body here.",
sendAt: 1719105508
},
})
console.log('Email scheduled:', sentMessage)
} catch (error) {
console.error('Error scheduling email:', error)
}
}
scheduleSendEmail()
from dotenv import load_dotenv
load_dotenv()
import os
import sys
from nylas import Client
nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)
grant_id = os.environ.get("GRANT_ID")
email = os.environ.get("EMAIL")
message = nylas.messages.send(
grant_id,
request_body={
"to": [{ "name": "Ram", "email": email }],
"reply_to": [{ "name": "Ram", "email": email }],
"subject": "Your Subject Here",
"body": "Your email body here.",
"send_at": 1713824548
}
)
print(message)
require 'nylas'
nylas = Nylas::Client.new(
api_key: "<NYLAS_API_KEY>"
)
request_body = {
subject: "Reaching out with Nylas",
body: "Hey! This is testing scheduled emails.",
to: [{name: "John Doe", email: "john.doe@example.com"}],
send_at: 1714639370
}
email, _ = nylas.messages.send(identifier: "<NYLAS_GRANT_ID>", request_body: request_body)
puts "Message \"#{email[:subject]}\" was sent"
import com.nylas.NylasClient;
import com.nylas.models.*;
import java.util.ArrayList;
import java.util.List;
public class SendScheduledEmails {
public static void main(String[] args) throws
NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
List<EmailName> emailNames = new ArrayList<>();
emailNames.add(new EmailName("john.doe@example.com", "John Doe"));
SendMessageRequest requestBody = new SendMessageRequest.Builder(emailNames).
subject("Reaching out with Nylas").
body("Hey! This is testing scheduled emails.").
sendAt(1714640910).
build();
Response<Message> email = nylas.messages().send("<NYLAS_GRANT_ID>", requestBody);
System.out.println(email.getData());
}
}
import com.nylas.NylasClient
import com.nylas.models.*
fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(
apiKey = "<NYLAS_API_KEY>"
)
val emailNames : List<EmailName> = listOf(EmailName("john.doe@example.com", "John Doe"))
val requestBody : SendMessageRequest = SendMessageRequest.Builder(emailNames).
subject("Reaching out with Nylas").
body("Hey! This is testing scheduled emails.").
sendAt(1714640910).
build()
val email = nylas.messages().send("<NYLAS_GRANT_ID>",requestBody)
print(email.data)
}
Schedule a draft
🔍 Currently, you can schedule drafts for Google and Microsoft Graph accounts only.
If you want to schedule an email message to be sent later, but you're not quite set on what you want the content to be yet, you can schedule a draft. To do this, make a Send Message request that includes the send_at
field, and specify "use_draft": true
.
curl --request POST \
--url https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"subject": "Reaching out with Nylas",
"body": "Hey! This is testing scheduled drafts.",
"to": [{
"name": "John Doe",
"email": "john.doe@example.com"
}],
"send_at": 1714478400,
"use_draft": true
}'
You can also schedule drafts using the Nylas SDKs.
import 'dotenv/config'
import Nylas from 'nylas'
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}
const nylas = new Nylas(NylasConfig)
async function scheduleSendEmail() {
try {
const sentMessage = await nylas.messages.send({
identifier: process.env.GRANT_ID,
requestBody: {
to: [{ name: "Ram", email: process.env.EMAIL}],
replyTo: [{ name: "Ram", email: process.env.EMAIL}],
subject: "Your Subject Here",
body: "Your email body here.",
sendAt: 1719105508,
useDraft: true
},
})
console.log('Email scheduled:', sentMessage)
} catch (error) {
console.error('Error scheduling email:', error)
}
}
scheduleSendEmail()
from dotenv import load_dotenv
load_dotenv()
import os
import sys
from nylas import Client
nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)
grant_id = os.environ.get("GRANT_ID")
email = os.environ.get("EMAIL")
message = nylas.messages.send(
grant_id,
request_body={
"to": [{ "name": "Ram", "email": email }],
"reply_to": [{ "name": "Ram", "email": email }],
"subject": "Your Subject Here",
"body": "Your email body here.",
"send_at": 1713824548,
"use_draft": True
}
)
print(message)
require 'nylas'
nylas = Nylas::Client.new(
api_key: "<NYLAS_API_KEY>"
)
request_body = {
subject: "Reaching out with Nylas",
body: "Hey! This is testing scheduled emails.",
to: [{name: "John Doe", email: "john.doe@example.com"}],
send_at: 1714639370,
use_draft: true
}
email, _ = nylas.messages.send(identifier: "<NYLAS_GRANT_ID>", request_body: request_body)
puts "Message \"#{email[:subject]}\" was sent"
import com.nylas.NylasClient;
import com.nylas.models.*;
import java.util.ArrayList;
import java.util.List;
public class SendScheduledEmails {
public static void main(String[] args) throws
NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
List<EmailName> emailNames = new ArrayList<>();
emailNames.add(new EmailName("john.doe@example.com", "John Doe"));
SendMessageRequest requestBody = new SendMessageRequest.Builder(emailNames).
subject("Reaching out with Nylas").
body("Hey! This is testing scheduled emails.").
sendAt(1714640910).
useDraft(true).
build();
Response<Message> email = nylas.messages().send("<NYLAS_GRANT_ID>", requestBody);
System.out.println(email.getData());
}
}
import com.nylas.NylasClient
import com.nylas.models.*
fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(
apiKey = "<NYLAS_API_KEY>"
)
val emailNames : List<EmailName> = listOf(EmailName("john.doe@example.com", "John Doe"))
val requestBody : SendMessageRequest = SendMessageRequest.Builder(emailNames).
subject("Reaching out with Nylas").
body("Hey! This is testing scheduled emails.").
sendAt(1714640910).
useDraft(true).
build()
val email = nylas.messages().send("<NYLAS_GRANT_ID>",requestBody)
print(email.data)
}
TODO
Nylas saves the email message in the end user's Drafts folder until the defined send_at
time.
⚠️ Do not move or delete scheduled drafts from the end user's Draft folder. If you do, Nylas is unable to send the schedule draft, and returns an error when it tries.
You can edit a scheduled draft until 10 seconds before the defined send_at
time. To do this, make an Update Draft request that includes the draft_id
and the fields you want to modify.
If you want to update a draft's scheduled send time, you must delete the schedule and create a new one.
Read scheduled email messages
You can make a Return Scheduled Messages request or use the Nylas SDKs to get information about all of your scheduled email messages. Nylas returns a list of schedule instructions, including their schedule IDs and information about their status.
curl --request GET \
--url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/schedules' \
--header 'Accept: application/json, application/gzip' \
--header 'Authorization: Bearer <NYLAS_API_KEY>'
[
{
"schedule_id": "8cd56334-6d95-432c-86d1-c5dab0ce98be",
"status": {
"code": "pending",
"description": "schedule send awaiting send at time"
}
},
{
"schedule_id": "rb856334-6d95-432c-86d1-c5dab0ce98be",
"status": {
"code": "sucess",
"description": "schedule send succeeded"
},
"close_time": 1690579819
}
]
import 'dotenv/config'
import Nylas from 'nylas'
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}
const nylas = new Nylas(NylasConfig)
async function fetchMessageSchedules() {
try {
const identifier: string = process.env.NYLAS_GRANT_ID
const messageSchedules = await nylas.messages.listScheduledMessages({
identifier,
})
console.log('Message Schedules:', messageSchedules)
} catch (error) {
console.error('Error fetching message schedules:', error)
}
}
fetchMessageSchedules()
from dotenv import load_dotenv
load_dotenv()
import os
import sys
from nylas import Client
nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)
grant_id = os.environ.get("NYLAS_GRANT_ID")
messages = nylas.messages.list_scheduled_messages(
grant_id
)
print(messages)
require 'nylas'
# Initialize Nylas client
nylas = Nylas::Client.new(
api_key: "<NYLAS_API_KEY>"
)
messages, _ = nylas.messages.list_scheduled_messages(identifier: ENV["NYLAS_GRANT_ID"])
messages.each {|message|
puts message
}
import com.nylas.NylasClient;
import com.nylas.models.*;
public class ReturnMessage {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
ListResponse<ScheduledMessage> message = nylas.messages().listScheduledMessages("<NYLAS_GRANT_ID>");
System.out.println(message.getData());
}
}
import com.nylas.NylasClient
fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(
apiKey = "<NYLAS_API_KEY>"
)
val messages = nylas.messages().listScheduledMessages("<NYLAS_GRANT_ID>").data
print(messages)
}
If you see a schedule instruction that you're interested in, you can pass its schedule_id
in a Return Scheduled Message request or using the Nylas SDKs to get information about it.
curl --request GET \
--url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>' \
--header 'Accept: application/json, application/gzip' \
--header 'Authorization: Bearer <NYLAS_API_KEY>'
{
"schedule_id": "8cd56334-6d95-432c-86d1-c5dab0ce98be",
"status": {
"code": "pending",
"description": "schedule send awaiting send at time"
}
}
import "dotenv/config";
import Nylas from "nylas";
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
};
const nylas = new Nylas(NylasConfig);
async function fetchScheduledMessageById() {
try {
const events = await nylas.messages.findScheduledMessage({
identifier: process.env.NYLAS_GRANT_ID,
scheduleId: process.env.SCHEDULE_ID,
});
console.log("Events:", events);
} catch (error) {
console.error("Error fetching calendars:", error);
}
}
fetchScheduledMessageById();
from dotenv import load_dotenv
load_dotenv()
import os
import sys
from nylas import Client
nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)
grant_id = os.environ.get("NYLAS_GRANT_ID")
schedule_id = os.environ.get("SCHEDULE_ID")
event = nylas.messages.find_scheduled_message(
grant_id,
schedule_id,
)
print(event)
require 'nylas'
# Initialize Nylas client
nylas = Nylas::Client.new(
api_key: "<NYLAS_API_KEY>"
)
messages, _ = nylas.messages.find_scheduled_messages(
identifier: ENV["NYLAS_GRANT_ID"],
schedule_id: "<SCHEDULE_ID>")
messages.each {|message|
puts message
}
import com.nylas.NylasClient;
import com.nylas.models.*;
public class ReturnMessage {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
Response<ScheduledMessage> message = nylas.messages().findScheduledMessage(
"<NYLAS_GRANT_ID>",
"<SCHEDULED_MESSAGE_ID>");
System.out.println(message.getData());
}
}
import com.nylas.NylasClient
fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(
apiKey = "<NYLAS_API_KEY>"
)
val messages = nylas.messages().findScheduledMessage(
"<NYLAS_GRANT_ID>",
"<SCHEDULED_MESSAGE_ID").data
print(messages)
}
Stop a scheduled email message
Sometimes, you might decide you don't want to send a message, and want to stop it from being sent. When this happens, make a Cancel Scheduled Message request with the schedule_id
.
⚠️ You must make a Cancel Scheduled Message request at least 10 seconds before the email message is scheduled to be sent. If you make a cancellation request less than 10 seconds before the send time, Nylas cannot guarantee it will succeed.
curl --request DELETE \
--url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/schedules/<SCHEDULE_ID>' \
--header 'Accept: application/json, application/gzip' \
--header 'Authorization: Bearer <NYLAS_API_KEY>'
{
"Succesfully Request Schedule Deletion": {
"value": {
"request_id": "8cd56334-6d95-432c-86d1-c5dab0ce98be",
"data": {
"message": "requested cancelation for workflow"
}
}
}
}
⚠️ If you delete the instructions to send an email message that isn't saved as a draft, you also delete the un-sent message and its contents. If you stop a scheduled draft ("use_draft": true
), you can still access the draft and its contents by making a Get Draft request that includes the draft_id
.
You can also use the Nylas SDKs to stop a scheduled email message, as in the following code samples.
import "dotenv/config";
import Nylas from "nylas";
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
};
const nylas = new Nylas(NylasConfig);
async function deleteMessageSchedule() {
try {
const result = await nylas.messages.stopScheduledMessage({
identifier: process.env.NYLAS_GRANT_ID,
scheduleId: process.env.SCHEDULE_ID,
});
console.log("Result:", result);
} catch (error) {
console.error("Error deleting message:", error);
}
}
deleteMessageSchedule();
from dotenv import load_dotenv
load_dotenv()
import os
import sys
from nylas import Client
nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)
grant_id = os.environ.get("NYLAS_GRANT_ID")
schedule_id = os.environ.get("SCHEDULE_ID")
result = nylas.messages.stop_scheduled_message(
grant_id,
schedule_id,
)
print(result)
require 'nylas'
# Initialize Nylas client
nylas = Nylas::Client.new(
api_key: "<NYLAS_API_KEY>"
)
messages, _ = nylas.messages.stop_scheduled_messages(
identifier: ENV["NYLAS_GRANT_ID"],
schedule_id: "<SCHEDULE_ID>")
messages.each {|message|
puts message
}
import com.nylas.NylasClient;
import com.nylas.models.*;
public class ReturnMessage {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();
Response<StopScheduledMessageResponse> message = nylas.messages().stopScheduledMessage(
"<NYLAS_GRANT_ID>",
"SCHEDULED_MESSAGE_ID");
System.out.println(message.getData());
}
}
import com.nylas.NylasClient
fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(
apiKey = "<NYLAS_API_KEY>"
)
val messages = nylas.messages().stopScheduledMessage(
"<NYLAS_GRANT_ID>",
"SCHEDULED_MESSAGE_ID").data
print(messages)
}