A spreadsheet that tracks appointments, deliveries, or shift confirmations becomes an active notification tool once it can send a text message directly, instead of leaving reminders as a manual phone-based task for whoever manages the sheet.
This tutorial calls Twilio's Messages resource with a POST request authenticated by HTTP Basic auth built from an Account SID and Auth Token, which is Twilio's standard authentication method for its REST API.
Unlike the JSON-based APIs used elsewhere in this series, Twilio's Messages endpoint expects a classic form-encoded payload with To, From, and Body fields, so the options object passes payload as a plain JavaScript object rather than a JSON string.
Both the Account SID and Auth Token are stored in Script Properties, and the sending phone number is kept as a separate constant so it can be swapped for a different Twilio number without touching the credential values.
Need this built? Hire a Google Apps Script developer →
Sheet / project setup
| Config | Value | Purpose |
|---|---|---|
| TWILIO_ACCOUNT_SID | Script property | Basic auth user |
| TWILIO_AUTH_TOKEN | Script property | Basic auth password |
| From | +15551234567 | Twilio number |
| Sheet | SMS Queue | To, Body, Status, Sid |
What it does
sendTwilioSms builds the Messages.json request URL from the account SID, encodes the SID and Auth Token as a Basic auth header, and posts a To, From, and Body payload to send a single text message.
Prerequisites
Twilio's REST API returns a message resource with a sid field on success, and this function returns that sid so the caller can log which specific message was sent for later status lookups.
Walkthrough
Copy the Account SID and Auth Token from the Twilio Console dashboard, buy or use an existing Twilio phone number capable of SMS, and store the two credential values under TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN in Script Properties.
Edge cases
Note that this endpoint expects form-encoded fields rather than a JSON body, so contentType is left at its UrlFetchApp default and the payload object is passed directly rather than being run through JSON.stringify.
Testing
A response code of 300 or higher indicates Twilio rejected the request, most often due to an unverified destination number on a trial account or an incorrectly formatted To number missing its country code.
Hardening
Trial Twilio accounts can only send to phone numbers verified in the Twilio Console, so confirm the destination number is verified before assuming a failed test run points to a code problem.
Variations
SMS pricing is billed per message segment, and messages longer than 160 characters split into multiple segments, so a production script that sends longer notifications should track message length to estimate cost accurately.
Full code: sendTwilioSms()
Store the Account SID and Auth Token in Script Properties, confirm the destination number is verified on a trial account, then call sendTwilioSms.
var TWILIO_ACCOUNT_SID_PROPERTY = 'TWILIO_ACCOUNT_SID';
var TWILIO_AUTH_TOKEN_PROPERTY = 'TWILIO_AUTH_TOKEN';
var TWILIO_FROM_NUMBER = '+15551234567';
function sendTwilioSms(toNumber, messageBody) {
var props = PropertiesService.getScriptProperties();
var accountSid = props.getProperty(TWILIO_ACCOUNT_SID_PROPERTY);
var authToken = props.getProperty(TWILIO_AUTH_TOKEN_PROPERTY);
if (!accountSid || !authToken) throw new Error('Missing Twilio credentials in script properties.');
var url = 'https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/Messages.json';
var authHeader = 'Basic ' + Utilities.base64Encode(accountSid + ':' + authToken);
var payload = {
To: toNumber,
From: TWILIO_FROM_NUMBER,
Body: messageBody
};
var options = {
method: 'post',
headers: { Authorization: authHeader },
payload: payload,
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(url, options);
var result = JSON.parse(response.getContentText());
if (response.getResponseCode() >= 300) {
throw new Error('Twilio send failed: ' + result.message);
}
return result.sid;
}- Line 7: Reads the Account SID from Script Properties.
- Line 8: Reads the Auth Token from Script Properties.
- Line 11: Builds the Basic auth header from the SID and Auth Token.
- Line 14: Uses form fields To, From, and Body instead of a JSON payload.
- Line 22: Passes the payload object directly since Twilio expects form encoding.
- Line 28: Throws for any response code of 300 or higher.
Deploy this example
- 01
Open Apps Script
In the bound spreadsheet: Extensions → Apps Script. For standalone projects, create one at script.google.com and link your Sheet by ID.
- 02
Paste and save
Add a .gs file, paste the code below, rename constants at the top (sheet names, column letters, API property keys), then save.
- 03
Authorize once
Run the main function from the editor. Accept OAuth scopes when prompted — triggers cannot run until authorization succeeds once.
- 04
Add the trigger
Triggers → Add trigger → choose the handler function and event (time-driven, on edit, or on form submit). Delete test triggers before production.
Before you run: send an SMS with Twilio
- 1Twilio Account SID and Auth Token copied from the Console dashboard
- 2Credentials stored under TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN in Script Properties
- 3TWILIO_FROM_NUMBER set to an SMS-capable Twilio number
- 4Destination number formatted with a country code
- 5Trial account destination numbers verified in the Twilio Console
- 6Response code checked for values of 300 or higher
- 7Message length reviewed for multi-segment billing impact
Frequently asked questions
Twilio's Messages endpoint follows the classic Twilio REST API convention of accepting application/x-www-form-urlencoded fields, and UrlFetchApp automatically form-encodes a plain object passed as payload.
Trial Twilio accounts restrict outbound messages to phone numbers you have explicitly verified in the Console, as a safeguard against abuse; upgrading the account removes that restriction.
It is the unique identifier Twilio assigns to that specific message, which you can use later to query delivery status through the Messages resource.
Use E.164 format, meaning a leading plus sign followed by the country code and subscriber number with no spaces or punctuation, such as +14155552671.
Yes, Twilio's WhatsApp API uses the same Messages endpoint and authentication, only prefixing the To and From numbers with whatsapp: to route through that channel instead.
Segments are limited to 160 GSM-7 characters or 70 UCS-2 characters when the message contains emoji or non-Latin text, so messages with special characters split into more, smaller segments than plain text of the same length.