Artur
Artur
Founder

Google Sheets Threshold Alerts to Slack or Teams with n8n (2026)

October 26, 2026

google-sheetsn8n-slackmicrosoft-teamsthreshold-alertsworkflow-automation

TL;DR: To get a channel alert when a spreadsheet value crosses a threshold, run an n8n workflow on a schedule that reads the sheet, compares each value with its threshold, and posts to Slack or Teams only when the state changes (ok to breached, breached to ok). The comparison is the easy part. What makes it usable is remembering the previous state, a cooldown so a flapping value cannot spam the channel, and a small hysteresis band so "back to normal" means something. The importable workflow below does all three.

How do I alert a channel when a Google Sheets value crosses a threshold?

Use five n8n nodes: a Schedule Trigger, a Google Sheets "Get Row(s)" read, a Code node that detects crossings and stores state, then a Slack node (Message > Send) and/or a Microsoft Teams node (Channel Message > Create). The Code node outputs nothing on most runs, so the channel only hears about real changes.

The sheet holds one row per thing you watch. The thresholds live in the sheet too, so the people who own the numbers can change limits without touching the workflow:

metricvaluethresholddirection
Stock: SKU-10423850below
Ad spend today (EUR)412500above
Open support tickets6140above

value can be a formula pulling from another tab. direction says whether going above or below the threshold is the problem.

Why alert on the change instead of the condition?

Because alerting on the condition floods the channel. A workflow that checks "is spend over 500?" every 15 minutes and posts each time it is true sends 96 messages a day for one bad day. People mute the channel, and then the one alert that matters is missed.

A plain "IF value > threshold, send message" is fine for a daily digest. For a channel alert, track three things per metric:

  • Previous state. Alert on ok to breached. Stay silent while it remains breached.

  • Cooldown. If a metric breaches, recovers, and breaches again within the cooldown window, the second breach is muted (and so is its recovery message).

  • Hysteresis. A metric with an "above 100" threshold only counts as recovered once it falls to 95 or lower (5% band by default). Without this, a value hovering at 99-101 alternates alert and recovery forever.

The workflow, node by node

