AutomationFlowsSlack & Telegram › Send Tiered Contract Renewal Slack Alerts From Google Sheets

Send Tiered Contract Renewal Slack Alerts From Google Sheets

ByFelix @easybits on n8n.io

Runs every morning at 9am. Reads every contract from the 5 tabs in your Contracts sheet, checks how many days are left until each , and pings Slack with tiered alerts so no renewal slips through.

Cron / scheduled trigger★★★★☆ complexity21 nodesSlackGoogle Sheets
Slack & Telegram Trigger: Cron / scheduled Nodes: 21 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #15231 — we link there as the canonical source.

This workflow follows the Google Sheets → Slack recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Contract Renewal Watchdog Daily Slack Alerts",
  "tags": [],
  "nodes": [
    {
      "id": "bbbe82aa-9e9b-4ff8-a21c-119132a123df",
      "name": "Needs Alert Today?",
      "type": "n8n-nodes-base.if",
      "position": [
        880,
        624
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d325af5d-5241-4baf-8b02-406b725cfe11",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json['Cancellation Deadline'] }}",
              "rightValue": ""
            },
            {
              "id": "5224a5b3-393c-4a93-9927-daa56591d3d6",
              "operator": {
                "type": "number",
                "operation": "lte"
              },
              "leftValue": "={{ $json.days_left }}",
              "rightValue": 30
            },
            {
              "id": "a6eaad68-55b9-4807-bbc6-66335b737d09",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.days_left }}",
              "rightValue": 0
            },
            {
              "id": "26b6a84b-dcaf-43f4-a0df-7a70abd65baa",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ ({red: 3, yellow: 2, green: 1, none: 0}[$json.tier] || 0) > ({red: 3, yellow: 2, green: 1, '': 0}[($json['Last Alert Tier'] || '').toLowerCase()] || 0) }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ec84588c-2aa7-4740-a11d-9dcc27f8d1f1",
      "name": "Route by Tier",
      "type": "n8n-nodes-base.switch",
      "position": [
        1168,
        592
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Red",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "1a6d0b2f-a334-4f5f-95e3-f8f55792da94",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.tier }}",
                    "rightValue": "red"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Yellow",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "73e1171c-33bc-4b62-b954-abba75505bdf",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.tier }}",
                    "rightValue": "yellow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Green",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "eb626e3e-d730-4d91-8c89-ab2f0cb11774",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.tier }}",
                    "rightValue": "green"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "536797a1-a49a-4ce6-ac5e-9cc4af7e5f6e",
      "name": "Slack: Urgent (Red)",
      "type": "n8n-nodes-base.slack",
      "position": [
        1440,
        432
      ],
      "parameters": {
        "text": "=\ud83d\udd34 *URGENT: {{ $json.vendor_label }} cancellation deadline in {{ $json.days_left }} day{{ $json.days_left === 1 ? '' : 's' }}*\n\n- *Contract type:* {{ $json['Contract Class'] }}\n- *Cancellation deadline:* {{ $json['Cancellation Deadline'] }}\n- *End date:* {{ $json['End Date'] }}\n- *Value:* {{ $json['Contract Value'] }}\n- *Notice period:* {{ $json['Notice Period Days'] }} days\n{{ $json.auto_renew_bool ? '\\n\u26a0\ufe0f *This contract will AUTO-RENEW* unless notice of cancellation is given before the deadline.' : '\\n\ud83d\udcc5 This contract will expire at the end of its term (no auto-renewal).' }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SLACK_CHANNEL_ID"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "0c75e393-c495-40c7-b765-5b81c1cc390d",
      "name": "Slack: Action (Yellow)",
      "type": "n8n-nodes-base.slack",
      "position": [
        1440,
        608
      ],
      "parameters": {
        "text": "=\ud83d\udfe1 *Action needed: {{ $json.vendor_label }} cancellation deadline in {{ $json.days_left }} day{{ $json.days_left === 1 ? '' : 's' }}*",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SLACK_CHANNEL_ID"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "f2b74ea1-8bd9-49e8-a6d8-a5905a06b872",
      "name": "Slack: Heads-up (Green)",
      "type": "n8n-nodes-base.slack",
      "position": [
        1440,
        784
      ],
      "parameters": {
        "text": "=\ud83d\udfe2 *Heads-up: {{ $json.vendor_label }} cancellation deadline in {{ $json.days_left }} day{{ $json.days_left === 1 ? '' : 's' }}*",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SLACK_CHANNEL_ID"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "09be045a-1ec3-411b-947d-561bb459ea0a",
      "name": "Trigger: Daily at 9am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -256,
        624
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "b3a8b02e-6c40-4c68-ad09-f50f033c0a0c",
      "name": "Read: Services Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        16,
        624
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Services"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "e44852b3-c060-436c-8f8c-a6efe50f6c56",
      "name": "Read: Leases Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        16,
        448
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leases"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "c5dbdf98-9ff8-43cf-a7b2-7cc30e7c74eb",
      "name": "Read: SaaS Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        16,
        272
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "SaaS"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "dc6ba403-ffdb-46cf-8e17-359888dc9b0f",
      "name": "Read: Insurance Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        16,
        800
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Insurance"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "b9e469c1-e150-4a45-a3d4-188147301f49",
      "name": "Read: Other Tab",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        16,
        976
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Other"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "cef80888-a96c-403b-9bcb-47163e1f5345",
      "name": "Merge: All Contracts",
      "type": "n8n-nodes-base.merge",
      "position": [
        288,
        576
      ],
      "parameters": {
        "numberInputs": 5
      },
      "typeVersion": 3.2
    },
    {
      "id": "a5f75835-aede-4788-85ff-ac4189a65a0e",
      "name": "Calculate Tier & Days Left",
      "type": "n8n-nodes-base.set",
      "position": [
        576,
        624
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "76ce752f-acde-48ac-a083-d65487eb6c2b",
              "name": "days_left",
              "type": "number",
              "value": "={{ Math.round((DateTime.fromISO($json['Cancellation Deadline']).startOf('day').toMillis() - DateTime.now().startOf('day').toMillis()) / 86400000) }}"
            },
            {
              "id": "7bce067b-37b0-4868-8326-7f0749754277",
              "name": "tier",
              "type": "string",
              "value": "={{ (() => { const d = Math.round((DateTime.fromISO($json['Cancellation Deadline']).startOf('day').toMillis() - DateTime.now().startOf('day').toMillis()) / 86400000); return d <= 7 ? 'red' : d <= 14 ? 'yellow' : d <= 30 ? 'green' : 'none'; })() }}"
            },
            {
              "id": "d2e49f8d-987a-4e36-b821-dffbb99915fb",
              "name": "vendor_label",
              "type": "string",
              "value": "={{ $json['Provider Name'] || $json['Client Name'] || 'Unknown contract' }}"
            },
            {
              "id": "1729e5d4-aa22-42cc-88ad-94095a162f72",
              "name": "auto_renew_bool",
              "type": "boolean",
              "value": "={{ String($json['Auto Renew']).toLowerCase() === 'true' }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "80e01f6e-0927-4bb2-b202-6bcdc8cf2d5b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -336,
        480
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 352,
        "content": "### \u23f0 Daily Trigger\nFires every morning at 9am. Change the hour in the node if your team prefers a different check-in time."
      },
      "typeVersion": 1
    },
    {
      "id": "a247f9bd-3cb4-4b9d-acd1-0cdaa31c9100",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 1040,
        "content": "### \ud83d\udce5 Read All 5 Tabs\nOne Google Sheets read per contract class. Each returns every row from its tab. They converge in the merge node next."
      },
      "typeVersion": 1
    },
    {
      "id": "c198349a-55ce-4036-a516-ab633b9fd5e5",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 400,
        "content": "### \ud83d\udd17 Merge: All Contracts\nAppend mode combines rows from all 5 tabs into one stream so the filtering logic lives in a single place downstream."
      },
      "typeVersion": 1
    },
    {
      "id": "40da47f5-4b6d-41ec-b720-088cf2656e9b",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        336
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 496,
        "content": "### \ud83e\uddee Calculate Tier & Days Left\nFor every contract row, derives:\n- `days_left` \u2013 days between today and `Cancellation Deadline`\n- `tier` \u2013 \ud83d\udd34 red (\u22647), \ud83d\udfe1 yellow (\u226414), \ud83d\udfe2 green (\u226430), `none` otherwise\n- `vendor_label` \u2013 Provider or Client name for the Slack message\n- `auto_renew_bool` \u2013 normalizes the string `\"true\"/\"false\"` from the sheet into a real boolean"
      },
      "typeVersion": 1
    },
    {
      "id": "ff1b0154-ed64-4267-8b10-b9107aeaeab4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        784,
        336
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 496,
        "content": "### \u2753 Needs Alert Today?\nPasses a contract through only if:\n- `Cancellation Deadline` exists\n- `days_left` is between 0 and 30\n- the new tier is more urgent than the `Last Alert Tier` already stored on the row\n\nThis is the escalation gate \u2013 contracts that already got their \ud83d\udfe2 ping won't ping again until they cross into \ud83d\udfe1."
      },
      "typeVersion": 1
    },
    {
      "id": "fe072824-4672-4894-9c70-5a8708f2b340",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1088,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 400,
        "content": "### \ud83d\udd00 Route by Tier\nExpression-based Switch on `tier`. Each output feeds a tier-specific Slack message so the urgency of the ping matches the urgency of the deadline."
      },
      "typeVersion": 1
    },
    {
      "id": "f812f850-0459-41df-b611-8cf44882c087",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 752,
        "content": "### \ud83d\udcac Slack Alerts\nOne message per contract that crossed a new tier today. Auto-renewing contracts get a stronger warning (\"this WILL auto-renew unless cancelled\") than contracts that just expire \u2013 that's the distinction that actually matters financially."
      },
      "typeVersion": 1
    },
    {
      "id": "5efea265-9448-44e9-a83c-7aac0b55f54f",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1072,
        16
      ],
      "parameters": {
        "width": 720,
        "height": 1264,
        "content": "## \ud83d\udd14 Contract Renewal Watchdog \u2013 Daily Slack Alerts\n\n**What this workflow does**\nRuns every morning at 9am. Reads every contract from the 5 tabs in your Contracts sheet, checks how many days are left until each `Cancellation Deadline`, and pings Slack with tiered alerts so no renewal slips through.\n\n### Alert tiers\n\n**\ud83d\udd34 Urgent** \u2013 \u2264 7 days until deadline\nAct now\n\n**\ud83d\udfe1 Action needed** \u2013 \u2264 14 days until deadline\nPlan this week\n\n**\ud83d\udfe2 Heads-up** \u2013 \u2264 30 days until deadline\nOn your radar\n\n**No alert** \u2013 more than 30 days out, or already past due\n\nAuto-renewing contracts get a stronger warning than contracts that simply expire \u2013 because that's the case where money is actually at stake.\n\n### Setup guide\n\n**1. Add two columns to every tab in your Contracts sheet**\nOpen your sheet and add these two columns to all 5 tabs (SaaS, Leases, Services, Insurance, Other):\n- `Last Alert Tier`\n- `Last Alert Date`\n\nLeave both empty for existing rows. These drive the de-duplication \u2013 without them you'll get the same ping every day for weeks.\n\n**2. Paste your Sheet ID**\nReplace `YOUR_GOOGLE_SHEET_ID` in all 5 Read nodes. It's the same Sheet you used in Part 1.\n\n**3. Set your Slack channel**\nReplace `YOUR_SLACK_CHANNEL_ID` in all 3 Slack nodes with the channel (or DM) where alerts should land. `#contracts` or `#finance-alerts` work well; DM to the contract owner is fine too.\n\n**4. Connect credentials**\n- Google Sheets \u2192 OAuth\n- Slack \u2192 OAuth\n\n**5. Activate and test**\nBefore turning it on, temporarily set one row's `Cancellation Deadline` to ~10 days from today and clear its `Last Alert Tier`. Click **Execute Workflow** and confirm a \ud83d\udfe1 Slack message lands. Revert the row, then activate.\n\n### Known limitations (v1)\n\n**No automatic writeback.** Today the workflow alerts but doesn't mark the row as \"alerted\". To stop daily re-pings, either:\n- Fill `Last Alert Tier` manually when the alert fires (\ud83d\udfe2 / \ud83d\udfe1 / \ud83d\udd34), or\n- Add a Google Sheets Update node per tab (Switch by `Contract Class`, match on Start Date + Provider Name as a composite key)\n\n**Works on ISO dates only.** The `days_left` calculation assumes `Cancellation Deadline` is stored as `YYYY-MM-DD`. Part 1 produces this format correctly, so this is only a concern if you're adding rows manually."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "connections": {
    "Route by Tier": {
      "main": [
        [
          {
            "node": "Slack: Urgent (Red)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Action (Yellow)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Heads-up (Green)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read: SaaS Tab": {
      "main": [
        [
          {
            "node": "Merge: All Contracts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read: Other Tab": {
      "main": [
        [
          {
            "node": "Merge: All Contracts",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "Read: Leases Tab": {
      "main": [
        [
          {
            "node": "Merge: All Contracts",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Needs Alert Today?": {
      "main": [
        [
          {
            "node": "Route by Tier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read: Services Tab": {
      "main": [
        [
          {
            "node": "Merge: All Contracts",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Read: Insurance Tab": {
      "main": [
        [
          {
            "node": "Merge: All Contracts",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Merge: All Contracts": {
      "main": [
        [
          {
            "node": "Calculate Tier & Days Left",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger: Daily at 9am": {
      "main": [
        [
          {
            "node": "Read: SaaS Tab",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read: Leases Tab",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read: Services Tab",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read: Insurance Tab",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read: Other Tab",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Tier & Days Left": {
      "main": [
        [
          {
            "node": "Needs Alert Today?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Runs every morning at 9am. Reads every contract from the 5 tabs in your Contracts sheet, checks how many days are left until each , and pings Slack with tiered alerts so no renewal slips through.

Source: https://n8n.io/workflows/15231/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

This workflow continuously monitors the Meta Ads Library for new creatives from a specific competitor pages, logs them into Google Sheets, and sends a concise Telegram notification with the number of

HTTP Request, Telegram, Google Sheets +1
Slack & Telegram

Enhance financial oversight with this automated n8n workflow. Triggered every 5 minutes, it fetches real-time bank transactions via an API, enriches and transforms the data, and applies smart logic to

HTTP Request, Email Send, Google Sheets +1
Slack & Telegram

This workflow automates competitive price intelligence using Bright Data's enterprise web scraping API. On a scheduled basis (default: daily at 9 AM), the system loops through configured competitor pr

HTTP Request, Google Sheets, Slack +1
Slack & Telegram

Ensure your customer SLAs never slip with this n8n automation template. The workflow runs on a schedule, fetching open tickets from Zendesk, calculating SLA time remaining, and sending proactive alert

Zendesk, Slack, Google Sheets
Slack & Telegram

&gt; n8n, Binance API, Google Sheets, Slack, Telegram, Jira & Email

HTTP Request, Google Sheets, Slack +3