Back in August 2022, we released Twilio Message Scheduling for general availability! This feature enables you to schedule an SMS, MMS, or WhatsApp message for a fixed time in the future.
Scheduling a message is free of charge and can be done by including the following additional parameters in your API request:
ScheduleType
: indicates your intent to schedule a messageSendAt
: indicates when Twilio will send a message
At the time of publishing this blog post, Twilio Studio, our low-code/no-code application builder, does not have a built-in feature to schedule messages. In this blog post, you’ll learn a workaround that uses Studio and a bit of Python code.
Please note that code snippets will be shown in Python (in this GitHub repo), however the same principle applies to scheduling a message in other languages.
This blog post will be structured as followed (feel free to jump ahead):
- Prerequisites: Things you need before continuing
- Step 1: Create a free Twilio account
- Step 2: Buy a Twilio phone number
- Step 3: Create a Messaging Service
- Step 4: Setup local environment
- Step 5: Configure environment variables
- Step 6: Schedule a text message with Twilio
- Step 7: Create an endpoint to connect with Studio
- Step 8: Use Twilio Studio to schedule a message
- Python 3.7 or higher installed on your computer
- ngrok installed on your machine. ngrok is a useful tool for connecting your local server to a public URL. You can sign up for a free account and learn how to install ngrok
- Access to a phone that can make and receive SMS messages
If you want to give Twilio a spin, and haven’t yet, sign up for a free Twilio account. Sign up is quick and no credit card is required!
The signup process includes verifying your personal phone number, as a security measure.
If you haven’t done so already, buy a Twilio phone number – a phone number purchased through Twilio – to send messages using Twilio.
After signing up for an account, log in to the Twilio Console. Then, navigate to the Phone Numbers page. Click Buy a Number to purchase a Twilio number.
When you sign up for a free Twilio account, you’re given a free trial balance ($15 in the United States) for you to experiment with.
Twilio pricing uses a pay-as-you-go usage-based model for SMS so you aren’t locked into any contracts.
To take advantage of Message Scheduling, you will need to configure your Twilio number with a Messaging Service.
In the Twilio Console, visit the Messaging Services page and click the Create Messaging Service button, then follow the prompts.
On the next screen, enter a Messaging Service friendly name, such as “schedule-a-message”. Then click the Create Messaging Service button.
Click the Add Senders button to add your Twilio phone number to this Messaging Service.
In the Sender Type dropdown, select Phone Number, then click Continue.
Select your Twilio phone number, by clicking the checkbox next to the number you want to use as a sender. Then click the Add Phone Numbers button.
You should see a confirmation notification at the top right corner of the screen that says “Numbers {YOUR-NUMBER} were successfully assigned to the service”
Click the Step 3: Set up integration button to continue.
In this step, you can use the default settings, so click the Step 4: Add compliance info button.
Next, click the Complete Messaging Service Setup button to finalize creating a Messaging Service.
To test out your Messaging Service, click the Try sending a message button.
Continuing on this screen, enter the following details:
- To phone number, or the phone number that would receive the message
- In the From dropdown, select Messaging Service
- Select the Messaging Service which you created earlier
- Input text for the Body and click the Send test SMS button
If successful, you should receive an SMS at your test number and see a similar response to the following in the Console:
201 - CREATED - The request was successful. We created a new resource and the response body contains the representation.
{
"body": "Let's test out our Messaging Service!",
"num_segments": "0",
"direction": "outbound-api",
"from": null,
"date_updated": "Wed, 01 Mar 2023 05:22:27 +0000",
"price": null,
"error_message": null,
"uri": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXX/Messages/SMXXXXXXXXXX.json",
"account_sid": "ACXXXXXXXXXXXXXXXXX",
"num_media": "0",
"to": "+1469XXXXXXX",
"date_created": "Wed, 01 Mar 2023 05:22:27 +0000",
"status": "accepted",
"sid": "SMXXXXXXXXXX",
"date_sent": null,
"messaging_service_sid": "MGXXXXXXXXXXXXXXXXX",
"error_code": null,
"price_unit": null,
"api_version": "2010-04-01",
"subresource_uris": {
"media": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXX/Messages/SMXXXXXXXXXX/Media.json"
}
}
Once you have created a Messaging Service, you should note down your Messaging Service SID from the list of Messaging Services. It should look similar to this: MGXXXXXXXXXXX
Having successfully created a Messaging Service, this step will set up a project that will accommodate your code.
Open a Terminal window and create an empty project directory called twilio-schedule-message:
mkdir twilio-schedule-message
Then change into that directory, as that’s where your code will be.
cd twilio-schedule-message
Since the code for this tutorial will be in Python, create a virtual environment:
python3 -m venv .venv
Activate your virtual environment:
source .venv/bin/activate
Then, using the pip package manager, install the required dependencies in your virtual environment:
pip install python-dotenv twilio>=7.16.5 Flask
With your local environment set up, it’s time to configure environment variables so your credentials are hidden. As a best practice when working with sensitive information like API keys and passwords, it’s important to ensure they are secure and not exposed to the public.
Create a file called .env in the project’s root directory (twilio-schedule-message/) to store your API keys.
Within the .env file, create the following environment variables:
TWILIO_ACCOUNT_SID=PASTE_YOUR_ACCOUNT_SID_HERE
TWILIO_AUTH_TOKEN=PASTE_YOUR_AUTH_TOKEN_HERE
TWILIO_MSG_SRVC_SID=PASTE_YOUR_MESSAGE_SERVICE_SID_HERE
Make sure to replace PASTE_YOUR_ACCOUNT_SID_HERE
and PASTE_YOUR_AUTH_TOKEN_HERE
with the Account SID and Auth Token associated with your Twilio Account. These can be found on the Homepage of your Twilio Console under Account Info.
Also, replace PASTE_YOUR_MESSAGE_SERVICE_SID_HERE
with the Message Service SID from the Message Service you created earlier. This can be found from the list of Messaging Services.
Your .env file should now look similar to this:
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXX
TWILIO_AUTH_TOKEN=12345678901234567
TWILIO_MSG_SRVC_SID=MGXXXXXXXXXXX
If you’re pushing this code to a Git repository, please make sure to add the .env file to your .gitignore so that these credentials are secured. i.e. echo ".env" >> .gitignore
In this next step, you will interact with the Twilio SMS API to create a scheduled message.
If you’d like to see the following code associated with this blog post, it’s available in this GitHub repository.
Within your project directory, create a file called scheduler.py and paste the following code into it:
import os
from datetime import datetime
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
from dotenv import load_dotenv
load_dotenv()
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
client = Client(account_sid, auth_token)
print(repr(datetime.utcnow()))
def schedule_message():
try:
message = client.messages \
.create(
messaging_service_sid = os.getenv('TWILIO_MSG_SRVC_SID'),
to = 'ENTER_THE_NUMBER_YOURE_TEXTING_TO',
body = 'Ahoy, world! This is a scheduled message in Python.',
schedule_type = 'fixed',
send_at = datetime(2023, 3, 3, 5, 55, 10)
)
print(message.sid)
except TwilioRestException as e:
print(e)
raise
schedule_message()
Here’s a breakdown of the code in scheduler.py:
- Lines 1-7, are module imports to make use of their functionality.
- Lines 1-2, provide access to operating system and date functionality.
- Lines 5-6, provide access to functionality within the Twilio API.
- Line 7, provides access to environment variables from the .env file.
- Line 10, reads environment variables from the .env file.
- Lines 13-14, assigns variables from the values of your Twilio credentials.
- Line 15, creates a client object using your Twilio credentials.
- Lines 21-34, define a function that sends a scheduled message using the Twilio API.
- Line 25,
messaging_service_sid
parameter is set to the environment variable of your Messaging Service SID, which identifies the Messaging Service used to send the message. - Line 26,
to
parameter is used as the recipient of the message. Make sure to replaceENTER_THE_NUMBER_YOURE_TEXTING_TO
with the recipient’s number. - Line 27,
body
parameter sets the text of your message. In this case, the recipient will receive a text message reading, “Ahoy, world! This is a scheduled message in Python.” - Line 28,
schedule_type
parameter indicates your intent to schedule a message.fixed
means the message is scheduled at a fixed time. - Line 29,
send_at
parameter indicates the time that Twilio will send the message. It must be in ISO 8601 format. In this example, the message is scheduled to be sent on March 3, 2023 at 5:55.
- Line 25,
- Line 37, invokes the
schedule_message()
function and sends out a text message.
To test out the message, run the scheduler.py file with this command in your terminal:
python3 scheduler.py
Message Scheduling supports messages that need to be scheduled more than 15 minutes but fewer than 7 days in advance. Therefore, when modifying the value of the send_at
parameter, make sure it falls within that range (>15 minutes and <7 days).
Your application is now capable of sending a scheduled message at a fixed time in the future. But by modifying the scheduler.py file, you can adjust the schedule_message()
function to accept dynamic parameters. This way, instead of hardcoding values into the function, they can be provided at execution time for greater flexibility. Check out changes made to the scheduler.py file:
import os
from datetime import datetime
from datetime import timedelta
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
from dotenv import load_dotenv
load_dotenv()
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
client = Client(account_sid, auth_token)
def schedule_message(minutes, body):
try:
message = client.messages \
.create(
messaging_service_sid = os.getenv('TWILIO_MSG_SRVC_SID'),
to = 'ENTER_THE_NUMBER_YOURE_TEXTING_TO',
body = body,
schedule_type = 'fixed',
send_at = minutes_from_now(minutes)
)
print(message.sid)
except TwilioRestException as e:
print(e)
raise
def minutes_from_now(minutes):
if (minutes > 15 and minutes < 10080):
return datetime.utcnow() + timedelta(minutes=minutes)
else:
print('Message must be scheduled more than 15 minutes and fewer than 7 days in advance.')
schedule_message(16, 'Ahoy, world! This is another scheduled message in Python.')
Here’s a breakdown of the revisions made to scheduler.py:
- Line 3, provides access to manipulate date and times.
- Line 20, the
schedule_message()
function now accepts two parameters:minutes
andbody
. Using theminutes
parameter, you can specify how many minutes in advance you want to schedule a message. Using thebody
parameter, you can pass in the body of the text message. - Line 26, the
body
parameter will take in a parameterized message. - Line 28, the
send_at
parameter will invoke a new function calledminutes_from_now()
which returns a datetime object. - Lines 36-40, define a function that returns a datetime object based on the input parameter.
- Lines 37-38, if the input parameter is within 15 minutes to 7 days in advance, a datetime object will be returned.
- Line 43, invokes the
schedule_message()
function and sends out a text message 16 minutes in advance.
Test out the scheduler by re-running the scheduler.py file with this command in your terminal:
python3 scheduler.py
Before continuing to the next step, let’s make one more change to our function. Instead of hardcoding a “to” phone number, you can parameterize it. Make note of the following changes to schedule_message()
:
def schedule_message(to_number, minutes, body):
try:
message = client.messages \
.create(
messaging_service_sid = os.getenv('TWILIO_MSG_SRVC_SID'),
to = to_number,
body = body,
schedule_type = 'fixed',
send_at = minutes_from_now(minutes)
)
print(message.sid)
except TwilioRestException as e:
print(e)
raise
- Line 1, adds a new parameter,
to_number
, to the function. - Line 6, passes the value of
to_number
to theto
parameter. - Delete the call to
schedule_message()
at the bottom of the file
In the previous step, you created a function called schedule_message()
that will schedule a message using the Twilio SMS API. In this step, you will create an endpoint using Flask, a web framework for Python. This endpoint will be used later in Studio and interact with the Make HTTP Request Widget.
Within your project directory, create a file called app.py and paste the following code into it:
from flask import Flask
from flask import Response
from flask import request
from scheduler import schedule_message
app = Flask(__name__)
@app.route("/v1/message/schedule", methods=["POST"])
def send_scheduled_texts():
try:
data = request.get_json()
to_number = data.get("number")
minutes_ahead = data.get("minutes")
message = data.get("message")
schedule_message(to_number, minutes_ahead, message)
return Response('{"status": "Message sent successfully"}', status=201, mimetype='application/json')
except Exception as e:
print(e)
return Response('{"status": "Message was not sent"}', status=500, mimetype='application/json')
app.run(host='localhost', port=3000)
Here’s a breakdown of the code in app.py:
- Lines 1-3, are module imports to make use of Flask functionality.
- Line 5, is a function import from the previously created scheduler.py file.
- Line 8, creates an instance of Flask.
- Lines 11-23, define a function that sets a route for the Flask app to respond to HTTP POST requests via the
/v1/message/schedule
endpoint.- Lines 13-17, extracts JSON data from the request body and assigns them to variables.
- Lines 19-20, calls the
schedule_message()
function, passing in the data from the JSON request. If the function call succeeds, it returns a Response object with a successful response and a 201 status code. - Lines 21-23, if the function call fails, it returns a Response object with a failure response and a 500 status code.
- Lines 26, starts the Flask app listening on port 8080 of localhost.
In a new terminal window, run app.py with the following command:
python3 app.py
If you receive an error message, you may need to comment out the schedule_message()
invocation in scheduler.py on line 43.
At this point, your server should be running on http://localhost:8080. As of now, your application is only running on a server within your computer. But you need a public-facing URL (not http://localhost). You could deploy your application to a remote host, but a quick way to temporarily make your web application available on the Internet is by using a tool called ngrok.
In another terminal window run the following command:
ngrok http 8080
This will create a “tunnel” from the public Internet to port 8080 on your local machine, where the Flask app is listening for requests. You should see output similar to this:
Take note of the line that says “Forwarding”. In the image above, it reads: https://5bad813c2718.ngrok.io -> http://localhost:8080
.
This means that your local application is accessible, publicly, on https://5bad813c2718.ngrok.io
and your endpoint is accessible on https://5bad813c2718.ngrok.io/v1/message/schedule
.
Each time you run the command ngrok http 8080
, a new Forwarding URL will be randomly generated.
Now that you have created the backend for scheduling a message, it’s time to leverage Twilio Studio to schedule a message using your backend application.
You can use an existing Studio Flow or create a new Flow.
To create a new Flow:
- Navigate to the Studio Flows section in the Console.
- Click the Create new Flow button to create a new Flow.
- Name your Flow. For this project, let’s name it “Schedule SMS in Studio”. Then click Next.
- Select the Start from scratch option. Then click Next.
From the Widget Library, drag and drop the Make HTTP Request widget onto the Canvas. Then, from the Trigger widget, draw the Transition from Incoming Message to the Make HTTP Request widget. Your Flow should look similar to this:
Select the Make HTTP Request widget to configure the following properties:
- For the Widget Name, call it something like “schedule_message”
- Select “POST” as the Request Method in the dropdown
- Set the Request URL to “{YOUR-NGROK-URL}/v1/message/schedule”
- Change the Content Type to “Application/JSON” in the dropdown
- Add the following to the Request Body:
{
"number": "{{contact.channel.address}}",
"minutes": 16,
"message": "Ahoy, world!"
}
The Request URL should point to a publicly available URL. If you used ngrok, paste the Forwarding URL from the output of running ngrok http 8080
.
When modifying minutes, make sure it falls within the range (>15 and <10080 minutes), since Message Scheduling supports messages that need to be scheduled more than 15 minutes and fewer than 7 days in advance.
Save those changes by clicking the Save button. Finally, publish your Flow by clicking the Publish button.
Although the Flow is published, you still need to configure your Twilio number to test it out.
Select the Trigger Widget by clicking on it. In Flow Configuration, under Active configurations for this Flow, click the Manage Phone Numbers link.
Select your Twilio phone number and scroll down to the Messaging section. Under A Message Comes In, select Studio Flow. Then select the name of the Studio Flow you created earlier. If you used the example name, it should be “Schedule SMS in Studio”. Then click Save.
With your Flow published, your web server and ngrok running, you can try it out.
Make sure that your web server and ngrok are running before the message is meant to be scheduled (in this case, 16 minutes).
Since the Flow is triggered by an Incoming Message, send a text message to your Twilio number to trigger the scheduled message.
As a recap, when your Flow is triggered, it makes an HTTP Request to your endpoint /v1/message/schedule
. From there, the schedule_message()
function is called which will send a scheduled message based on the input from Studio.
Nice job following along, but the fun doesn’t stop here. There are many other features and use cases you can incorporate into your own workflows. For instance, you can see a list of scheduled messages and also cancel a scheduled message before it’s sent.
For more information about scheduling a message and answering common questions, see the Message Scheduling FAQs.
If you’d like to see the code associated with this blog post, it’s available in this GitHub repository.
Thanks so much for reading! If you found this tutorial helpful, have any questions, or want to show me what you’ve built, let me know online. And if you want to learn more about me, check out my intro blog post.
Anthony Dellavecchia is a Developer Evangelist at Twilio who writes code on stage in front of a crowd. He is an experienced software developer who teaches thousands of people how to change the world with code. His goal is to help you build deep experiences and connections with technology so that they stick with you forever.
Check him out online @anthonyjdella -- Twitter • Linkedin • GitHub • TikTok • Medium • Dev.to • Email • anthonydellavecchia.com 👈

Learn how to use GitHub Actions and SendGrid to stay updated on the latest changes to websites you're interested in.

Learn how to build a helpful AI chatbot on WhatsApp using the Whisper, ChatGPT, and Twilio APIs. Send your AI assistant a voice note and receive informative responses to your questions.

Learn how to receive Twilio webhooks using DigitalOcean Functions, allowing you to create dynamic applications that can process real-time notifications and perform custom actions with ease.

Learn how to create a Wikipedia AI assistant on WhatsApp that can provide instant information and answers, using Python, LangChain, OpenAI, and Twilio.

Learn how to send scheduled WhatsApp messages, using the Programmable Messaging API and Python.

Learn how to manage your SMS surveys from a Python script using Twilio Programmable Messaging.