This is a starting workflow, not a finished product: import it, replace the placeholder spreadsheet URL, Slack channel, Teams IDs and the three credentials, then publish (activate) it. Each node does one job:

  1. Every 15 minutes (Schedule Trigger). Set the interval to match how fast you need to know. A stock level rarely needs faster than 15 minutes; a payment-failure count might.
  2. Read threshold sheet (Google Sheets, Get Row(s)). Reads the whole Thresholds tab. Output Formatting is pinned to unformatted values (also the node's default), so 1,250.00 arrives as the number 1250 and not as text. Each row also carries a row_number, which the alert message includes.
  3. Detect crossings (state + cooldown) (Code). Compares, updates state, and emits one item per state change. Three constants at the top control behaviour: COOLDOWN_MINUTES = 60, HYSTERESIS_PCT = 5, NOTIFY_RECOVERY = true.
  4. Post to Slack channel (Slack, Message > Send). Uses the slackText field the Code node builds.
  5. Post to Teams channel (Microsoft Teams, Channel Message > Create). Ships disabled. Enable it if you use Teams, or delete the Slack node if you only use Teams. It sends HTML (teamsHtml) so the bold and line break render.

The Code node fails loudly on bad data: an empty metric name, a duplicate metric, a non-numeric value or threshold, or a direction other than above/below throws an error naming the sheet row. A typo in the sheet should break the run visibly, not quietly disable an alert. Pair the workflow with an error workflow so those failures reach you, as described in n8n error handling: catch, retry and alert.

How does the workflow remember the previous state?

It stores a small object per metric in n8n's workflow static data, via $getWorkflowStaticData('global') in the Code node. Each entry holds whether the metric is currently breached, when it last alerted, and whether the current breach was muted by the cooldown.

The core of the detection, stripped of validation:

const prev = state.metrics[key] ?? { breached: false, lastAlertAt: null, muted: false };

if (!prev.breached && breached) {
  prev.breached = true;
  prev.muted = prev.lastAlertAt !== null && now - prev.lastAlertAt < COOLDOWN_MINUTES * 60000;
  if (!prev.muted) { event = 'breach'; prev.lastAlertAt = now; }
} else if (prev.breached && recovered) {
  prev.breached = false;
  if (NOTIFY_RECOVERY && !prev.muted) event = 'recovered';
  prev.muted = false;
}

Static data has three documented properties that matter here. It is not available in manual test runs, so the state only builds up once the workflow is published and runs from its trigger; a test execution behaves as if every metric is new. It is saved only when the execution succeeds: if the Slack post fails, the new state is not saved and the next run alerts again, which is the behaviour you want. And n8n says it "may behave unreliably under high-frequency workflow executions" and that the data should stay small. A 15-minute schedule storing one small object per metric is a low-frequency, small-data use. The workflow also deletes state for metrics removed from the sheet.

If you need the state visible, or you run many workflows writing to it, n8n's docs point to Data tables (Get row / Upsert row) as the alternative. Swap the static data object for those two nodes and keep the same logic.

Should I use the Google Sheets Trigger instead of a schedule?

Use the schedule. The Google Sheets Trigger node fires on Row added, Row updated, or Row added or updated, which is useful for "a new row arrived" alerts. For thresholds, the schedule is the better fit: the cadence is explicit, every run sees every metric, and values that change because a formula recalculated are evaluated the same way as typed ones.

If you do swap in the trigger (for example, when people type values in by hand and you want a faster reaction), keep the Code node but delete the final loop that forgets missing metrics. The trigger only passes changed rows, so that loop would wipe the state of every row that did not change.

Will this hit Google Sheets API limits?

No. Each run is one read of the tab. Google's published Sheets API quota is 300 read requests per minute per project and 60 per minute per user per project; requests over the limit get HTTP 429 and the quota refills every minute. A 15-minute schedule is 96 reads a day.

Where limits do start to matter is when the same Google credential is shared across many workflows that poll sheets every minute. Those all count against the same 60-per-minute user quota. Stagger the schedules, or read several tabs in one workflow instead of one workflow per tab. If the sheet itself is large, read only the columns and rows you need: if you save execution data, the full sheet is stored in every execution, which adds up fast on a short schedule.

Slack and Teams setup details that break the first run

Slack. The Slack node needs a Slack app with the chat:write scope. The bot has to be a member of the target channel, so invite it (/invite @your-app) or add chat:write.public for public channels. After changing scopes, reinstall the app to the workspace. Full credential walkthrough: n8n Slack integration: notifications and bot workflow. The workflow turns off "Include Link to Workflow" so the alert is just the alert.

Microsoft Teams. Use the Teams OAuth2 credential. Channel Message > Create is not available with the Service Principal (app-only) credential, so connect it with an account that is a member of the channel. On n8n Cloud, a Microsoft Entra administrator has to approve the Teams OAuth2 app before non-admins can connect it. Get the team ID and the channel ID (it starts with 19: and ends with @thread.tacv2) from the channel's "Get link to channel" URL.

Route by severity if you need to: add a channel column to the sheet and map it into the Slack node's channel field instead of the fixed #ops-alerts.

When not to use n8n for this

Skip n8n when an email is enough and your account supports Google Sheets conditional notifications (Tools > Conditional notifications). It emails chosen recipients when a column changes and matches a condition. Its limits: it is only available on certain Google Workspace work or school editions, it does not support group addresses or non-Google recipients, and it sends email, not channel messages.

Skip it too if the numbers already live in a Looker Studio Pro report: Pro supports threshold alerts on charts, sent by email. And if the spreadsheet is only a copy of data that lives in a database or an app, alert from the source instead. A sheet that refreshes once an hour adds an hour of delay to every alert.

Use n8n when the alert has to land in a Slack or Teams channel, when thresholds are managed by non-technical people in the sheet, or when an alert should also do something: open a ticket, create a reorder draft, or page someone outside working hours. For more Sheets-driven patterns, see n8n Google Sheets automation templates.

FAQ

Can n8n send a Slack alert when a Google Sheets cell changes?

Yes. Use the Google Sheets Trigger (Row updated) for "any change" alerts, or a Schedule Trigger plus a Google Sheets read for threshold alerts. Then send with the Slack node's Message > Send operation. For thresholds, add state tracking so you alert on the crossing, not on every run.

How do I stop duplicate alerts for the same breach?

Store the previous state per row and only post when it changes from ok to breached. In n8n, workflow static data or a Data table holds that state between executions. Add a cooldown so a value that crosses back and forth inside an hour produces one alert, not ten.

Why does my threshold alert work in a test run but not remember anything?

Workflow static data is not available in manual test executions. The state only persists when the workflow is published (active) and runs from its trigger, and only after executions that succeed. Use Data tables if you need state that also works in tests.

Can the alert go to Microsoft Teams instead of Slack?

Yes. Use the Microsoft Teams node with Channel Message > Create and the Teams OAuth2 credential. That operation is not available with an app-only Service Principal credential.

How often should the workflow check the sheet?

Match it to how fast someone can act on the alert. Every 15 minutes is a sensible default; it is one Sheets API read per run, far below Google's 60 reads per minute per user quota.

Can Google Sheets send threshold alerts without any automation tool?

Partly. Conditional notifications send emails when a column matches a condition, on supported work or school accounts. They cannot post to Slack or Teams, and they do not handle cooldowns or recovery messages.

Need the alert to do more than notify?

The workflow above covers the common case: a sheet of numbers, a channel, alerts that stay rare enough to be read. If your thresholds need to trigger a reorder, a ticket, or an escalation path, or the data should come from the source system instead of a sheet, get in touch and we will scope the build.


Google Sheets Threshold Alerts to Slack or Teams with n8n (2026) | n8nlogic