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
- Go to api.slack.com/apps and click Create New App
- Choose From scratch
- Name it something like "Google Sheets Notifier" and select your workspace
- In the left sidebar, click Incoming Webhooks
- Toggle Activate Incoming Webhooks to On
- Click Add New Webhook to Workspace
- Select the channel where notifications should appear
- Click Allow
You'll get a webhook URL that looks like:
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXXCopy 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:
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:
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:
- Click the clock icon (Triggers) in the left sidebar
- Click + Add Trigger
- Set function to
checkForOverdueTasks, event source to Time-driven, type to Day timer, time to 7am–8am - 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):
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):
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
- How to Automatically Send Emails from Google Sheets — email version of the same pattern
- How to Create a Project Tracker in Google Sheets — add Slack alerts to the project tracker
- Apps Script for Beginners — if this is your first Apps Script, start here for the basics
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