Alert when support tickets wait too long
Every 30 minutes, find open tickets past their response target and post them to your support channel.
How it works
Trigger: Schedule — every 30 minutes
- Read the Tickets sheet (id, subject, status, priority, created at, assignee).
- Compare each open ticket's age with its target: 1h urgent, 4h high, 24h normal.
- Post the late tickets to Slack, oldest first.
Diagram
Trigger App Code
Code
JavaScript used in the Code step — already included in the template below.
// Tickets sheet columns: id | customer | subject | status | priority | created_at (ISO) | assignee
// Finds open tickets older than their response target.
const TARGET_HOURS = { urgent: 1, high: 4, normal: 24, low: 48 };
const HOUR = 60 * 60 * 1000;
const late = $input.all()
.map((item) => item.json)
.filter((t) => ["open", "new"].includes(String(t.status).toLowerCase()))
.map((t) => {
const priority = String(t.priority || "normal").toLowerCase();
const hours = Math.floor((Date.now() - new Date(t.created_at)) / HOUR);
return { ...t, priority, hours, target: TARGET_HOURS[priority] ?? 24 };
})
.filter((t) => t.hours >= t.target)
.sort((a, b) => b.hours - a.hours);
if (!late.length) return [];
const message =
`⏰ ${late.length} ticket${late.length > 1 ? "s" : ""} past the response target:\n` +
late.map((t) => `• #${t.id} ${t.subject} (${t.priority}, ${t.hours}h, ${t.assignee || "unassigned"})`).join("\n");
return [{ json: { message } }];Build it
Pick your tool — same automation, step by step.
Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder.
Show code (140 lines)
{
"name": "Alert when support tickets wait too long (Flowngn)",
"nodes": [
{
"parameters": {
"content": "## Alert when support tickets wait too long\nEvery 30 minutes, find open tickets past their response target and post them to your support channel.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
"height": 260,
"width": 340
},
"id": "45821f1c-7a2a-4a1b-a1b8-08dd9ee50303",
"name": "Setup notes",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-420,
-60
]
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 30
}
]
}
},
"id": "45821f1c-7a2a-4a1b-a1b8-08dd9ee50303",
"name": "Every 30 minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
0
]
},
{
"parameters": {
"operation": "read",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "Tickets",
"mode": "name"
},
"options": {}
},
"id": "45821f1c-7a2a-4a1b-a1b8-08dd9ee50303",
"name": "Read tickets",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
300,
0
]
},
{
"parameters": {
"jsCode": "// Tickets sheet columns: id | customer | subject | status | priority | created_at (ISO) | assignee\n// Finds open tickets older than their response target.\nconst TARGET_HOURS = { urgent: 1, high: 4, normal: 24, low: 48 };\nconst HOUR = 60 * 60 * 1000;\n\nconst late = $input.all()\n .map((item) => item.json)\n .filter((t) => [\"open\", \"new\"].includes(String(t.status).toLowerCase()))\n .map((t) => {\n const priority = String(t.priority || \"normal\").toLowerCase();\n const hours = Math.floor((Date.now() - new Date(t.created_at)) / HOUR);\n return { ...t, priority, hours, target: TARGET_HOURS[priority] ?? 24 };\n })\n .filter((t) => t.hours >= t.target)\n .sort((a, b) => b.hours - a.hours);\n\nif (!late.length) return [];\n\nconst message =\n `⏰ ${late.length} ticket${late.length > 1 ? \"s\" : \"\"} past the response target:\\n` +\n late.map((t) => `• #${t.id} ${t.subject} (${t.priority}, ${t.hours}h, ${t.assignee || \"unassigned\"})`).join(\"\\n\");\n\nreturn [{ json: { message } }];"
},
"id": "45821f1c-7a2a-4a1b-a1b8-08dd9ee50303",
"name": "Past response target",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
600,
0
]
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "#support",
"mode": "name"
},
"text": "={{ $json.message }}",
"otherOptions": {}
},
"id": "45821f1c-7a2a-4a1b-a1b8-08dd9ee50303",
"name": "Alert support",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
900,
0
]
}
],
"connections": {
"Every 30 minutes": {
"main": [
[
{
"node": "Read tickets",
"type": "main",
"index": 0
}
]
]
},
"Read tickets": {
"main": [
[
{
"node": "Past response target",
"type": "main",
"index": 0
}
]
]
},
"Past response target": {
"main": [
[
{
"node": "Alert support",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"meta": {
"templateCredsSetupCompleted": false
},
"tags": []
}In Make, create a new scenario and add these modules in order:
- Schedule the scenario: Every 30 minutes
- Google Sheets › Search Rows
- Tools › Set variable / Iterator + Array aggregator (same logic as the Code step)
- Slack › Create a Message
- Every module run uses Make operations — the free plan includes a monthly quota.
- Make's built-in functions (map, sum, formatDate…) can replace most Code steps; for complex logic use a code module.
- Module and step names can differ slightly as Make and Zapier update their apps.
In Zapier, create a new Zap and add these steps in order:
- Schedule by Zapier — Every 30 minutes
- Google Sheets — Lookup Spreadsheet Rows (or Code by Zapier)
- Code by Zapier — Run JavaScript (paste the code from the Code section)
- Slack — Send Channel Message
- Zaps with more than two steps need a paid Zapier plan.
- Webhooks, Filters/Paths and Code by Zapier are premium features on some Zapier plans.
- Code by Zapier runs JavaScript: reuse the code shown above, reading inputs from inputData instead of $json.
- Module and step names can differ slightly as Make and Zapier update their apps.
Tips
- Change the targets in the Code step to match your promises to customers.
Related flows
Customer SupportMedium
AI triage for support emails
AI labels every support email (billing, bug, question) and routes it to the right place.
Gmail → AI model → Slack10h/mo
Customer SupportMedium
Catch unhappy customers from feedback forms
AI reads every feedback response and alerts you instantly when it's negative.
Google Forms → AI model → Slack3h/mo
Customer SupportMedium
Translate foreign-language support emails
AI translates non-English support emails and posts the translation to Slack.
Gmail → AI model → Slack5h/mo