Guide · For businesses
How to send WhatsApp messages from Google Sheets automatically (form rows, daily reminders and batches)
Four ways to turn rows in a Google Sheet into approved WhatsApp template messages, with working Apps Script code, a gym renewal reminder example, phone-number cleaning and the limits of each method.
By Broadchat teamPublished 8 min read
In short
To send WhatsApp messages from Google Sheets automatically, have a small Google Apps Script call a Broadchat workflow's Custom Webhook URL with the row's data, and let the workflow send an approved template. Use an On form submit trigger for instant messages on new rows, and a daily time-driven trigger for reminders such as renewals. For one-off sends, download the sheet as CSV and use a bulk campaign instead.
Key takeaways
- A new or edited row does not start anything by itself; Apps Script triggers (on form submit, time-driven) are what make a sheet 'automatic'.
- Let the script decide which rows are due and post one row per webhook call; the workflow only has to send the template.
- Every send must be an approved template to an opted-in number with country code, for example 919876543210.
- Write a Sent marker back to the sheet so a row is never messaged twice, whichever method you use.
- Reading the sheet from inside Broadchat works too, but each Google Sheets step uses a paid task and long loops hit run limits.
Send WhatsApp template messages from a Google Sheet automatically
Time needed: 1 h. You will need: A Google Sheet you can edit, Google Apps Script (Extensions → Apps Script), Broadchat panel with Automations.
Prepare the sheet
Put headers in row 1 (Name, Phone, Plan, Expiry, Status) and phone numbers in one column. Add a Status column the automation will fill so no row is messaged twice.
Create the workflow that sends the template
In Automations → Workflow, create a workflow with the Custom Webhook trigger, generate the hook, and add Send Template Message (V2) mapping WhatsApp Number and variables from the webhook fields.
Add the Apps Script
In the sheet open Extensions → Apps Script, paste the script, store the Webhook URL in Script Properties, and run it once to grant permissions.
Add the trigger
In Apps Script's Triggers page, add an On form submit trigger for instant messages or a Time-driven Day timer for daily reminders.
Capture, test and switch on
Use Capture Incoming Webhook with one test row addressed to your own number, map the fields, then activate the workflow and check the run history and the Status column.
Which method fits your sheet
You can send WhatsApp messages from Google Sheets in four ways, and the right one depends on when a row should produce a message:
| Situation | Method | Runs |
|---|---|---|
| A Google Form adds a row and the person should get a WhatsApp at once | Apps Script On form submit → workflow webhook | Instantly, per row |
| Reminders that depend on a date in the sheet (renewals, fee due, appointments) | Apps Script Time-driven trigger selects due rows → webhook | Once a day |
| You prefer to keep everything inside Broadchat | Workflow with Get Rows Data → Iterator → Send Template | When the workflow is called |
| A one-off announcement to everyone in the sheet | Download as CSV → CRM → Bulk Campaign | Once, with a report |
Whatever you choose, the WhatsApp rules are the same: you can start a conversation only with an approved message template, only to people who agreed to receive WhatsApp messages from you, and each number needs its country code.
The example: FitZone gym's membership renewal reminders
FitZone, a gym in Indore, keeps members in a Google Sheet. Three days before a membership ends, the member should get a WhatsApp reminder, and the front desk should be able to see who was reminded. The sheet looks like this:
| Name | Phone | Plan | Expiry | Status |
|---|---|---|---|---|
| Priya Malviya | 9826012345 | Quarterly | 15/10/2026 | |
| Arjun Rathore | +91 98930 54321 | Annual | 18/10/2026 |
The utility template, approved in CRM → WhatsApp Templates, is factual and has no offer in it (Meta may recategorise templates that promote):
Step 1: prepare the sheet
Keep headers in row 1 and one row per person. The Status column is the most important: the automation writes Sent 12-10-2026 there, so the same member is never messaged twice, even if the script runs again. Phone numbers can be messy (spaces, +91, missing 91); the workflow below cleans them, but if your customers are outside India, store full international numbers instead.
Step 2: create the workflow that sends the template
- In Automations → Workflow, click Create Workflow and rename it "FitZone renewal reminder".
- Add the Webhook app's Custom Webhook trigger, enter a Webhook Title, click + Generate Hook and copy the Webhook URL.
- Add CRM → Send Template Message (V2): Select Channel, Select Template
membership_expiry_reminder, WhatsApp Number91slice(parseNumber({{1.phone}});-10), variable 1{{1.name}}, variable 2{{1.plan}}, variable 3{{1.expiry}}. - Optionally add CRM → Assign Tags to Contact (Creates Tag if Tag not exist) with
renewal-due, so the front desk can filter these members in the inbox and a segment.
The number formula strips everything except digits, keeps the last ten and puts 91 in front, so 9826012345, 09826012345 and +91 98930 54321 all become valid Indian WhatsApp numbers. The step needs the country code; a number shorter than eight digits fails.
Step 3: add the Apps Script
In the sheet, open Extensions → Apps Script. In Project Settings, add a Script Property BROADCHAT_HOOK with the Webhook URL (keeping it out of the code means a shared sheet does not expose it). Then paste:
// FitZone: send WhatsApp renewal reminders 3 days before expiry
const SHEET_NAME = 'Members';
const DAYS_BEFORE = 3;
function sendRenewalReminders() {
const hook = PropertiesService.getScriptProperties().getProperty('BROADCHAT_HOOK');
const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
const rows = sheet.getDataRange().getValues();
const head = rows[0];
const col = (name) => head.indexOf(name);
const tz = Session.getScriptTimeZone();
const target = new Date();
target.setDate(target.getDate() + DAYS_BEFORE);
const targetText = Utilities.formatDate(target, tz, 'dd/MM/yyyy');
for (let r = 1; r < rows.length; r++) {
const row = rows[r];
if (row[col('Status')]) continue; // already handled
const expiry = row[col('Expiry')] instanceof Date
? Utilities.formatDate(row[col('Expiry')], tz, 'dd/MM/yyyy')
: String(row[col('Expiry')]);
if (expiry !== targetText) continue; // not due today
const res = UrlFetchApp.fetch(hook, {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({
name: String(row[col('Name')]).split(' ')[0],
phone: String(row[col('Phone')]),
plan: row[col('Plan')],
expiry: expiry
}),
muteHttpExceptions: true
});
const ok = res.getResponseCode() === 200;
sheet.getRange(r + 1, col('Status') + 1)
.setValue((ok ? 'Sent ' : 'Error ' + res.getResponseCode() + ' ') +
Utilities.formatDate(new Date(), tz, 'dd-MM-yyyy'));
}
}Click Run once and approve the permissions Google asks for (access to this spreadsheet and to external URLs). A 200 response means Broadchat accepted the request and started the workflow; the WhatsApp delivery itself shows in the workflow's run history.
For the Google Form version, use the form's submission instead of a loop:
function onFormSubmit(e) {
const v = e.namedValues; // e.g. { 'Name': ['Priya'], 'WhatsApp number': ['9826012345'] }
const hook = PropertiesService.getScriptProperties().getProperty('BROADCHAT_HOOK');
UrlFetchApp.fetch(hook, {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({ name: v['Name'][0], phone: v['WhatsApp number'][0] }),
muteHttpExceptions: true
});
}Add a consent question to the form ("Send me updates on WhatsApp: Yes") and only post when it is Yes.
Step 4: add the trigger
In the Apps Script editor, open Triggers (the clock icon) and click Add Trigger:
- Daily reminders: function
sendRenewalReminders, event source Time-driven, type Day timer, time of day such as 9 am to 10 am. - Form rows: function
onFormSubmit, event source From spreadsheet, event type On form submit.
This is how the sheet becomes automatic: Broadchat workflows have no scheduled trigger and no Google Sheets trigger, so the schedule lives in Google and the sending lives in Broadchat.
Step 5: capture, test and switch on
- Put a test row with your own number and an expiry three days from today.
- In the workflow, click Capture Incoming Webhook, then run
sendRenewalRemindersfrom Apps Script. Click Show Received Data to confirm the fieldsname,phone,planandexpiry. - Clear the test row's Status, save the workflow and switch it to Active. Run the script again: you should receive the reminder and see
Sent …in the Status column. - If nothing arrives, open the workflow's History: a failed step shows the error, such as a number without country code or a low channel wallet balance.
Doing it all inside Broadchat
If you prefer not to write any script, a workflow can read the sheet itself. Connect Google once in Automations → App Authentications (Add Connection, then Google (Sheets, Drive, Meet)), then build: a Custom Webhook trigger, Google Sheet → Get Rows Data (Spreadsheet ID and Sheet Name), an Iterator with Array {{2.rows}}, a filter on the next connection such as {{3.element.data.Status}} Not Exist, Send Template Message (V2) with {{3.element.data.Phone}}, and Google Sheet → Update Row with Row Number {{3.element.row_number}} and Status = Sent.
Two limits make this best for small lists: every Google Sheets step uses one paid task from your plan, and a single run stops after 400 steps, so a loop with two steps per row handles fewer than 200 rows per call. You still need something to call the webhook each day, which brings you back to an Apps Script trigger. The Google Sheets integration page covers the app's other actions, such as lookups.
One-off sends: use a campaign
For a single announcement ("the gym is closed on Diwali"), automation is overkill. Download the sheet as CSV, open CRM → Bulk Campaign, create a campaign with the Quick audience, upload the CSV and map its columns to the template variables. You get delivery and read reports per number. A Quick audience does not skip contacts marked as unsubscribed, so clean the list first, or import it into contacts and send to a segment. The guide to sending bulk WhatsApp messages without getting blocked covers pacing and quality.
Costs and good practice
- Utility templates such as reminders are charged per delivered message: ₹0.115 per utility message to India (Meta rate card effective 1 Oct 2026, excl. GST), from the channel's messaging wallet. See WhatsApp API pricing.
- Each workflow step counts as a task; the webhook method above uses two or three free tasks per member and no paid tasks.
- Send reminders in daytime hours and keep them factual. A reminder that turns into an offer is a marketing template, priced and limited differently.
For how triggers, workflows and webhooks fit together, read the WhatsApp automation workflows guide; for the basics, what the WhatsApp Business API is. Agencies that build sheet-based reminders for gyms, clinics and coaching centres can sell them under their own brand with the white-label platform.
Sources
- Installable triggers — Google for Developers, checked 12 October 2026
- Class UrlFetchApp — Google for Developers, checked 12 October 2026
- Send message templates — Meta for Developers, checked 12 October 2026
- Pricing on the WhatsApp Business Platform — Meta for Developers, checked 12 October 2026
Broadchat team · Product and partner success team
The people who build and support Broadchat, the white-label WhatsApp Business API platform. We write from what we see while onboarding partners and their clients on the official WhatsApp Cloud API, and we check every article against Meta's current documentation.
Last updated:
Frequently asked questions
Can a new row in Google Sheets send a WhatsApp message by itself?
Not without a trigger. Use a Google Apps Script with an On form submit or time-driven trigger to call a Broadchat workflow's Custom Webhook URL, and send an approved template from that workflow.
Do I need the Google Sheets app connected in Broadchat for this?
Not for the Apps Script method, where the script reads the sheet and posts each row to the webhook. You need the Google connection only if the workflow itself reads or updates the sheet with Google Sheet steps.
How do I stop the same person getting the message twice?
Write a marker to a Status column when a row is sent, and skip rows that already have one. The script in this guide does that automatically, including writing the HTTP error code when a call fails.
What phone number format does WhatsApp need?
Digits with the country code and no plus sign or spaces, such as 919826012345 for an Indian mobile. The workflow formula 91slice(parseNumber(...);-10) converts common Indian formats; do not use it for international numbers.