Send and log WhatsApp messages from Sheets using Meta’s Cloud API + Google Apps Script—no paid middleware required.

TL;DR

You can connect WhatsApp Cloud API to Google Sheets for free by (1) creating a Meta developer app and getting a test sender number + token, (2) writing a short Apps Script to call POST /{PHONE_NUMBER_ID}/messages, and (3) optionally exposing a Sheets webhook (Apps Script web app URL) to log replies/statuses back into your sheet. During development, Meta lets you add up to five recipients and send messages using a test number; in production you’ll need a verified business number and follow WhatsApp’s 24‑hour and template rules.

What you’ll need

  • A Google Sheet (to hold recipients, messages, logs).
  • A Meta Developer account + a WhatsApp Cloud API app with:
    • Phone Number ID and WhatsApp Business Account (WABA) ID
    • Access token (temporary for dev; create a permanent token for production) [labnol.org], [docs.watso.app]
  • (Optional) A public webhook URL for incoming events—Apps Script can act as a web app endpoint. [coefficient.io]

Step 1 — Set up WhatsApp Cloud API (free dev setup)

  1. Go to developers.facebook.com → Create App → Business → Add product: WhatsApp. This provisions your test sender number, Phone Number ID, and a short‑lived token. [labnol.org]
  2. Add recipients (up to 5 numbers) in the “API Setup” screen and verify them with the code WhatsApp sends—only verified dev recipients can receive your messages from the test number. [labnol.org]
  3. (Production later) Generate a permanent access token from Business Manager → System Users and store it server‑side; the temporary token expires quickly. [docs.watso.app], [devopsschool.com]

Why this is free: Meta’s test environment provides a sender number and lets you message up to 5 verified recipients while you build. You’ll pay conversation fees only when you move to production sending at scale. [labnol.org]

Step 2 — Prepare your Google Sheet

Create columns like:

  • A: phone_e164 (e.g., +14155551234)
  • B: message_text
  • C: status (write back “SENT/DELIVERED/READ/ERROR”)
  • D: message_id

This schema will keep the example code simple.

Step 3 — Send WhatsApp messages from Apps Script

Open Extensions → Apps Script and add this code. Replace placeholders with your values from Meta.

 

/**
* WhatsApp Cloud API → Send messages from Google Sheets
* Sheet columns: A=phone_e164, B=message_text, C=status, D=message_id
*/
const WA_TOKEN = ‘YOUR_LONG_LIVED_OR_TEMP_TOKEN’;       // Dev: temp token; Prod: permanent token
const PHONE_NUMBER_ID = ‘YOUR_PHONE_NUMBER_ID’;         // From API Setup in Meta
const GRAPH_VERSION = ‘v21.0’;                          // Or the current version in Meta docs
const GRAPH_URL = https://graph.facebook.com/${GRAPH_VERSION}/${PHONE_NUMBER_ID}/messages;

 

function sendWhatsAppFromSheet() {
  const sh = SpreadsheetApp.getActiveSheet();
  const rows = sh.getRange(2,1, sh.getLastRow()-1, 4).getValues(); // A:D
  rows.forEach((row, i) => {
    const to = row[0];                // phone_e164
    const body = row[1];              // message_text
    if (!to || !body) return;

 

    const payload = {
      messaging_product: “whatsapp”,
      to: to.replace(/\s+/g,”),
      type: “text”,
      text: { body }
    };

 

    const options = {
      method: “post”,
      contentType: “application/json”,
      headers: { Authorization: Bearer ${WA_TOKEN} },
      payload: JSON.stringify(payload),
      muteHttpExceptions: true
    };

 

    const res = UrlFetchApp.fetch(GRAPH_URL, options); // Cloud API /messages
    const json = JSON.parse(res.getContentText() || “{}”);
    const status = res.getResponseCode() === 200 ? ‘SENT’ : ERROR:${res.getResponseCode()};
    const messageId = (json.messages && json.messages[0] && json.messages[0].id) || ”;

 

    sh.getRange(i+2, 3).setValue(status);     // C: status
    sh.getRange(i+2, 4).setValue(messageId);  // D: message_id
    Utilities.sleep(250); // polite pacing
  });
}

 

  • The UrlFetchApp.fetch → POST /{PHONE_NUMBER_ID}/messages call follows Meta’s Cloud API spec and works well from Apps Script for dev/test. [labnol.org]
  • If messages don’t arrive, ensure the recipient is verified during development and respect the 24‑hour session rule (free‑form messages only inside an active window; otherwise use approved templates). [stackoverflow.com], [chatarmin.com]

