appscript.dev
Automation Beginner Sheets

Bridge Sheets to Zapier or Make

Trigger external automations from Northwind Sheets via webhooks — no Apps Script logic needed downstream.

Published Nov 8, 2025

Northwind already runs plenty of automations in Zapier and Make — sending emails, updating a CRM, posting to Slack. The missing piece is a clean way to kick those off from a spreadsheet without rebuilding all that logic in Apps Script. A webhook is the bridge.

This script turns a Google Sheet into a trigger source. When someone edits a row on a Triggers tab, it packages that row as a JSON object — keyed by the column headers — and POSTs it to a Zapier or Make webhook. Apps Script does the catching; Zapier or Make does everything after.

What you’ll need

  • A Google Sheet with a tab named exactly Triggers, with column headers in row 1. Each header becomes a field in the webhook payload.
  • A catch webhook in Zapier (“Webhooks by Zapier” trigger) or Make (“Custom webhook” module). Copy its URL.
  • The script bound to that Sheet — write it from Extensions > Apps Script inside the spreadsheet so the onEdit trigger has somewhere to attach.

The script

// The catch-hook URL from Zapier or Make. Paste yours here.
const ZAPIER_HOOK = 'https://hooks.zapier.com/hooks/catch/...';

// The tab whose edits should fire the webhook.
const TRIGGER_SHEET = 'Triggers';

/**
 * Simple onEdit trigger. When a row on the Triggers tab is edited,
 * POSTs that row to the webhook as a JSON object keyed by header.
 *
 * @param {Object} e  The edit event Apps Script passes in.
 */
function onEdit(e) {
  // 1. Ignore manual runs and edits on any other tab.
  if (!e || e.range.getSheet().getName() !== TRIGGER_SHEET) return;

  const sheet = e.range.getSheet();
  const row = e.range.getRow();

  // 2. Ignore edits to the header row itself.
  if (row === 1) return;

  // 3. Read the header row — these become the payload's keys.
  const headers = sheet
    .getRange(1, 1, 1, sheet.getLastColumn())
    .getValues()[0];

  // 4. Read the full edited row, then zip headers and values together.
  const values = sheet
    .getRange(row, 1, 1, headers.length)
    .getValues()[0];
  const payload = Object.fromEntries(headers.map((h, i) => [h, values[i]]));

  // 5. POST the row to the external webhook as JSON.
  UrlFetchApp.fetch(ZAPIER_HOOK, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
  });
}

How it works

  1. onEdit runs automatically on every edit to the spreadsheet. It first checks the event exists and that the edit happened on the Triggers tab — any other tab is ignored.
  2. It ignores edits to row 1, since the header row is not a record to send.
  3. It reads the header row across the used columns. These labels become the keys of the JSON payload, so the downstream automation gets named fields rather than bare cell values.
  4. It reads the full edited row and uses Object.fromEntries to zip the headers and the row’s values into a single object.
  5. It POSTs that object as JSON to the Zapier or Make webhook, which catches it and runs whatever steps you have configured there.

Example run

The Triggers tab has these headers and one freshly edited row:

nameemailplan
Dana Reed[email protected]Pro

Editing any cell in that row sends this payload to the webhook:

{ "name": "Dana Reed", "email": "[email protected]", "plan": "Pro" }

Zapier or Make catches it and takes over — adding the contact to a CRM, sending a welcome email, whatever the Zap or scenario does.

Run it

The onEdit function runs automatically once the script is in place — but a plain onEdit cannot make external requests as a simple trigger. You need an installable trigger:

  1. In the bound script’s editor, open Triggers (the clock icon).
  2. Add a trigger: choose onEdit, event source From spreadsheet, event type On edit.
  3. Approve the authorisation prompt — the installable trigger is what grants UrlFetchApp permission to call out.

After that, every edit to a data row on the Triggers tab fires the webhook.

Watch out for

  • A simple onEdit (no installed trigger) runs without the authorisation needed for UrlFetchApp, so the fetch silently fails. The installable trigger above is required.
  • The function fires on every edit to a row — change three cells and the webhook is hit three times. Add a guard (for example, only fire when a specific “status” column is set) if duplicates would cause problems.
  • A pasted block of rows triggers onEdit once for the whole range, not once per row. e.range.getRow() then points at the first pasted row only.
  • The webhook URL sits in the script. Anyone with edit access to the script can read it, so treat it as a secret and rotate it if the Sheet is widely shared.
  • There is no retry. If Zapier or Make is briefly down, the POST is lost. For important events, log sent rows and reconcile, or queue and retry.
  • Webhook triggers in both Zapier and Make count against your task or operations quota. A busy Triggers tab can burn through a plan quickly.

Related