This workflow corresponds to n8n.io template #17508 — we link there as the canonical source.
This workflow follows the Google Sheets → Telegram 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 →
{
"id": "OLdnJpiLK1YjAgSo",
"name": "Production order tracker: Google Sheets to shop floor chat alerts",
"tags": [
{
"id": "02ThXpPxieX6RXPt",
"name": "Google Sheets",
"createdAt": "2026-07-25T22:20:02.957Z",
"updatedAt": "2026-07-25T22:20:02.957Z"
},
{
"id": "6tLEpuPTZKvuhUfj",
"name": "Manufacturing",
"createdAt": "2026-07-25T22:20:02.983Z",
"updatedAt": "2026-07-25T22:20:02.983Z"
},
{
"id": "kUT2LehedzPpj79Q",
"name": "Operations",
"createdAt": "2026-07-25T22:20:02.924Z",
"updatedAt": "2026-07-25T22:20:02.924Z"
}
],
"nodes": [
{
"id": "1d8f301e-3259-4a5c-acaa-8d89df3a5e42",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
816,
192
],
"parameters": {
"width": 480,
"height": 816,
"content": "## Production order tracker: Google Sheets to shop floor chat alerts\n\n### How it works\n\nThis workflow monitors a Google Sheets production order tracker and alerts the shop floor chat about newly detected orders. It can be run manually or on a 15-minute schedule, reads the configured order columns, filters for unannounced orders, sends Telegram notifications, and then updates the sheet to prevent duplicate alerts.\n\n### Setup steps\n\n- Connect Google Sheets credentials and configure the production spreadsheet and sheet range in both Google Sheets nodes.\n- Connect Telegram bot credentials and set the target shop floor chat ID in the Telegram node.\n- Confirm the Settings node column names match the spreadsheet headers, including order ID, product, quantity, and due date.\n- Ensure the sheet includes a status or announced marker column that the filtering code and update node can use consistently.\n\n### Customization\n\nAdjust the schedule interval, Telegram message format, column mappings, or filtering logic to match different production tracking rules."
},
"typeVersion": 1
},
{
"id": "f4a6f0b5-7764-4d2a-bc33-2dafcbb322f7",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1376,
192
],
"parameters": {
"color": 7,
"width": 416,
"height": 496,
"content": "## Start and configure\n\nManual and scheduled triggers start the workflow, then the settings node defines the Google Sheets column names used to identify order details."
},
"typeVersion": 1
},
{
"id": "c50056ba-1dc9-464d-a337-2c2fa578219b",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1824,
288
],
"parameters": {
"color": 7,
"width": 432,
"height": 320,
"content": "## Read and filter orders\n\nThe workflow reads the production sheet and runs code to identify production orders that have not yet been announced to the shop floor."
},
"typeVersion": 1
},
{
"id": "543cb9ab-0682-4fdd-9b05-83457be269f9",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2320,
288
],
"parameters": {
"color": 7,
"width": 448,
"height": 320,
"content": "## Notify and mark orders\n\nNew orders are sent to the shop floor chat through Telegram, then the corresponding sheet rows are updated to mark them as announced."
},
"typeVersion": 1
},
{
"id": "f1cc8530-9c18-419b-a6e4-8680d3558a15",
"name": "Scheduled 15 Min Interval",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
1440,
528
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 15
}
]
}
},
"typeVersion": 1.2
},
{
"id": "a8f4a98a-b679-43f6-a872-2d3b2b90e073",
"name": "Set Order Fields",
"type": "n8n-nodes-base.set",
"position": [
1648,
448
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0",
"name": "colOrderId",
"type": "string",
"value": "Order"
},
{
"id": "1",
"name": "colProduct",
"type": "string",
"value": "Product"
},
{
"id": "2",
"name": "colQuantity",
"type": "string",
"value": "Quantity"
},
{
"id": "3",
"name": "colDueDate",
"type": "string",
"value": "Due date"
},
{
"id": "4",
"name": "colStation",
"type": "string",
"value": "Station"
},
{
"id": "5",
"name": "colPriority",
"type": "string",
"value": "Priority"
},
{
"id": "6",
"name": "colStatus",
"type": "string",
"value": "Announced"
},
{
"id": "7",
"name": "announcedValue",
"type": "string",
"value": "yes"
},
{
"id": "8",
"name": "urgentDays",
"type": "string",
"value": "2"
},
{
"id": "9",
"name": "maxPerRun",
"type": "string",
"value": "20"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "cc3f3681-55cc-45e8-8075-95d9f66bb828",
"name": "Read Production Sheet",
"type": "n8n-nodes-base.googleSheets",
"notes": "Select your production spreadsheet and worksheet.",
"position": [
1872,
448
],
"parameters": {
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
},
{
"id": "4f671df5-9595-4cd9-a404-c84e2d8f7241",
"name": "Identify New Orders",
"type": "n8n-nodes-base.code",
"position": [
2112,
448
],
"parameters": {
"jsCode": "// Pick up production orders that have not been announced to the shop floor yet,\n// and build one short message per order. Nothing here is company specific \u2014\n// change the column names in Settings, not this code.\nconst cfg = $('Set Order Fields').first().json;\n\nconst COL = {\n id: cfg.colOrderId || 'Order',\n product: cfg.colProduct || 'Product',\n qty: cfg.colQuantity || 'Quantity',\n due: cfg.colDueDate || 'Due date',\n station: cfg.colStation || 'Station',\n status: cfg.colStatus || 'Announced',\n priority: cfg.colPriority || 'Priority',\n};\nconst DONE = (cfg.announcedValue || 'yes').toLowerCase();\nconst MAX = Number(cfg.maxPerRun || 20);\n\nconst rows = $input.all().map(i => i.json);\nconst pending = rows.filter(r => {\n const flag = String(r[COL.status] ?? '').trim().toLowerCase();\n const hasId = String(r[COL.id] ?? '').trim() !== '';\n return hasId && flag !== DONE;\n});\n\nif (!pending.length) return [];\n\nfunction daysLeft(v) {\n if (!v) return null;\n const d = new Date(v);\n if (isNaN(d)) return null;\n return Math.ceil((d - new Date()) / 86400000);\n}\n\nconst out = [];\nfor (const r of pending.slice(0, MAX)) {\n const left = daysLeft(r[COL.due]);\n const urgent = left !== null && left <= Number(cfg.urgentDays || 2);\n\n const lines = [];\n lines.push((urgent ? '\ud83d\udd34 ' : '\ud83d\udd35 ') + 'ORDER ' + String(r[COL.id]));\n if (r[COL.product]) lines.push('Product: ' + r[COL.product]);\n if (r[COL.qty]) lines.push('Quantity: ' + r[COL.qty]);\n if (r[COL.station]) lines.push('Station: ' + r[COL.station]);\n if (r[COL.due]) lines.push('Due: ' + r[COL.due] + (left !== null ? ' (' + left + ' days)' : ''));\n if (r[COL.priority]) lines.push('Priority: ' + r[COL.priority]);\n\n out.push({ json: {\n order_id: String(r[COL.id]),\n row_number: r.row_number ?? null,\n urgent,\n days_left: left,\n text: lines.join('\\n'),\n }});\n}\n\n// Newest deadline first \u2014 the shop floor should see what burns, not what is alphabetically first.\nout.sort((a, b) => (a.json.days_left ?? 9999) - (b.json.days_left ?? 9999));\nreturn out;"
},
"typeVersion": 2
},
{
"id": "0fbc2b16-993b-41f0-9220-f5a1e09d49d3",
"name": "Alert Shop Floor via Telegram",
"type": "n8n-nodes-base.telegram",
"notes": "Swap this node for Slack, e-mail or any chat you already use \u2014 the message is plain text.",
"onError": "continueRegularOutput",
"position": [
2368,
448
],
"parameters": {
"text": "={{ $json.text }}",
"additionalFields": {
"disable_web_page_preview": true
}
},
"typeVersion": 1.2
},
{
"id": "da10a508-4099-4825-8a2f-b4cacecc09fe",
"name": "Update Announcement Status in Sheets",
"type": "n8n-nodes-base.googleSheets",
"notes": "Match on the order column and write the announcedValue into the Announced column.",
"onError": "continueRegularOutput",
"position": [
2640,
448
],
"parameters": {
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "2826f3c5-c0cc-4e7f-91ed-b6341c13c743",
"nodeGroups": [],
"connections": {
"Set Order Fields": {
"main": [
[
{
"node": "Read Production Sheet",
"type": "main",
"index": 0
}
]
]
},
"Identify New Orders": {
"main": [
[
{
"node": "Alert Shop Floor via Telegram",
"type": "main",
"index": 0
}
]
]
},
"Read Production Sheet": {
"main": [
[
{
"node": "Identify New Orders",
"type": "main",
"index": 0
}
]
]
},
"Scheduled 15 Min Interval": {
"main": [
[
{
"node": "Set Order Fields",
"type": "main",
"index": 0
}
]
]
},
"Alert Shop Floor via Telegram": {
"main": [
[
{
"node": "Update Announcement Status in Sheets",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs every 15 minutes to read a Google Sheets production order tracker, posts each unannounced order as a plain-text alert to a Telegram shop-floor chat, and then updates the sheet to mark those orders as announced so they are not sent twice. Runs on a 15-minute…
Source: https://n8n.io/workflows/17508/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
⚠️ Heads up: this is satire. The "Hell Yeah!" workflow is a parody of "automate your whole life with AI agents" grindset content. The API endpoints are fictional and the function nodes are illustrativ
This workflow tracks a configurable crypto watchlist using the CoinGecko API, sends Telegram alerts when price, % change, or volume-spike conditions are met (with optional RSI filtering), optionally l
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
> n8n, Binance API, Google Sheets, Slack, Telegram, Jira & Email
This workflow is ideal for marketers, product managers, competitive intelligence teams, and anyone who needs to track changes on web pages — whether it's competitor pricing, job postings, policy updat