{
  "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
          }
        ]
      ]
    }
  }
}