This workflow corresponds to n8n.io template #12017 — we link there as the canonical source.
This workflow follows the Google Sheets → HTTP Request 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": "Xl5V31AdUfxfiFbr",
"name": "Monitor employee stress levels from Slack and tasks to Google Sheets",
"tags": [],
"nodes": [
{
"id": "a7b9c8d2-3e4f-5678-9012-3456789abcde",
"name": "Workflow Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-256,
-752
],
"parameters": {
"width": 380,
"height": 686,
"content": "### \ud83e\udde0 Employee Stress Monitor & Wellness Assistant\n\nThis workflow automates employee well-being monitoring by analyzing three data points: **Communication Tone (Slack)**, **Attendance**, and **Task Completion**.\n\n### \u2699\ufe0f How it works\n1. **Daily Analysis:** Runs every night to check the previous day's activity.\n2. **AI Processing:** Uses OpenAI (GPT-4) to analyze sentiment in Slack messages and combine it with overtime/delay metrics.\n3. **Privacy First:** \n - **HR Dashboard:** Sends **anonymized (hashed)** data to Google Sheets for trend analysis.\n - **Direct Intervention:** If stress levels are \"High\", sends a private, empathetic DM to the employee offering counseling.\n\n### \ud83d\udcdd Setup Guide\n1. **Credentials:** Configure OpenAI, Slack, Google Sheets, and Postgres credentials.\n2. **Data Sources:** The `HTTP Request` nodes for Attendance and Tasks are placeholders. Replace them with your actual tools (e.g., Jira, Asana, BambooHR) or configure the API URLs.\n3. **Database:** Create a Google Sheet with columns (`employee_hash`, `stress_score`, etc.) and a Postgres table (`counseling_logs`)."
},
"typeVersion": 1
},
{
"id": "b1c2d3e4-f5a6-7890-1234-567890abcdef",
"name": "Run Daily at 2 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
400,
48
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 2
}
]
}
},
"typeVersion": 1.2
},
{
"id": "c3d4e5f6-a7b8-9012-3456-7890abcdef01",
"name": "Note: Schedule",
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
-96
],
"parameters": {
"color": 7,
"width": 426,
"height": 308,
"content": "**\ud83d\udcc5 1. Schedule & Timeframe**\n- Trigger: Runs daily at 02:00 AM.\n- Code Node: Calculates the timestamp range for the previous 24 hours (00:00 - 23:59)."
},
"typeVersion": 1
},
{
"id": "d4e5f6a7-b8c9-0123-4567-890abcdef012",
"name": "Calculate Analysis Period",
"type": "n8n-nodes-base.code",
"position": [
624,
48
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Calculate analysis period\nconst now = new Date();\nconst yesterday = new Date(now);\nyesterday.setDate(yesterday.getDate() - 1);\n\n// Set from 00:00:00 to 23:59:59 of the previous day\nconst analysisStart = new Date(yesterday);\nanalysisStart.setHours(0, 0, 0, 0);\n\nconst analysisEnd = new Date(yesterday);\nanalysisEnd.setHours(23, 59, 59, 999);\n\nreturn {\n analysisStart: analysisStart.toISOString(),\n analysisEnd: analysisEnd.toISOString(),\n analysisStartUnix: Math.floor(analysisStart.getTime() / 1000),\n analysisEndUnix: Math.floor(analysisEnd.getTime() / 1000)\n};"
},
"typeVersion": 2
},
{
"id": "e5f6a7b8-c9d0-1234-5678-90abcdef0123",
"name": "Note: Period Calculation",
"type": "n8n-nodes-base.stickyNote",
"position": [
1216,
-288
],
"parameters": {
"color": 7,
"width": 698,
"height": 404,
"content": "**\ud83e\udd16 2. Data Aggregation & AI Analysis**\n- **Slack:** Fetches user messages and summarizes them.\n- **OpenAI:** Analyzes the tone/sentiment of the messages.\n- **Metrics:** Fetches Attendance (Work hours) and Task (Completion rate) data via API.\n- **Consolidation:** Merges all metrics for the final AI assessment."
},
"typeVersion": 1
},
{
"id": "f6a7b8c9-d0e1-2345-6789-0abcdef01234",
"name": "Fetch Employee List",
"type": "n8n-nodes-base.httpRequest",
"position": [
848,
48
],
"parameters": {
"url": "https://hr.example.com/api/employees",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "status",
"value": "active"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "a7b8c9d0-e1f2-3456-7890-abcdef01234",
"name": "Note: Fetch Employees",
"type": "n8n-nodes-base.stickyNote",
"position": [
3248,
-256
],
"parameters": {
"color": 7,
"width": 858,
"height": 350,
"content": "**\ud83d\udee1\ufe0f 3. Anonymization & Dashboard**\n- **Hashing:** Hashes the `Employee ID` to protect identity in the general report.\n- **Google Sheets:** Logs the anonymized stress score for HR trends/analytics."
},
"typeVersion": 1
},
{
"id": "b8c9d0e1-f2a3-4567-8901-bcdef0123456",
"name": "Start Employee Loop",
"type": "n8n-nodes-base.splitInBatches",
"position": [
1072,
48
],
"parameters": {
"options": {},
"batchSize": 10
},
"typeVersion": 3
},
{
"id": "d0e1f2a3-b4c5-6789-0123-def012345678",
"name": "Fetch Slack Messages",
"type": "n8n-nodes-base.slack",
"position": [
1296,
-96
],
"parameters": {
"resource": "search"
},
"typeVersion": 2.1
},
{
"id": "e1f2a3b4-c5d6-7890-1234-ef0123456789",
"name": "Generate Message Summary",
"type": "n8n-nodes-base.code",
"position": [
1520,
-96
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Combine messages for summary\nconst messages = $input.item.json.messages || [];\nconst maxMessages = 50; // Up to the latest 50 messages\n\nconst messageTexts = messages\n .slice(0, maxMessages)\n .map(m => m.text)\n .filter(text => text && text.length > 0)\n .join('\\n');\n\nreturn {\n ...$input.item.json,\n messageSummary: messageTexts,\n messageCount: messages.length\n};"
},
"typeVersion": 2
},
{
"id": "f2a3b4c5-d6e7-8901-2345-f01234567890",
"name": "AI Tone Analysis",
"type": "n8n-nodes-base.openAi",
"position": [
1744,
-96
],
"parameters": {
"operation": "message",
"requestOptions": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "b4c5d6e7-f8a9-0123-4567-123456789012",
"name": "Format Data for Privacy",
"type": "n8n-nodes-base.set",
"position": [
1968,
-96
],
"parameters": {
"options": {}
},
"typeVersion": 3.2
},
{
"id": "c5d6e7f8-a9b0-1234-5678-234567890123",
"name": "Fetch Attendance Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
2192,
-96
],
"parameters": {
"url": "https://attendance.example.com/api/logs",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "employee_id",
"value": "={{ $json.employee_id }}"
},
{
"name": "from",
"value": "={{ $json.analysisStart }}"
},
{
"name": "to",
"value": "={{ $json.analysisEnd }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "d6e7f8a9-b0c1-2345-6789-345678901234",
"name": "Calculate Attendance Metrics",
"type": "n8n-nodes-base.code",
"position": [
2416,
-96
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Calculate attendance metrics\nconst logs = $input.item.json.attendance_logs || [];\n\nlet totalWorkHours = 0;\nlet totalOvertimeHours = 0;\nlet lateArrivalCount = 0;\nlet absentCount = 0;\n\nlogs.forEach(log => {\n totalWorkHours += log.work_hours || 0;\n totalOvertimeHours += log.overtime_hours || 0;\n if (log.late_arrival) lateArrivalCount++;\n if (log.absent) absentCount++;\n});\n\nconst avgWorkHours = logs.length > 0 ? totalWorkHours / logs.length : 0;\n\nreturn {\n ...$input.item.json,\n avg_work_hours: avgWorkHours,\n total_overtime_hours: totalOvertimeHours,\n late_arrival_count: lateArrivalCount,\n absent_count: absentCount\n};"
},
"typeVersion": 2
},
{
"id": "e7f8a9b0-c1d2-3456-7890-456789012345",
"name": "Fetch Task Completion Rates",
"type": "n8n-nodes-base.httpRequest",
"position": [
2640,
-96
],
"parameters": {
"url": "https://tasks.example.com/api/tasks",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "assignee_id",
"value": "={{ $json.employee_id }}"
},
{
"name": "from",
"value": "={{ $json.analysisStart }}"
},
{
"name": "to",
"value": "={{ $json.analysisEnd }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "f8a9b0c1-d2e3-4567-8901-567890123456",
"name": "Calculate Task Metrics",
"type": "n8n-nodes-base.code",
"position": [
2864,
-96
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Calculate task metrics\nconst tasks = $input.item.json.tasks || [];\n\nlet completedCount = 0;\nlet overdueCount = 0;\nlet totalDelayDays = 0;\n\ntasks.forEach(task => {\n if (task.status === 'completed') completedCount++;\n if (task.overdue) {\n overdueCount++;\n totalDelayDays += task.delay_days || 0;\n }\n});\n\nconst completionRate = tasks.length > 0 ? completedCount / tasks.length : 0;\nconst avgDelayDays = overdueCount > 0 ? totalDelayDays / overdueCount : 0;\n\nreturn {\n ...$input.item.json,\n task_completion_rate: completionRate,\n overdue_task_count: overdueCount,\n avg_task_delay_days: avgDelayDays\n};"
},
"typeVersion": 2
},
{
"id": "a9b0c1d2-e3f4-5678-9012-678901234567",
"name": "Consolidate Features",
"type": "n8n-nodes-base.code",
"position": [
3088,
-96
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Consolidate all features\nreturn {\n employee_id: $input.item.json.employee_id,\n slack_user_id: $input.item.json.slack_user_id,\n department: $input.item.json.department,\n tone_stress_score: $input.item.json.tone_stress_score || 0,\n avg_work_hours: $input.item.json.avg_work_hours || 0,\n total_overtime_hours: $input.item.json.total_overtime_hours || 0,\n late_arrival_count: $input.item.json.late_arrival_count || 0,\n absent_count: $input.item.json.absent_count || 0,\n task_completion_rate: $input.item.json.task_completion_rate || 0,\n overdue_task_count: $input.item.json.overdue_task_count || 0,\n analysisStart: $input.item.json.analysisStart,\n analysisEnd: $input.item.json.analysisEnd\n};"
},
"typeVersion": 2
},
{
"id": "b0c1d2e3-f4a5-6789-0123-789012345678",
"name": "AI Stress Level Prediction",
"type": "n8n-nodes-base.openAi",
"position": [
3312,
-96
],
"parameters": {
"operation": "message",
"requestOptions": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "c1d2e3f4-a5b6-7890-1234-890123456789",
"name": "Note: Stress Prediction",
"type": "n8n-nodes-base.stickyNote",
"position": [
4144,
-304
],
"parameters": {
"color": 7,
"width": 682,
"height": 452,
"content": "**\u2764\ufe0f 4. Action & Intervention**\n- **Logic:** Checks if `stress_level` is \"High\".\n- **True:** Sends a private Slack DM to the employee suggesting counseling and logs the event in a restricted Postgres DB.\n- **False:** Loop continues to the next employee."
},
"typeVersion": 1
},
{
"id": "d2e3f4a5-b6c7-8901-2345-901234567890",
"name": "Generate Anonymized Data",
"type": "n8n-nodes-base.code",
"position": [
3536,
-96
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Generate anonymized data\nconst crypto = require('crypto');\n\n// Hash employee_id\nconst hash = crypto.createHash('sha256');\nhash.update($input.item.json.employee_id);\nconst employee_hash = hash.digest('hex');\n\nconst stressData = JSON.parse($input.item.json.response || '{}');\n\nreturn {\n employee_hash: employee_hash,\n employee_id: $input.item.json.employee_id, // Keep for separate branch\n slack_user_id: $input.item.json.slack_user_id, // Keep for separate branch\n department: $input.item.json.department,\n stress_score: stressData.stress_score || 0,\n stress_level: stressData.stress_level || 'unknown',\n main_risk_factors: stressData.main_risk_factors || [],\n analysis_date: new Date().toISOString().split('T')[0],\n analysis_period_from: $input.item.json.analysisStart,\n analysis_period_to: $input.item.json.analysisEnd\n};"
},
"typeVersion": 2
},
{
"id": "e3f4a5b6-c7d8-9012-3456-012345678901",
"name": "Filter Fields for HR Dashboard",
"type": "n8n-nodes-base.set",
"position": [
3760,
-96
],
"parameters": {
"options": {}
},
"typeVersion": 3.2
},
{
"id": "f4a5b6c7-d8e9-0123-4567-123456789012",
"name": "Save to HR Dashboard",
"type": "n8n-nodes-base.googleSheets",
"position": [
3984,
-96
],
"parameters": {
"columns": {
"value": {
"department": "={{ $json.department }}",
"stress_level": "={{ $json.stress_level }}",
"stress_score": "={{ $json.stress_score }}",
"analysis_date": "={{ $json.analysis_date }}",
"employee_hash": "={{ $json.employee_hash }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": "Stress Analysis Logs",
"documentId": "YOUR_SPREADSHEET_ID"
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
},
{
"id": "b6c7d8e9-f0a1-2345-6789-345678901234",
"name": "Check for High Stress",
"type": "n8n-nodes-base.if",
"position": [
4208,
-96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.stress_level }}",
"rightValue": "high"
}
]
}
},
"typeVersion": 2
},
{
"id": "d8e9f0a1-b2c3-4567-8901-567890123456",
"name": "Send Slack DM to High Stress Employee",
"type": "n8n-nodes-base.slack",
"position": [
4432,
-96
],
"parameters": {
"operation": "send",
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "f0a1b2c3-d4e5-6789-0123-789012345678",
"name": "Log Counseling Proposal",
"type": "n8n-nodes-base.postgres",
"position": [
4656,
-32
],
"parameters": {
"table": "counseling_logs",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"0": "e",
"1": "m",
"2": "p",
"3": "l",
"4": "o",
"5": "y",
"6": "e",
"7": "e",
"8": "_",
"9": "h",
"10": "a",
"11": "s",
"12": "h",
"13": ",",
"14": "d",
"15": "e",
"16": "p",
"17": "a",
"18": "r",
"19": "t",
"20": "m",
"21": "e",
"22": "n",
"23": "t",
"24": ",",
"25": "s",
"26": "t",
"27": "r",
"28": "e",
"29": "s",
"30": "s",
"31": "_",
"32": "l",
"33": "e",
"34": "v",
"35": "e",
"36": "l",
"37": ",",
"38": "s",
"39": "t",
"40": "r",
"41": "e",
"42": "s",
"43": "s",
"44": "_",
"45": "s",
"46": "c",
"47": "o",
"48": "r",
"49": "e",
"50": ",",
"51": "n",
"52": "o",
"53": "t",
"54": "i",
"55": "f",
"56": "i",
"57": "e",
"58": "d",
"59": "_",
"60": "a",
"61": "t",
"62": ",",
"63": "a",
"64": "n",
"65": "a",
"66": "l",
"67": "y",
"68": "s",
"69": "i",
"70": "s",
"71": "_",
"72": "p",
"73": "e",
"74": "r",
"75": "i",
"76": "o",
"77": "d",
"78": "_",
"79": "f",
"80": "r",
"81": "o",
"82": "m",
"83": ",",
"84": "a",
"85": "n",
"86": "a",
"87": "l",
"88": "y",
"89": "s",
"90": "i",
"91": "s",
"92": "_",
"93": "p",
"94": "e",
"95": "r",
"96": "i",
"97": "o",
"98": "d",
"99": "_",
"100": "t",
"101": "o",
"value": {},
"schema": [],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"typeVersion": 2.4
}
],
"active": false,
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "4b49193b-ba49-4179-bd69-b12b7e07b457",
"connections": {
"AI Tone Analysis": {
"main": [
[
{
"node": "Format Data for Privacy",
"type": "main",
"index": 0
}
]
]
},
"Run Daily at 2 AM": {
"main": [
[
{
"node": "Calculate Analysis Period",
"type": "main",
"index": 0
}
]
]
},
"Fetch Employee List": {
"main": [
[
{
"node": "Start Employee Loop",
"type": "main",
"index": 0
}
]
]
},
"Start Employee Loop": {
"main": [
[
{
"node": "Fetch Slack Messages",
"type": "main",
"index": 0
}
]
]
},
"Consolidate Features": {
"main": [
[
{
"node": "AI Stress Level Prediction",
"type": "main",
"index": 0
}
]
]
},
"Fetch Slack Messages": {
"main": [
[
{
"node": "Generate Message Summary",
"type": "main",
"index": 0
}
]
]
},
"Save to HR Dashboard": {
"main": [
[
{
"node": "Check for High Stress",
"type": "main",
"index": 0
}
]
]
},
"Check for High Stress": {
"main": [
[
{
"node": "Send Slack DM to High Stress Employee",
"type": "main",
"index": 0
}
],
[
{
"node": "Start Employee Loop",
"type": "main",
"index": 0
}
]
]
},
"Fetch Attendance Data": {
"main": [
[
{
"node": "Calculate Attendance Metrics",
"type": "main",
"index": 0
}
]
]
},
"Calculate Task Metrics": {
"main": [
[
{
"node": "Consolidate Features",
"type": "main",
"index": 0
}
]
]
},
"Format Data for Privacy": {
"main": [
[
{
"node": "Fetch Attendance Data",
"type": "main",
"index": 0
}
]
]
},
"Log Counseling Proposal": {
"main": [
[
{
"node": "Start Employee Loop",
"type": "main",
"index": 0
}
]
]
},
"Generate Anonymized Data": {
"main": [
[
{
"node": "Filter Fields for HR Dashboard",
"type": "main",
"index": 0
}
]
]
},
"Generate Message Summary": {
"main": [
[
{
"node": "AI Tone Analysis",
"type": "main",
"index": 0
}
]
]
},
"Calculate Analysis Period": {
"main": [
[
{
"node": "Fetch Employee List",
"type": "main",
"index": 0
}
]
]
},
"AI Stress Level Prediction": {
"main": [
[
{
"node": "Generate Anonymized Data",
"type": "main",
"index": 0
}
]
]
},
"Fetch Task Completion Rates": {
"main": [
[
{
"node": "Calculate Task Metrics",
"type": "main",
"index": 0
}
]
]
},
"Calculate Attendance Metrics": {
"main": [
[
{
"node": "Fetch Task Completion Rates",
"type": "main",
"index": 0
}
]
]
},
"Filter Fields for HR Dashboard": {
"main": [
[
{
"node": "Save to HR Dashboard",
"type": "main",
"index": 0
}
]
]
},
"Send Slack DM to High Stress Employee": {
"main": [
[
{
"node": "Log Counseling Proposal",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleSheetsOAuth2ApiopenAiApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow functions as an automated "Chief Wellness Officer," helping HR teams and managers prevent employee burnout before it happens. It aggregates data from communication channels and work tools to provide an AI-driven daily assessment of employee well-being, while…
Source: https://n8n.io/workflows/12017/ — 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.
Scheduled processes retrieve customer feedback from multiple channels. The system performs sentiment analysis to classify tone, then uses OpenAI models to extract themes, topics, and urgency indicator
This n8n workflow automates the transformation of spreadsheet data into professional charts and graphs using AI-driven analysis. Triggered via Slack, it processes uploaded files (Excel, CSV, Google Sh
This workflow is designed for Customer Success Managers, Growth Teams, and SaaS Business Owners who want to proactively reduce churn using AI. It automates the analysis of customer health and the deli
This workflow transforms raw SaaS metrics into a fully automated Product Health Monitoring & Incident Management system.
This workflow continuously validates data quality using rules stored in Notion, runs anomaly checks against your SQL database, generates AI-powered diagnostics, and alerts your team only when real iss