If your team runs on Slack, email notifications from Google Sheets often get ignored. This tutorial sets up Slack notifications instead — a message to any Slack channel when something in your sheet changes or when a scheduled condition is met.

No add-ons, no paid tools. You need a Slack workspace where you can create an app, and Google Sheets.

---

How It Works

Slack provides Incoming Webhooks — a URL you can send a POST request to and a message will appear in a channel. Apps Script sends that request. The whole setup takes about 10 minutes.

---

Step 1: Create a Slack Webhook

  1. Go to api.slack.com/apps and click Create New App
  2. Choose From scratch
  3. Name it something like "Google Sheets Notifier" and select your workspace
  4. In the left sidebar, click Incoming Webhooks
  5. Toggle Activate Incoming Webhooks to On
  6. Click Add New Webhook to Workspace
  7. Select the channel where notifications should appear
  8. Click Allow

You'll get a webhook URL that looks like:

Code
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

Copy this URL. Keep it private — anyone with this URL can post to your channel.

---

Step 2: Send a Test Message From Apps Script

Open your Google Sheet. Go to Extensions > Apps Script. Delete the placeholder and paste:

Code
function sendSlackMessage(message) {
  var webhookUrl = "YOUR_WEBHOOK_URL_HERE";  // Paste your webhook URL

  var payload = JSON.stringify({ text: message });

  var options = {
method: "post",
contentType: "application/json",
payload: payload,
  };

  UrlFetchApp.fetch(webhookUrl, options);
}

function testSlack() {
  sendSlackMessage("Test from Google Sheets! It's working.");
}

Replace YOUR_WEBHOOK_URL_HERE with the webhook URL you copied.

Click Run with testSlack selected. Authorize when prompted. Check your Slack channel — the test message should appear.

---

Step 3: Trigger on a Condition

Here's a complete script that checks your sheet daily and posts to Slack if any task is overdue:

Code
var WEBHOOK_URL  = "YOUR_WEBHOOK_URL_HERE";
var SHEET_NAME   = "Tasks";
var STATUS_COL   = 5;   // Column E — status
var DUE_DATE_COL = 3;   // Column C — due date
var TASK_COL     = 1;   // Column A — task name
var OWNER_COL    = 2;   // Column B — owner name

function sendSlackMessage(message) {
  UrlFetchApp.fetch(WEBHOOK_URL, {
method: "post",
contentType: "application/json",
payload: JSON.stringify({ text: message }),
  });
}

function checkForOverdueTasks() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
  var data  = sheet.getDataRange().getValues();
  var today = new Date();
  today.setHours(0, 0, 0, 0);

  var overdue = [];

  for (var i = 1; i < data.length; i++) {
var task    = data[i][TASK_COL - 1];
var owner   = data[i][OWNER_COL - 1];
var dueDate = data[i][DUE_DATE_COL - 1];
var status  = data[i][STATUS_COL - 1];

if (!task || status === "Done") continue;
if (!(dueDate instanceof Date)) continue;

dueDate.setHours(0, 0, 0, 0);
if (dueDate < today) {
  overdue.push("• " + task + " — " + owner + " (was due " + Utilities.formatDate(dueDate, Session.getScriptTimeZone(), "MMM d") + ")");
}
  }

  if (overdue.length > 0) {
var msg = ":warning: *" + overdue.length + " overdue task" + (overdue.length > 1 ? "s" : "") + ":*\n" + overdue.join("\n");
msg += "\n\n<" + SpreadsheetApp.getActiveSpreadsheet().getUrl() + "|View tracker>";
sendSlackMessage(msg);
Logger.log("Slack message sent: " + overdue.length + " overdue tasks.");
  } else {
Logger.log("No overdue tasks. No message sent.");
  }
}

Adjust the column numbers at the top to match your sheet.

Slack formatting notes:

  • text = bold
  • _text_ = italic
  • :warning: = emoji
  • = hyperlink

---

Step 4: Schedule It

To run this automatically every morning:

  1. Click the clock icon (Triggers) in the left sidebar
  2. Click + Add Trigger
  3. Set function to checkForOverdueTasks, event source to Time-driven, type to Day timer, time to 7am–8am
  4. Click Save and authorize

Every morning, the script checks the sheet and posts to Slack only if there are overdue tasks. If everything is on track, nothing is posted.

---

Other Trigger Patterns

Notify when a new row is added (uses an onEdit simple trigger):

Code
function onEdit(e) {
  var sheet = e.source.getActiveSheet();
  if (sheet.getName() !== "Orders") return;  // Only watch the Orders tab

  var row = e.range.getRow();
  if (row <= 1) return;  // Skip header row

  // Only fire when column A (first column) is edited
  if (e.range.getColumn() !== 1) return;

  var newValue = e.value;
  if (!newValue) return;

  sendSlackMessage("New order logged: " + newValue + " (row " + row + ")");
}

Notify when a specific cell changes (useful for status changes):

Code
function onEdit(e) {
  var sheet = e.source.getActiveSheet();
  var range = e.range;

  if (sheet.getName() === "Tasks" && range.getColumn() === 5) {  // Column E = Status
var task   = sheet.getRange(range.getRow(), 1).getValue();
var status = e.value;
if (status === "Done") {
  sendSlackMessage(":white_check_mark: Task completed: *" + task + "*");
}
  }
}

---

Common Problems

The webhook URL stopped working

Webhook URLs can be revoked if the Slack app is deleted or the bot is removed from the channel. Go back to api.slack.com/apps, find your app, and generate a new webhook URL.

Messages post but formatting looks wrong

Slack's message formatting uses mrkdwn (Slack's markdown). Standard markdown doesn't work — use bold not bold, and _italic_ not italic.

Apps Script error: "Request failed"

Check that the webhook URL is correctly pasted — no extra spaces, no truncation. Test with the testSlack() function first before adding the full logic.

---

What to Build on Top of This

Don't want to set this up yourself? Describe what should trigger the Slack message and it'll be configured for your sheet. Get it installed