AutomationFlowsAI & RAG › Monitor Overtime Risk Against Japanese Labour Caps with Gemini, Sheets,…

Monitor Overtime Risk Against Japanese Labour Caps with Gemini, Sheets,…

Original n8n title: Monitor Overtime Risk Against Japanese Labour Caps with Gemini, Sheets, Slack & Gmail

ByOka Hironobu @okp29 on n8n.io

This workflow runs weekly to read attendance overtime from Google Sheets, totals it per employee and month, uses Google Gemini to classify risk against Japanese labour-law limits, writes results back to Sheets, and escalates high-risk cases via Slack and a Gmail draft. Runs…

Cron / scheduled trigger★★★★☆ complexityAI-powered20 nodesGoogle SheetsChain LlmGoogle Gemini ChatOutput Parser StructuredSlackGmail
AI & RAG Trigger: Cron / scheduled Nodes: 20 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Chainllm → Gmail 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
{
  "id": "AVlmcAwuhhZs5AFw",
  "meta": {
    "builderVariant": "mcp",
    "aiBuilderAssisted": true
  },
  "name": "Monitor overtime against Japanese labour-law limits with Gemini, Sheets and Slack",
  "tags": [],
  "nodes": [
    {
      "id": "74992b71-6542-498d-b223-b78f9fca9548",
      "name": "Schedule Weekly Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        48,
        48
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "b23c5d49-5521-42b2-81b0-31d23764c9cc",
      "name": "Set Legal Limits",
      "type": "n8n-nodes-base.set",
      "position": [
        272,
        48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "m",
              "name": "monthlyLimit",
              "type": "number",
              "value": 45
            },
            {
              "id": "w",
              "name": "warningThreshold",
              "type": "number",
              "value": 36
            },
            {
              "id": "s",
              "name": "singleMonthMax",
              "type": "number",
              "value": 100
            },
            {
              "id": "y",
              "name": "annualLimit",
              "type": "number",
              "value": 720
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "8e2cadb7-4f2d-47e1-a14b-3a5c98023ce4",
      "name": "Get Attendance Records",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        480,
        48
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Attendance"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "a046f64b-5d56-4e19-8ae7-3b0dfc7976f8",
      "name": "Aggregate Overtime by Employee",
      "type": "n8n-nodes-base.code",
      "position": [
        848,
        48
      ],
      "parameters": {
        "jsCode": "const rows = $input.all().map(function (i) { return i.json; });\nconst map = {};\nfor (const r of rows) {\n  const name = r.Employee || r.employee || r.Name || '';\n  if (!name) continue;\n  const month = String(r.Month || r.month || String(r.Date || '').slice(0, 7));\n  const key = name + '|' + month;\n  const ot = Number(r['Overtime Hours'] || r.overtimeHours || r.Overtime || 0) || 0;\n  if (!map[key]) { map[key] = { recordKey: key, employee: name, month: month, overtimeHours: 0, entries: 0 }; }\n  map[key].overtimeHours += ot;\n  map[key].entries += 1;\n}\nreturn Object.keys(map).map(function (k) { return { json: map[k] }; });"
      },
      "typeVersion": 2
    },
    {
      "id": "a647f3c4-04c4-4875-bf7e-196aaaa762d0",
      "name": "Assess Overtime Risk with Gemini",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "onError": "continueErrorOutput",
      "position": [
        1168,
        48
      ],
      "parameters": {
        "text": "=You are a Japanese labour-compliance adviser. Under the Labour Standards Act and a 36 agreement, overtime is capped at {{ $(\"Set Legal Limits\").first().json.monthlyLimit }} hours per month, must never exceed {{ $(\"Set Legal Limits\").first().json.singleMonthMax }} hours in a single month, and {{ $(\"Set Legal Limits\").first().json.annualLimit }} hours per year.\n\nSet risk_level to \"over\" when the monthly cap is already exceeded, \"approaching\" when it is at or above {{ $(\"Set Legal Limits\").first().json.warningThreshold }} hours but still under the cap, and \"ok\" otherwise. Give a short summary and concrete recommended actions for the manager.\n\nEmployee: {{ $json.employee }}\nMonth: {{ $json.month }}\nOvertime hours this month: {{ $json.overtimeHours }}",
        "batching": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "2f02a3f9-249e-47cd-8b86-84b9ab1dd08b",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1136,
        240
      ],
      "parameters": {
        "options": {
          "temperature": 0.1
        },
        "modelName": "models/gemini-3.1-flash-lite"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "f089da35-5794-42a9-bf98-1b0c04af53da",
      "name": "Overtime Risk Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1392,
        240
      ],
      "parameters": {
        "jsonSchemaExample": "{\"risk_level\":\"over\",\"summary\":\"48h exceeds the 45h monthly cap\",\"recommended_actions\":[\"Redistribute workload\",\"Record the special-clause reason\"]}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "119c050c-4ec5-430b-a0b3-a39431e56491",
      "name": "Parse Overtime Risk",
      "type": "n8n-nodes-base.code",
      "position": [
        1664,
        32
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const src = $json.output || $json;\nconst a = $('Aggregate Overtime by Employee').item.json;\nconst arr = function (x) { return Array.isArray(x) ? x.join('; ') : (x || ''); };\nreturn {\n  recordKey: a.recordKey,\n  employee: a.employee,\n  month: a.month,\n  overtimeHours: a.overtimeHours,\n  riskLevel: String(src.risk_level || 'ok').toLowerCase(),\n  summary: src.summary || '',\n  recommendedActions: arr(src.recommended_actions)\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "d5b7ba08-e000-4183-ba5c-6fad306c3dfd",
      "name": "Update Overtime Status",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2032,
        32
      ],
      "parameters": {
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Overtime Status"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your spreadsheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "4d1ce4ca-2006-4bbf-bb4a-64f87fecc5e2",
      "name": "Route by Risk Level",
      "type": "n8n-nodes-base.switch",
      "position": [
        2240,
        0
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $(\"Parse Overtime Risk\").item.json.riskLevel }}",
                    "rightValue": "over"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $(\"Parse Overtime Risk\").item.json.riskLevel }}",
                    "rightValue": "approaching"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $(\"Parse Overtime Risk\").item.json.riskLevel }}",
                    "rightValue": "ok"
                  }
                ]
              }
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "Other"
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "2cdb69ce-3421-4967-9d91-e413819c39ac",
      "name": "Alert HR Immediately",
      "type": "n8n-nodes-base.slack",
      "position": [
        2528,
        -16
      ],
      "parameters": {
        "text": "=*:rotating_light: Overtime cap exceeded*\n*Employee:* {{ $(\"Parse Overtime Risk\").item.json.employee }}\n*Month:* {{ $(\"Parse Overtime Risk\").item.json.month }}\n*Overtime:* {{ $(\"Parse Overtime Risk\").item.json.overtimeHours }}h\n*Summary:* {{ $(\"Parse Overtime Risk\").item.json.summary }}\n*Actions:* {{ $(\"Parse Overtime Risk\").item.json.recommendedActions }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "#hr"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "e5fc08e8-7f79-47d1-bdbc-a5b139f09017",
      "name": "Draft Manager Notice",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2752,
        -16
      ],
      "parameters": {
        "message": "={{ \"The following employee has exceeded the monthly overtime cap.\\n\\nEmployee: \" + $(\"Parse Overtime Risk\").item.json.employee + \"\\nMonth: \" + $(\"Parse Overtime Risk\").item.json.month + \"\\nOvertime hours: \" + $(\"Parse Overtime Risk\").item.json.overtimeHours + \"\\n\\n\" + $(\"Parse Overtime Risk\").item.json.summary + \"\\n\\nRecommended actions:\\n\" + $(\"Parse Overtime Risk\").item.json.recommendedActions }}",
        "options": {},
        "subject": "={{ \"Overtime cap exceeded - \" + $(\"Parse Overtime Risk\").item.json.employee }}",
        "resource": "draft"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "728b87a9-4b3e-49df-a85f-7600cdda57ee",
      "name": "Warn Manager Early",
      "type": "n8n-nodes-base.slack",
      "position": [
        2528,
        144
      ],
      "parameters": {
        "text": "=*:warning: Overtime approaching the legal cap*\n*Employee:* {{ $(\"Parse Overtime Risk\").item.json.employee }}\n*Overtime so far:* {{ $(\"Parse Overtime Risk\").item.json.overtimeHours }}h in {{ $(\"Parse Overtime Risk\").item.json.month }}\n*Actions:* {{ $(\"Parse Overtime Risk\").item.json.recommendedActions }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "#hr"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "89bc09b3-0026-44a9-8f60-9ed5d4a1c329",
      "name": "Flag Unclear Result",
      "type": "n8n-nodes-base.slack",
      "position": [
        2528,
        304
      ],
      "parameters": {
        "text": "=*:mag: Overtime risk could not be classified*\n*Employee:* {{ $(\"Parse Overtime Risk\").item.json.employee }}\nPlease review manually.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "#hr"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "734deb31-76dc-4c38-b288-eb35cdae1fc6",
      "name": "Report Assessment Failure",
      "type": "n8n-nodes-base.slack",
      "position": [
        1664,
        208
      ],
      "parameters": {
        "text": "=*:x: Overtime assessment failed*\n*Employee:* {{ $(\"Aggregate Overtime by Employee\").item.json.employee }}\nPlease review the hours manually.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "#hr"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "db7040ac-c2d9-455f-ab2b-3ed2fc12516b",
      "name": "Overview Sticky",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -464,
        -288
      ],
      "parameters": {
        "width": 440,
        "height": 824,
        "content": "## Monitor overtime against Japanese labour-law limits with Gemini, Sheets and Slack\n\n### How it works\nUnder Japan's Labour Standards Act and a 36 agreement, overtime is capped at 45 hours a month and 720 a year, and breaching it is a real legal risk. Every week this template reads raw attendance rows from Google Sheets, aggregates overtime per employee and month, and sends each total to a Basic LLM Chain with Google Gemini together with your configured limits. Gemini classifies the record as over, approaching or ok and returns concrete actions for the manager. Results are upserted into a status sheet keyed by employee and month so repeated runs never duplicate, then a Switch escalates: over the cap raises an HR alert plus a manager email draft, approaching sends an early warning, ok is logged only.\n\n### Setup\n1. Connect Google Sheets, Google Gemini (PaLM) API, Gmail and Slack.\n2. Provide an Attendance sheet with Employee, Month and Overtime Hours columns.\n3. Adjust the caps in **Set Legal Limits** and pick your HR Slack channel.\n\n### Customization tips\nSwitch to daily runs, or add the 2-6 month average check."
      },
      "typeVersion": 1
    },
    {
      "id": "09f5fecf-5fef-4057-a3c8-93f9e782557d",
      "name": "S1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 720,
        "height": 760,
        "content": "## 1. Schedule, limits & fetch\nRun weekly, set the statutory overtime caps, and read the raw attendance rows."
      },
      "typeVersion": 1
    },
    {
      "id": "46a61812-3b9d-48a3-8530-e4340be2e527",
      "name": "S2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        752,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 760,
        "content": "## 2. Aggregate per employee\nTotal overtime hours by employee and month."
      },
      "typeVersion": 1
    },
    {
      "id": "3d3069df-8f27-4718-ae94-71cad61291a6",
      "name": "S3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 760,
        "content": "## 3. AI risk assessment (Basic LLM Chain)\nGemini classifies each total as over, approaching or ok against the caps and suggests actions. Failures are reported to Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "e812b4c8-168d-448b-b71c-c237c05ebb1f",
      "name": "S4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 1056,
        "height": 760,
        "content": "## 4. Persist & escalate\nUpsert by employee and month so runs never duplicate, then route: over the cap alerts HR and drafts a manager notice, approaching warns early, ok is logged only."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "0eab4327-037f-4016-a0bc-ed126269b8c1",
  "nodeGroups": [],
  "connections": {
    "Set Legal Limits": {
      "main": [
        [
          {
            "node": "Get Attendance Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Overtime Risk": {
      "main": [
        [
          {
            "node": "Update Overtime Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Risk Level": {
      "main": [
        [
          {
            "node": "Alert HR Immediately",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Warn Manager Early",
            "type": "main",
            "index": 0
          }
        ],
        [],
        [
          {
            "node": "Flag Unclear Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert HR Immediately": {
      "main": [
        [
          {
            "node": "Draft Manager Notice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Overtime Risk Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Assess Overtime Risk with Gemini",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Weekly Check": {
      "main": [
        [
          {
            "node": "Set Legal Limits",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Attendance Records": {
      "main": [
        [
          {
            "node": "Aggregate Overtime by Employee",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Overtime Status": {
      "main": [
        [
          {
            "node": "Route by Risk Level",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Assess Overtime Risk with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Overtime by Employee": {
      "main": [
        [
          {
            "node": "Assess Overtime Risk with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assess Overtime Risk with Gemini": {
      "main": [
        [
          {
            "node": "Parse Overtime Risk",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Report Assessment Failure",
            "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.

Pro

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

About this workflow

This workflow runs weekly to read attendance overtime from Google Sheets, totals it per employee and month, uses Google Gemini to classify risk against Japanese labour-law limits, writes results back to Sheets, and escalates high-risk cases via Slack and a Gmail draft. Runs…

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This workflow runs daily to review Japanese ad copy in Google Sheets using Google Gemini, writes compliance risk results back to the sheet, and alerts the right team in Slack (and drafts a Gmail revis

Google Sheets, Chain Llm, Google Gemini Chat +3
AI & RAG

Categories Content Creation AI Automation Publishing Social Media

Google Docs, HTTP Request, Slack +7
AI & RAG

Automate your lead intake, scoring, and outreach pipeline. This workflow collects leads from forms, enriches and scores them using Relevance AI, routes them by quality, and triggers the right follow-u

Form Trigger, HTTP Request, Chain Llm +6
AI & RAG

Automatically identifies overdue sales leads and generates personalized follow-up emails using AI. Runs every weekday Reads leads from Google Sheets Filters leads with no contact for 5+ days Downloads

Google Sheets, Chain Llm, Google Gemini Chat +3
AI & RAG

This workflow monitors Gmail for unread invoice emails with PDF attachments, extracts invoice details with Google Gemini, verifies Japanese qualified invoice registration numbers against the National

Gmail Trigger, Chain Llm, Google Gemini Chat +5