Send SMS notifications with Twilio
Text Northwind alerts straight from your scripts — for production outages or VIP events.
Published Jul 19, 2025
Email and chat notifications are fine for the routine stuff, but some alerts cannot wait for someone to open their laptop. When Northwind’s production checkout goes down at 2am, or a VIP account signs a renewal, the on-call engineer needs a buzz in their pocket — and an SMS is the one channel that reliably gets through.
Twilio sends a text message over a simple HTTP request, which means Apps Script
can do it with UrlFetchApp and no add-on libraries. This script wraps that
request in a sendSms helper, so any of your other scripts can text a number
in one line. Keep it for the genuinely urgent alerts — texts cost money and
deserve to mean something.
What you’ll need
- A Twilio account, plus a Twilio phone number that can send SMS to your destination countries.
- Your Twilio Account SID and Auth Token, both on the Twilio console dashboard.
- Three values saved in Script Properties — see
Store API keys and secrets securely:
TWILIO_SID— your Account SID.TWILIO_TOKEN— your Auth Token (a secret; never paste it into the code).TWILIO_FROM— your Twilio number in E.164 format, e.g.+447700900123.
- Destination numbers also in E.164 format (a
+, the country code, then the number, with no spaces).
The script
// Twilio's REST endpoint. The {SID} placeholder is filled in at call time
// because the account SID is part of the URL path.
const TWILIO_BASE = 'https://api.twilio.com/2010-04-01/Accounts/';
/**
* Sends a single SMS through Twilio.
*
* @param {string} to The destination number in E.164 format (+447700900123).
* @param {string} body The message text.
* @return {GoogleAppsScript.URL_Fetch.HTTPResponse} The Twilio API response.
*/
function sendSms(to, body) {
const props = PropertiesService.getScriptProperties();
const sid = props.getProperty('TWILIO_SID');
const token = props.getProperty('TWILIO_TOKEN');
const from = props.getProperty('TWILIO_FROM');
// Bail out clearly if the credentials were never set, rather than
// sending a malformed request and reading a cryptic 401.
if (!sid || !token || !from) {
throw new Error('Missing Twilio credentials in Script Properties.');
}
// Twilio uses HTTP Basic auth: the username is the SID, the password is
// the auth token. base64Encode turns "sid:token" into the header value.
const auth = Utilities.base64Encode(sid + ':' + token);
// The request body is form-encoded (not JSON). Passing a plain object as
// "payload" makes UrlFetchApp send it as application/x-www-form-urlencoded.
const response = UrlFetchApp.fetch(TWILIO_BASE + sid + '/Messages.json', {
method: 'post',
headers: { Authorization: 'Basic ' + auth },
payload: { From: from, To: to, Body: body },
muteHttpExceptions: true,
});
// 201 Created means Twilio queued the message. Anything else is a failure
// worth surfacing — an unverified number, no balance, a bad credential.
const code = response.getResponseCode();
if (code !== 201) {
Logger.log('Twilio error ' + code + ': ' + response.getContentText());
}
return response;
}
How it works
TWILIO_BASEholds the constant part of the REST URL. The account SID is part of the path, so it is appended at call time rather than baked into the constant.sendSmsreads all three credentials from Script Properties and throws a clear error if any are missing — far easier to debug than a401from Twilio.- Twilio authenticates with HTTP Basic auth, where the SID is the username and
the auth token is the password.
Utilities.base64Encodebuilds theBasic ...header value fromsid:token. - The request body is form-encoded, not JSON. Passing a plain object as
payloadtellsUrlFetchAppto send it asapplication/x-www-form-urlencoded, which is exactly what Twilio’sMessagesendpoint expects. - A queued message returns HTTP
201 Created. Any other status — an unverified recipient on a trial account, no balance, a typo in a credential — is logged with Twilio’s own error body so you can see what went wrong.
Example run
Call the helper from another script at the point an alert fires:
sendSms('+447700900000', 'Northwind: production checkout is down — investigating.');
The destination handset receives a text from your Twilio number reading
Northwind: production checkout is down — investigating. Twilio’s API returns
a 201 response whose JSON body includes a message SID and a status of
queued, which you can see in the execution log if you need to trace it.
Run it
sendSms is a helper, not a scheduled job — call it from your existing scripts
wherever an urgent event happens:
- From an uptime check, when a health endpoint fails twice in a row.
- From a billing script, when a high-value renewal is signed.
To confirm your setup before wiring it in, run a one-off test from the editor:
function testSms() {
sendSms('+447700900000', 'Northwind SMS test — ignore.');
}
Watch out for
- Every number must be in E.164 format: a leading
+, the country code, then the number, no spaces or dashes. Twilio rejects anything else. - Trial accounts can only text numbers you have verified in the Twilio console, and prepend a trial banner to every message. Upgrade before relying on this in production.
- SMS costs money per message, and prices vary by destination country. Reserve it for genuinely urgent alerts; route routine updates to email or chat.
- A single SMS segment is 160 characters (70 if it contains emoji or other non-GSM characters). Longer messages are split into multiple billed segments, so keep alerts terse.
- The auth token is a full credential — anyone who has it can send messages on your account and run up your bill. Keep it in Script Properties and rotate it in the Twilio console if it ever leaks.
sendSmsreturns once Twilio has queued the message, not once it is delivered. To confirm delivery, configure a status-callback webhook on the Twilio side.
Related
Build a two-factor SMS verification step
Add phone verification to a Northwind workflow — code via Twilio, validated by web app.
Updated Dec 18, 2025
Bridge Sheets to Zapier or Make
Trigger external automations from Northwind Sheets via webhooks — no Apps Script logic needed downstream.
Updated Nov 8, 2025
Send rich notifications to Discord
Push Northwind deploy alerts and KPI updates to a Discord channel — embeds, not plain text.
Updated Oct 15, 2025
Build a payment-webhook receiver
Catch Stripe payment events into a Northwind sheet — paid invoices flip status instantly.
Updated Oct 11, 2025
Build a WhatsApp notification sender
Push Northwind updates via the WhatsApp Business API — for client billing milestones.
Updated Jul 23, 2025