Step 4 (Optional) — Receive messages/statuses into Sheets via a Webhook

You can capture replies and delivery statuses in your sheet.

4A. Publish an Apps Script web app as your webhook endpoint

  1. In Apps Script, add a doGet and doPost handler (see below).
  2. Deploy → New deployment → type: Web app → Anyone (or “Anyone with link”). Copy the web app URL. Apps Script web apps are valid HTTPS endpoints you can use as webhooks. [coefficient.io]

 

// Basic WhatsApp webhook (verification + logging)
const VERIFY_TOKEN = ‘set-a-random-string’;

 

function doGet(e) {
  // Meta verification challenge
  const mode = e.parameter[‘hub.mode’];
  const token = e.parameter[‘hub.verify_token’];
  const challenge = e.parameter[‘hub.challenge’];
  if (mode === ‘subscribe’ && token === VERIFY_TOKEN) {
    return ContentService.createTextOutput(challenge);
  }
  return ContentService.createTextOutput(‘forbidden’).setMimeType(ContentService.MimeType.TEXT);
}

 

function doPost(e) {
  const body = JSON.parse(e.postData.contents || ‘{}’);
  const entries = body.entry || [];
  const sh = SpreadsheetApp.getActiveSheet();

 

  entries.forEach(entry => {
    (entry.changes || []).forEach(change => {
      const messages = (((change.value || {}).messages) || []);
      messages.forEach(m => {
        const from = m.from;                 // sender wa_id
        const text = m.text && m.text.body;  // incoming text
        sh.appendRow([from, text, ‘INBOUND’, m.id || ”, new Date()]);
      });
      const statuses = (((change.value || {}).statuses) || []);
      statuses.forEach(s => {
        sh.appendRow([s.recipient_id, s.status, ‘STATUS’, s.id || ”, new Date()]);
      });
    });
  });

 

  return ContentService.createTextOutput(‘OK’);
}

 

4B. Point WhatsApp to your webhook

In Meta App → WhatsApp → Configuration, set Callback URL to your web app URL and Verify Token to the value in your script, then subscribe to fields like messages and message_template_status_update. [support.bolddesk.com]

Need a reference listener? Meta’s Node.js SDK docs show the same concepts (verify token, webhook endpoint, payload handling) if you later move off Apps Script. [whatsapp.github.io]

Common pitfalls (and fixes)

  • Nothing arrives to the recipient
    Ensure the phone is one of the verified dev recipients for the test sender and that you use the correct Phone Number ID in the Graph URL. [labnol.org]
  • “Worked once, now failing”
    Your temporary token likely expired; use a permanent access token for stability. [docs.watso.app]
  • Message blocked outside 24 hours
    WhatsApp allows free‑form messages only within a 24‑hour session after the user’s last reply; outside of that window, send approved templates. [chatarmin.com]
  • Do I need a public server for a webhook?
    No—Apps Script web apps are public HTTPS endpoints, perfect for webhook development and writing to Sheets. [coefficient.io]

Scaling to production (when you’re ready)

  • Complete Business Verification, add a real number, and generate permanent tokens for your backend. [devopsschool.com]
  • Respect quality ratings, messaging limits, and template rules as you scale. [chatarmin.com]
  • If you just want a quick sandbox beyond Meta’s test number, some providers offer limited free sandboxes; use these only for experiments, not production. [docs.360dialog.com]

FAQ

Can I log inbound WhatsApp messages into Sheets for free?
Yes. By pointing your Cloud API webhook to your Apps Script web app, doPost can write inbound messages and status events to your sheet in real time. [coefficient.io], [support.bolddesk.com]

Can I build this with templates for broadcasts?
Yes, create and approve templates in your WhatsApp Manager, then call the template message type from Apps Script; this is required outside the 24‑hour window. [labnol.org]

Final Thoughts

With Meta’s WhatsApp Cloud API and Google Apps Script, you can ship a cost‑free prototype that sends messages from Sheets and logs replies/statuses back—no third‑party middleware needed. Use the dev test number + 5 recipients to validate, then move to a verified number and permanent token for production‑grade reliability. [labnol.org], [docs.watso.app]

Want me to?

Want me to tailor the Apps Script to your exact sheet and message format (including template sends, attachments, or error logging)?
Share a sample sheet structure and your use case (alerts, reminders, leads, etc.)—I’ll customize the script and webhook for you.