Build a WhatsApp notification sender
Push Northwind updates via the WhatsApp Business API — for client billing milestones.
Published Jul 23, 2025
Northwind’s clients do not read email the day it arrives, and a billing milestone buried in an inbox gets missed. Most of them, though, check WhatsApp constantly — so when an invoice is raised or a project hits a payment stage, Northwind wants that note to land in WhatsApp, where it will actually be seen.
This script wraps the WhatsApp Business API in one small sendWhatsApp helper.
Give it a destination number and a message and it does the rest. It is the
piece you call from any other automation — an invoice script, a project tracker
— whenever a client needs a nudge.
What you’ll need
- A WhatsApp Business Platform account with a registered phone number, set up through Meta’s developer console.
- Two values saved in Script Properties — see
Store API keys and secrets securely:
WA_PHONE_ID— the Phone Number ID of your WhatsApp sender.WA_TOKEN— a permanent access token for the WhatsApp Business API.
- Recipient numbers in international format without a
+or spaces (e.g.441632960111).
The script
// WhatsApp Business API version. Bump this when Meta deprecates it.
const WA_API_VERSION = 'v18.0';
/**
* Sends a plain-text WhatsApp message through the WhatsApp Business API.
*
* @param {string} to Recipient number, international format, no '+'.
* @param {string} message The message text to send.
*/
function sendWhatsApp(to, message) {
const p = PropertiesService.getScriptProperties();
const phoneId = p.getProperty('WA_PHONE_ID');
const token = p.getProperty('WA_TOKEN');
// Bail out early rather than firing a request that is bound to fail.
if (!phoneId || !token) {
throw new Error('WA_PHONE_ID and WA_TOKEN must be set in Script Properties.');
}
// The endpoint is scoped to your sender's Phone Number ID.
const url = 'https://graph.facebook.com/' + WA_API_VERSION +
'/' + phoneId + '/messages';
const res = UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/json',
headers: { Authorization: 'Bearer ' + token },
payload: JSON.stringify({
messaging_product: 'whatsapp',
to: to,
type: 'text',
text: { body: message },
}),
muteHttpExceptions: true,
});
// A non-2xx status means Meta rejected the message — surface the reason
// instead of letting the caller assume it was delivered.
const code = res.getResponseCode();
if (code < 200 || code >= 300) {
throw new Error('WhatsApp API error (' + code + '): ' + res.getContentText());
}
}
/**
* Example caller: notify a client that their invoice is ready.
*
* @param {string} clientPhone Recipient number, international format.
* @param {string} invoiceNo The invoice reference.
*/
function notifyInvoiceRaised(clientPhone, invoiceNo) {
sendWhatsApp(
clientPhone,
'Hi from Northwind — invoice ' + invoiceNo + ' is ready to view in your portal.'
);
}
How it works
sendWhatsAppreadsWA_PHONE_IDandWA_TOKENfrom Script Properties, so no credentials ever sit in the code.- It guards on those two values. If either is missing it throws straight away,
which is a clearer failure than a
400from Meta about a malformed request. - It builds the Graph API URL, scoped to your sender’s Phone Number ID, with
the API version pulled out into the
WA_API_VERSIONconstant for easy upgrades later. - It POSTs a JSON body in the shape WhatsApp expects —
messaging_product, the recipientto, atypeoftext, and the message undertext.body. - It checks the HTTP status. A non-2xx response throws, carrying Meta’s actual error text, so a delivery failure never looks like a success.
notifyInvoiceRaisedshows the intended use: a thin wrapper that builds one specific message and hands it tosendWhatsApp.
Example run
From another script, or straight from the editor:
sendWhatsApp('441632960111', 'Your Northwind project hit milestone 2 — payment is now due.');
| Input | Value |
|---|---|
to | 441632960111 |
message | Your Northwind project hit milestone 2 — payment is now due. |
The client’s phone shows a WhatsApp message from the Northwind business number:
Your Northwind project hit milestone 2 — payment is now due.
If the number is wrong or the token has expired, sendWhatsApp throws with the
exact error Meta returned, so you can fix the cause rather than guess.
Run it
sendWhatsApp is a helper, not a scheduled job — you call it from whatever
automation reaches the right moment:
- From the Apps Script editor, select
notifyInvoiceRaised(or callsendWhatsAppdirectly) and click Run to send a test message to your own number. - Approve the authorisation prompt the first time.
- In real use, call
sendWhatsAppfrom your invoice or project-tracking script at the point a client needs to hear from you.
Watch out for
- WhatsApp has a 24-hour window rule. Free-form text messages only reach a
client who has messaged your business in the last 24 hours. Outside that
window — which is most of the time for a billing nudge — you must send a
pre-approved message template, not the plain
textbody shown here. - Message templates need Meta’s approval before use. Submit your billing and
milestone templates in the WhatsApp Manager and switch the payload to
type: "template"once they are approved. - Access tokens expire. Temporary tokens last hours; generate a permanent
System User token for production and store it as
WA_TOKEN. - The recipient number must be in international format with no
+, no spaces, and no leading zeros. A malformed number fails or, worse, reaches someone else. - Meta deprecates Graph API versions on a schedule. The
WA_API_VERSIONconstant is there so the upgrade is a one-line change when that happens. - WhatsApp messaging is billed per conversation. High volume adds up — check the current pricing tiers before wiring this into a busy workflow.
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
Send SMS notifications with Twilio
Text Northwind alerts straight from your scripts — for production outages or VIP events.
Updated Jul 19, 2025