{
  "id": "7KHVOYiGL4lvvi2K",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Autonomous IT Asset Recovery Agent Q \u2014 Google Sheets Edition",
  "tags": [],
  "nodes": [
    {
      "id": "1bb904c6-4c13-4c7e-b6be-34d32cb10d8e",
      "name": "\ud83d\udccb Trigger \u2014 Watch Employees Sheet",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "notes": "Polls the Employees sheet for new/updated rows. Replace YOUR_GOOGLE_SHEET_ID_HERE with your actual Sheet ID from the URL.",
      "position": [
        -1872,
        2752
      ],
      "parameters": {
        "event": "rowAdded",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fiSPjKE-ELzgTJdNyfE1t8z5_Z7JeE6bBUUTcCjkBH0/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1fiSPjKE-ELzgTJdNyfE1t8z5_Z7JeE6bBUUTcCjkBH0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fiSPjKE-ELzgTJdNyfE1t8z5_Z7JeE6bBUUTcCjkBH0/edit?usp=drivesdk",
          "cachedResultName": "IT Allocation"
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c1e2abf9-a6ea-4464-ab18-8d74d95b37e5",
      "name": "\ud83d\udd0d Filter \u2014 Resigned or Terminated Only",
      "type": "n8n-nodes-base.code",
      "position": [
        -1648,
        2752
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// FILTER: Only process rows where status = Resigned or Terminated\n// and email_sent = No (avoid re-processing)\n// ============================================================\nconst rows = $input.all();\n\nconst eligible = rows.filter(item => {\n  const s = (item.json.status || '').trim();\n  const sent = (item.json.email_sent || 'No').trim();\n  return (s === 'Resigned' || s === 'Terminated') && sent === 'No';\n});\n\nif (eligible.length === 0) {\n  return [{ json: { __skip: true, reason: 'No eligible rows found' } }];\n}\n\nreturn eligible.map(item => ({ json: item.json }));"
      },
      "typeVersion": 2
    },
    {
      "id": "be2e2a39-76e2-4bc9-8b8e-282ed8a735df",
      "name": "\u2753 Has Eligible Rows?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1440,
        2752
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-skip",
              "operator": {
                "type": "boolean",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.__skip }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6ad7da48-dc8f-4937-8ac4-a5d87a3e3a27",
      "name": "\ud83d\uddc4\ufe0f Google Sheets \u2014 Read Employee Assets",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Reads all rows from Assets sheet where employee_id matches and status = Allocated",
      "position": [
        -1248,
        2592
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 358936068,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fiSPjKE-ELzgTJdNyfE1t8z5_Z7JeE6bBUUTcCjkBH0/edit#gid=358936068",
          "cachedResultName": "Sheet2"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1fiSPjKE-ELzgTJdNyfE1t8z5_Z7JeE6bBUUTcCjkBH0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fiSPjKE-ELzgTJdNyfE1t8z5_Z7JeE6bBUUTcCjkBH0/edit?usp=drivesdk",
          "cachedResultName": "IT Allocation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1f48a54b-24c6-42bc-b868-19b5a6252287",
      "name": "\ud83d\udd17 Merge Employee Profile & Asset List",
      "type": "n8n-nodes-base.code",
      "position": [
        -1024,
        2592
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// MERGE: Combine employee row with their assets from Sheets\n// $('Filter: Resigned or Terminated Only') = employee data\n// $input = asset rows just read from Google Sheets\n// ============================================================\n\n// Get the employee data from the filter node (passed through IF)\nconst employeeItem = $('IF: Has Eligible Rows?').item;\nconst employee = employeeItem.json;\n\n// All asset rows returned from Google Sheets for this employee\nconst assetItems = $input.all();\nconst assets = assetItems.map(a => ({\n  asset_id:        a.json.asset_id        || '',\n  employee_id:     a.json.employee_id     || '',\n  asset_type:      a.json.asset_type      || '',\n  brand_model:     a.json.brand_model     || '',\n  serial_number:   a.json.serial_number   || '',\n  asset_value_inr: Number(a.json.asset_value_inr) || 0,\n  status:          a.json.status          || 'Allocated',\n  row_number:      Number(a.json.row_number) || 0\n}));\n\nconst totalValue = assets.reduce((sum, a) => sum + a.asset_value_inr, 0);\n\nreturn [{\n  json: {\n    // \u2500\u2500 Employee fields (column names must match your Google Sheet header row exactly)\n    employee_id:        employee.employee_id        || '',\n    name:               employee.name               || '',\n    email:              employee.email              || '',\n    department:         employee.department         || '',\n    status:             employee.status             || '',\n    notice_period_days: Number(employee.notice_period_days) || 0,\n    last_working_day:   employee.last_working_day   || '',\n    manager_email:      employee.manager_email      || '',\n    manager_name:       employee.manager_name       || '',\n    employee_row_number: Number(employee.row_number) || 0,\n    // \u2500\u2500 Aggregated assets\n    assets,\n    total_asset_value_inr: totalValue,\n    asset_count: assets.length\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f6aa5fb0-d596-4ac6-a172-f2f5679f3f4a",
      "name": "\ud83d\udcdd Google Sheets \u2014 Update Employee Return Status",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Writes email_sent=Yes, return_deadline, and urgency back to the Employees sheet row",
      "position": [
        352,
        2352
      ],
      "parameters": {
        "columns": {
          "value": {
            "urgency": "={{ $json.urgency }}",
            "email_sent": "Yes",
            "row_number": "={{ $json.employee_row_number }}",
            "return_deadline": "={{ $json.return_deadline }}"
          },
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ]
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Employees"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "43bb9261-ef74-4041-acd4-353dbf6f2586",
      "name": "\u2699\ufe0f Split \u2014 One Item Per Asset",
      "type": "n8n-nodes-base.code",
      "position": [
        576,
        2352
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// Update each asset row in Google Sheets: status = Return Email Sent\n// Returns one item per asset so the Sheets node loops over them\n// ============================================================\nconst item = $input.item.json;\n\nreturn item.assets.map(asset => ({\n  json: {\n    ...item,\n    current_asset: asset\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "9f3299e6-af9d-4951-8744-bdd49148a5cf",
      "name": "\u2705 Google Sheets \u2014 Mark Asset Email Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        816,
        2352
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Assets"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "547178fc-d84c-4ea4-aa55-f6d0e28d5f07",
      "name": "\u26a0\ufe0f Split \u2014 Isolate Overdue Assets",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        2640
      ],
      "parameters": {
        "jsCode": "// Split overdue assets so we update each row individually\nconst item = $input.item.json;\nreturn item.overdue_assets.map(asset => ({\n  json: { ...item, current_asset: asset }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "02793764-89e6-4b7e-af04-1d5e3ed79881",
      "name": "\ud83d\udea9 Google Sheets \u2014 Mark Assets as Missing",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        624,
        2640
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Assets"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "bc206071-4a1b-469e-886a-6b97f8264a68",
      "name": "\ud83d\udcd2 Log \u2014 No Action Needed",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        2928
      ],
      "parameters": {
        "jsCode": "const item = $input.item.json;\nreturn [{\n  json: {\n    log: `No action needed for ${item.name || 'unknown'}. Reason: ${item.reason || 'Checklist invalid, assets returned, or not overdue.'}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "ed82f2c1-8535-49d4-ab76-f85a0d9900a0",
      "name": "\ud83e\udde0 MCTS \u2014 Plan Asset Return Timeline",
      "type": "n8n-nodes-base.code",
      "position": [
        -800,
        2592
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// MCTS: Monte Carlo Tree Search \u2014 Return Timeline Planner\n// Plans optimal return strategy based on notice period\n// ============================================================\nconst item = $input.item.json;\n\nconst today = new Date();\ntoday.setHours(0, 0, 0, 0);\nconst lastDay = new Date(item.last_working_day);\n\nlet return_strategy, return_deadline, reminder_day, urgency;\n\nif (item.status === 'Terminated' || item.notice_period_days === 0) {\n  return_strategy = 'IMMEDIATE';\n  return_deadline = new Date(today);\n  return_deadline.setDate(today.getDate() + 1);\n  reminder_day = new Date(today);\n  urgency = 'CRITICAL';\n} else if (item.notice_period_days <= 14) {\n  return_strategy = 'SHORT_NOTICE';\n  return_deadline = new Date(lastDay);\n  return_deadline.setDate(lastDay.getDate() - 1);\n  reminder_day = new Date(lastDay);\n  reminder_day.setDate(lastDay.getDate() - 3);\n  urgency = 'HIGH';\n} else {\n  // Standard 30-day notice\n  return_strategy = 'STANDARD';\n  return_deadline = new Date(lastDay);\n  return_deadline.setDate(lastDay.getDate() - 2);\n  reminder_day = new Date(lastDay);\n  reminder_day.setDate(lastDay.getDate() - 5);\n  urgency = 'NORMAL';\n}\n\nconst mcts_recovery_score = Math.round(\n  item.total_asset_value_inr *\n  (urgency === 'CRITICAL' ? 1.0 : urgency === 'HIGH' ? 0.85 : 0.70)\n);\n\nconst fmtDate = d => d.toISOString().split('T')[0];\n\nreturn [{\n  json: {\n    ...item,\n    return_strategy,\n    return_deadline:      fmtDate(return_deadline),\n    reminder_day:         fmtDate(reminder_day),\n    urgency,\n    mcts_recovery_score,\n    days_until_deadline:  Math.ceil((return_deadline - today) / 86400000)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d9068823-da33-467f-925c-96f8ec9dc5a8",
      "name": "\u2705 Self-Critique \u2014 Validate Return Checklist",
      "type": "n8n-nodes-base.code",
      "position": [
        -592,
        2592
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// SELF-CRITIQUE: Validates checklist completeness\n// Every asset must have serial_number, asset_value_inr, asset_type\n// ============================================================\nconst item = $input.item.json;\n\nconst critiques = [];\nlet checklist_valid = true;\n\nif (!item.assets || item.assets.length === 0) {\n  critiques.push('FAIL: No assets found for this employee in the Assets sheet.');\n  checklist_valid = false;\n}\n\n(item.assets || []).forEach((asset, idx) => {\n  if (!asset.serial_number || asset.serial_number.trim() === '') {\n    critiques.push(`WARN: Asset #${idx + 1} (${asset.asset_type}) missing serial number.`);\n    checklist_valid = false;\n  }\n  if (!asset.asset_value_inr || asset.asset_value_inr <= 0) {\n    critiques.push(`WARN: Asset #${idx + 1} (${asset.asset_type}) has no value recorded.`);\n    checklist_valid = false;\n  }\n  if (!asset.asset_type || asset.asset_type.trim() === '') {\n    critiques.push(`FAIL: Asset #${idx + 1} missing asset_type.`);\n    checklist_valid = false;\n  }\n});\n\nif (checklist_valid) {\n  critiques.push('PASS: All assets validated. Checklist complete.');\n}\n\nconst checklist_lines = (item.assets || []).map((a, i) =>\n  `${i + 1}. ${a.asset_type} \u2014 ${a.brand_model} (S/N: ${a.serial_number}) \u2014 \u20b9${Number(a.asset_value_inr).toLocaleString('en-IN')}`\n).join('\\n');\n\nreturn [{\n  json: {\n    ...item,\n    checklist_valid,\n    self_critique_notes: critiques,\n    checklist_lines\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c7126429-2398-4e64-a069-6206ffe13da8",
      "name": "\u2753 Checklist Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        -368,
        2592
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-valid",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.checklist_valid }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "88d476b4-d4de-49f3-b796-4c2af36b4b58",
      "name": "\u270d\ufe0f Build Return Instructions Email Body",
      "type": "n8n-nodes-base.code",
      "position": [
        -80,
        2352
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// Build Gmail Email Body \u2014 personalised per employee\n// ============================================================\nconst item = $input.item.json;\n\nconst firstName = item.name.split(' ')[0];\nconst subject = `[ACTION REQUIRED] IT Asset Return \u2014 ${item.name} | Deadline: ${item.return_deadline}`;\n\nconst body = `Dear ${firstName},\n\nThis is an official communication from the IT Asset Management team at Acme Corp.\n\nAs per HR records, your employment status has been updated to \"${item.status}\" effective ${item.last_working_day}.\n\nPlease arrange to return the following company-issued assets by ${item.return_deadline}:\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nITEMISED ASSET RETURN CHECKLIST\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${item.checklist_lines}\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nTotal Asset Value: \u20b9${Number(item.total_asset_value_inr).toLocaleString('en-IN')}\n\nRETURN INSTRUCTIONS:\n\u2022 Drop off assets at IT Helpdesk, 3rd Floor, Acme Corp HQ, Hyderabad\n\u2022 Office hours: Mon\u2013Fri, 9 AM \u2013 6 PM\n\u2022 Bring this email as reference\n\u2022 For courier returns, contact user@example.com\n\nDEADLINE: ${item.return_deadline}\nFailure to return assets by this date will result in escalation to the Legal team and deduction from your Full & Final Settlement.\n\nIf you have already returned any items, please reply to this email with confirmation.\n\nRegards,\nIT Asset Management Team\nAcme Corp | user@example.com`;\n\nreturn [{\n  json: {\n    ...item,\n    email_subject: subject,\n    email_body: body\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "16491ba6-2b21-48b2-ac4a-7142e5595df6",
      "name": "\ud83d\udce7 Gmail \u2014 Send Return Instructions to Employee",
      "type": "n8n-nodes-base.gmail",
      "position": [
        144,
        2352
      ],
      "parameters": {
        "sendTo": "={{ $json.email }}",
        "message": "={{ $json.email_body }}",
        "options": {
          "ccList": "={{ $json.manager_email }}"
        },
        "subject": "={{ $json.email_subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "4b16c219-6655-4e73-b4d8-9c8848c4dd71",
      "name": "\u23f0 Check Asset Return Deadline",
      "type": "n8n-nodes-base.code",
      "position": [
        -144,
        2688
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// DEADLINE CHECK: Has return_deadline passed today?\n// Also checks for Terminated employees (always immediate)\n// ============================================================\nconst item = $input.item.json;\n\nconst today = new Date();\ntoday.setHours(0, 0, 0, 0);\nconst deadline = new Date(item.return_deadline);\n\nconst isOverdue = today >= deadline;\n\n// Assets still in 'Return Email Sent' or 'Allocated' state = not yet returned\nconst overdue_assets = item.assets.filter(a =>\n  a.status === 'Allocated' || a.status === 'Return Email Sent'\n);\n\nreturn [{\n  json: {\n    ...item,\n    is_overdue: isOverdue,\n    overdue_assets,\n    missing_count: overdue_assets.length,\n    missing_value_inr: overdue_assets.reduce((s, a) => s + a.asset_value_inr, 0)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "cffc11ec-67f5-4f1c-b76f-d89d4f134a2b",
      "name": "\u2753 Assets Overdue?",
      "type": "n8n-nodes-base.if",
      "position": [
        80,
        2688
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-overdue",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.is_overdue }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ad1894ad-a6e1-43bf-854a-f88967f8f031",
      "name": "\ud83d\udd04 DPO \u2014 Build Escalation Message",
      "type": "n8n-nodes-base.code",
      "position": [
        864,
        2640
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// DPO: Direct Preference Optimization \u2014 Escalation Builder\n// Scores severity, selects escalation channel and tone\n// ============================================================\nconst item = $input.item.json;\n\nconst missingList = item.overdue_assets.map((a, i) =>\n  `\u2022 ${a.asset_type} | ${a.brand_model} | S/N: ${a.serial_number} | \u20b9${Number(a.asset_value_inr).toLocaleString('en-IN')}`\n).join('\\n');\n\nconst totalMissingValue = item.missing_value_inr;\nconst escalate_to_legal = totalMissingValue >= 50000;\n\nconst slackMessage = `:rotating_light: *IT ASSET RECOVERY ESCALATION*\\n\\n*Employee:* ${item.name} (${item.employee_id})\\n*Status:* ${item.status}\\n*Department:* ${item.department}\\n*Last Working Day:* ${item.last_working_day}\\n*Return Deadline Passed:* ${item.return_deadline}\\n\\n*MISSING ASSETS (${item.missing_count} items):*\\n${missingList}\\n\\n*Total At-Risk Value:* \u20b9${Number(totalMissingValue).toLocaleString('en-IN')}\\n*MCTS Recovery Score:* \u20b9${Number(item.mcts_recovery_score).toLocaleString('en-IN')}\\n\\n${escalate_to_legal ? ':scales: *Legal escalation triggered \u2014 Full & Final Settlement hold recommended.*' : ':warning: IT Manager follow-up required.'}\\n\\n*Action Required:* Contact employee immediately or initiate F&F deduction.\\nHR: user@example.com | IT: user@example.com`;\n\nreturn [{\n  json: {\n    ...item,\n    slack_escalation_message: slackMessage,\n    escalate_to_legal,\n    dpo_preference: escalate_to_legal ? 'LEGAL_ESCALATION' : 'IT_MANAGER_ONLY'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9088f5bd-f2d6-4741-ae81-5285801d3f80",
      "name": "\ud83d\udd14 Slack \u2014 Alert IT Manager",
      "type": "n8n-nodes-base.slack",
      "notes": "Replace C_IT_ALERTS with your actual Slack channel ID",
      "position": [
        1120,
        2528
      ],
      "parameters": {
        "text": "={{ $json.slack_escalation_message }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "C_IT_ALERTS"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "27af90dc-2756-46d9-b46c-9dde21020d8e",
      "name": "\u2753 Escalate to Legal?",
      "type": "n8n-nodes-base.if",
      "position": [
        1088,
        2720
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-legal",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.escalate_to_legal }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "01862aa3-9a35-420a-ad16-8bdd8a61cd62",
      "name": "\ud83d\udea8 Slack \u2014 Alert Legal Team",
      "type": "n8n-nodes-base.slack",
      "notes": "Replace C_LEGAL_ESCALATIONS with your actual Slack channel ID",
      "position": [
        1360,
        2656
      ],
      "parameters": {
        "text": "={{ $json.slack_escalation_message }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "C_LEGAL_ESCALATIONS"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "37b567ed-a801-4c41-b95f-54904094cc7d",
      "name": "Sticky Note \u2014 Phase 1: Offboarding Detection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1904,
        2528
      ],
      "parameters": {
        "color": 3,
        "width": 490,
        "height": 185,
        "content": "## \ud83d\udce5 Phase 1: Offboarding Detection\nWatches the Employees Google Sheet for changes.\nFilters rows to only process employees with status **Resigned** or **Terminated**.\n- \u2705 **Eligible rows found** \u2192 proceed to asset lookup\n- \u274c **No eligible rows** \u2192 log no action needed and stop"
      },
      "typeVersion": 1
    },
    {
      "id": "58abe98d-0809-425e-8427-fbc776eb1830",
      "name": "Sticky Note \u2014 Phase 2: Asset Lookup & Merge",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1344,
        2896
      ],
      "parameters": {
        "color": 5,
        "width": 470,
        "height": 175,
        "content": "## \ud83d\uddc4\ufe0f Phase 2: Asset Lookup & Data Merge\nFetches all assets assigned to the departing employee from the **Assets Google Sheet**.\nMerges the employee profile with their full asset list to create a unified recovery record used throughout the rest of the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "192ebe33-1ecd-4966-9388-6e22ba53be96",
      "name": "Sticky Note \u2014 Phase 3: MCTS Planning & Checklist Validation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1328,
        2320
      ],
      "parameters": {
        "color": 4,
        "width": 490,
        "height": 227,
        "content": "## \ud83e\udde0 Phase 3: MCTS Planning & Self-Critique\n**MCTS** generates an optimised asset return timeline based on the employee's last day, asset count, and location.\n\n**Self-Critique** then validates the return checklist against IT policy:\n- \u2705 **Valid** \u2192 build and send return instructions\n- \u274c **Invalid** \u2192 flag and stop before emailing"
      },
      "typeVersion": 1
    },
    {
      "id": "69767392-35ae-46fd-bd91-ab85a4a83ef5",
      "name": "Sticky Note \u2014 Phase 4: Return Instructions & Sheet Update",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        2240
      ],
      "parameters": {
        "color": 6,
        "width": 510,
        "height": 222,
        "content": "## \ud83d\udce7 Phase 4: Send Return Instructions & Update Sheets\nBuilds a personalised return instructions email listing all assets to be returned with deadlines.\n- **Gmail** sends the email directly to the departing employee\n- **Google Sheets** updates the employee row with return status\n- Each asset record is split and individually marked as **Email Sent** in the assets sheet"
      },
      "typeVersion": 1
    },
    {
      "id": "c000f387-a91b-40e3-9dbd-4b9afa725c50",
      "name": "Sticky Note \u2014 Phase 5: Deadline Check & Overdue Handling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        2128
      ],
      "parameters": {
        "color": 7,
        "width": 490,
        "height": 175,
        "content": "## \u23f0 Phase 5: Deadline Check & Overdue Asset Handling\nChecks whether the return deadline has passed for each asset:\n- \u2705 **On time** \u2192 no further action needed\n- \u26a0\ufe0f **Overdue** \u2192 isolate overdue assets, mark them as **Missing** in Google Sheets, then trigger the escalation pipeline"
      },
      "typeVersion": 1
    },
    {
      "id": "bb2636bd-8e3c-4dab-8fbe-e4147088e559",
      "name": "Sticky Note \u2014 Phase 6: Escalation & Legal Alert",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1088,
        2256
      ],
      "parameters": {
        "color": 2,
        "width": 490,
        "height": 185,
        "content": "## \ud83d\udea8 Phase 6: Escalation & Legal Alert\nFor overdue assets, Agent Q escalates via **DPO-style messaging**:\n- **Slack** alerts the IT Manager with asset and employee details\n- Evaluates whether the case warrants **legal escalation** based on asset value and overdue duration\n- \ud83d\udea8 If legal threshold met \u2192 **Slack** alerts the Legal Team for formal follow-up"
      },
      "typeVersion": 1
    },
    {
      "id": "4b1f3688-aa26-4427-a9d7-fcbda4fb0c6d",
      "name": "Sticky Note \u2014 Phase 7: No Action Path",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        2880
      ],
      "parameters": {
        "width": 430,
        "height": 150,
        "content": "## \ud83d\udcd2 Phase 7: No Action Needed\nIf no eligible offboarding rows are detected during the trigger check, the workflow logs a **No Action Needed** entry and exits cleanly \u2014 preventing any unnecessary processing or notifications."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "c14a0648-e059-4f12-ac1c-b99d32249e96",
  "connections": {
    "\u2753 Assets Overdue?": {
      "main": [
        [
          {
            "node": "\u26a0\ufe0f Split \u2014 Isolate Overdue Assets",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udcd2 Log \u2014 No Action Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2753 Checklist Valid?": {
      "main": [
        [
          {
            "node": "\u270d\ufe0f Build Return Instructions Email Body",
            "type": "main",
            "index": 0
          },
          {
            "node": "\u23f0 Check Asset Return Deadline",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udcd2 Log \u2014 No Action Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2753 Escalate to Legal?": {
      "main": [
        [
          {
            "node": "\ud83d\udea8 Slack \u2014 Alert Legal Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2753 Has Eligible Rows?": {
      "main": [
        [
          {
            "node": "\ud83d\uddc4\ufe0f Google Sheets \u2014 Read Employee Assets",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udcd2 Log \u2014 No Action Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 Check Asset Return Deadline": {
      "main": [
        [
          {
            "node": "\u2753 Assets Overdue?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Split \u2014 One Item Per Asset": {
      "main": [
        [
          {
            "node": "\u2705 Google Sheets \u2014 Mark Asset Email Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd04 DPO \u2014 Build Escalation Message": {
      "main": [
        [
          {
            "node": "\ud83d\udd14 Slack \u2014 Alert IT Manager",
            "type": "main",
            "index": 0
          },
          {
            "node": "\u2753 Escalate to Legal?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Trigger \u2014 Watch Employees Sheet": {
      "main": [
        [
          {
            "node": "\ud83d\udd0d Filter \u2014 Resigned or Terminated Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u26a0\ufe0f Split \u2014 Isolate Overdue Assets": {
      "main": [
        [
          {
            "node": "\ud83d\udea9 Google Sheets \u2014 Mark Assets as Missing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd17 Merge Employee Profile & Asset List": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 MCTS \u2014 Plan Asset Return Timeline",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 MCTS \u2014 Plan Asset Return Timeline": {
      "main": [
        [
          {
            "node": "\u2705 Self-Critique \u2014 Validate Return Checklist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u270d\ufe0f Build Return Instructions Email Body": {
      "main": [
        [
          {
            "node": "\ud83d\udce7 Gmail \u2014 Send Return Instructions to Employee",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0d Filter \u2014 Resigned or Terminated Only": {
      "main": [
        [
          {
            "node": "\u2753 Has Eligible Rows?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udea9 Google Sheets \u2014 Mark Assets as Missing": {
      "main": [
        [
          {
            "node": "\ud83d\udd04 DPO \u2014 Build Escalation Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\uddc4\ufe0f Google Sheets \u2014 Read Employee Assets": {
      "main": [
        [
          {
            "node": "\ud83d\udd17 Merge Employee Profile & Asset List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2705 Self-Critique \u2014 Validate Return Checklist": {
      "main": [
        [
          {
            "node": "\u2753 Checklist Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce7 Gmail \u2014 Send Return Instructions to Employee": {
      "main": [
        [
          {
            "node": "\ud83d\udcdd Google Sheets \u2014 Update Employee Return Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcdd Google Sheets \u2014 Update Employee Return Status": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Split \u2014 One Item Per Asset",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}