appscript.dev
Automation Beginner Sheets

Post Sheets alerts to a Slack channel

Bridge Northwind spreadsheet events to Slack — every threshold breach lands in #alerts.

Published Jul 11, 2025

Northwind tracks its sales pipeline in a spreadsheet, but a spreadsheet is passive — a deal can close, or a number can cross a danger line, and nobody notices until someone happens to open the tab. The team lives in Slack, not in Sheets, so the moments that matter need to come to them.

This automation bridges the two. A small slackPost helper sends a line of text to a Slack channel, and an onEdit trigger watches the Pipeline sheet so that the instant a deal is marked won, a celebratory note lands in #alerts. It is a beginner-friendly pattern you can point at any cell change worth shouting about.

What you’ll need

  • A Google Sheet with a tab named Pipeline, where a status column gets edited to values like won.
  • A Slack app with an Incoming Webhook for the channel you want alerts in.
  • The webhook URL saved as SLACK_WEBHOOK in Script Properties — see Store API keys and secrets securely.

The script

// The sheet tab whose edits we watch.
const WATCHED_SHEET = 'Pipeline';

// The status value that should trigger an alert.
const WON_STATUS = 'won';

/**
 * Sends a line of text to Slack via an Incoming Webhook.
 *
 * @param {string} text  The message to post.
 */
function slackPost(text) {
  const webhook = PropertiesService.getScriptProperties().getProperty('SLACK_WEBHOOK');

  // Bail out clearly if the webhook was never configured.
  if (!webhook) {
    throw new Error('SLACK_WEBHOOK is not set in Script Properties.');
  }

  UrlFetchApp.fetch(webhook, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify({ text: text }),
    muteHttpExceptions: true,
  });
}

/**
 * Simple installable/trigger handler. Fires on every edit to the
 * spreadsheet; posts to Slack only when a Pipeline row is marked "won".
 *
 * @param {Object} e  The onEdit event object.
 */
function onEdit(e) {
  // 1. Ignore edits with no event object, or on any sheet but Pipeline.
  if (!e || !e.range) return;
  const sheet = e.range.getSheet();
  if (sheet.getName() !== WATCHED_SHEET) return;

  // 2. Only react when the edited cell now reads "won".
  if (e.value !== WON_STATUS) return;

  // 3. Read the deal name from column A of the edited row.
  const dealName = sheet.getRange(e.range.getRow(), 1).getValue();

  // 4. Announce it in Slack.
  slackPost(':tada: ' + dealName + ' just closed.');
}

How it works

  1. slackPost reads the Incoming Webhook URL from Script Properties, so the URL — which anyone can post to — never sits in the code. If it is missing, the function throws a clear error rather than failing silently.
  2. It POSTs a small JSON body, { text }, to the webhook. Slack renders that as a message in the webhook’s channel. That is the whole helper — it is deliberately tiny so any automation can reuse it.
  3. onEdit runs on every cell change in the spreadsheet. It first checks the event object exists and that the edit happened on the Pipeline tab, returning early otherwise — most edits are not interesting.
  4. It then checks e.value, the cell’s new contents. Only when that equals won does it continue, so editing any other cell costs nothing.
  5. For a real win, it reads column A of the edited row to get the deal name.
  6. It calls slackPost with a :tada: message, and the alert appears in Slack the moment the cell is updated.

Example run

Someone changes the status cell of a Pipeline row:

Column A (deal)Status column
Acme Rebrandwon

The onEdit trigger fires, sees the new value is won, reads Acme Rebrand from column A, and posts to #alerts:

:tada: Acme Rebrand just closed.

Editing any other cell — a note, a date, a status of lost — produces nothing, because onEdit returns early before reaching slackPost.

Trigger it

onEdit is a special function name, but to call UrlFetchApp it must run as an installable trigger, not the limited simple trigger:

  1. In the Apps Script editor, open Triggers (the clock icon).
  2. Click Add Trigger.
  3. Choose function onEdit, event source From spreadsheet, event type On edit.
  4. Save and approve the authorisation prompt.

The simple version of onEdit runs without permission but cannot make external requests, so the installable trigger is what makes the Slack call work.

Watch out for

  • A simple onEdit cannot call UrlFetchApp. If alerts never arrive, you are almost certainly relying on the simple trigger — add the installable one as above.
  • e.value is only set for single-cell edits. Pasting a block, or using fill-down, leaves e.value undefined, so those edits will not raise an alert. Read e.range directly if you need to cover bulk edits.
  • The comparison is exact and case-sensitive. Won or won with a trailing space will not match WON_STATUS — use a dropdown (data validation) on the status column to keep the values clean.
  • onEdit fires on every edit in the whole spreadsheet. The early returns keep it cheap, but avoid adding slow work before those checks.
  • The webhook posts to one fixed channel, the one it was created for. To alert a different channel, create a separate webhook and store it under its own property.
  • Triggers run silently. If a slackPost fails, no one sees it — check Executions in the editor occasionally, or wrap the call in a try/catch that logs failures.

Related