AutomationFlows β€Ί AI & RAG β€Ί Autonomous It Asset Recovery & Offboarding Agent

Autonomous It Asset Recovery & Offboarding Agent

ByRahul Joshi @rahul08βœ“ on n8n.io

πŸ“Š Description Automate the entire IT asset recovery and employee offboarding process with this AI-powered n8n workflow. πŸ“‹πŸ’» This automation monitors Google Sheets for resigned or terminated employees, validates assigned company assets, sends personalized return instructions via…

Event triggerβ˜…β˜…β˜…β˜…β˜† complexity29 nodesGoogle Sheets TriggerGoogle SheetsGmailSlack
AI & RAG Trigger: Event Nodes: 29 Complexity: β˜…β˜…β˜…β˜…β˜† Added:

This workflow corresponds to n8n.io template #15690 β€” we link there as the canonical source.

This workflow follows the Gmail β†’ Google Sheets 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": "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
          }
        ]
      ]
    }
  }
}

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

πŸ“Š Description Automate the entire IT asset recovery and employee offboarding process with this AI-powered n8n workflow. πŸ“‹πŸ’» This automation monitors Google Sheets for resigned or terminated employees, validates assigned company assets, sends personalized return instructions via…

Source: https://n8n.io/workflows/15690/ β€” 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

Automate end-to-end vendor onboarding workflows with an AI-powered autonomous agent built in n8n πŸ€–. This workflow uses advanced MCTS (Monte Carlo Tree Search) reasoning to intelligently map procuremen

Agent, OpenAI Chat, Google Drive +4
AI & RAG

Automate your entire supplier negotiation process with this AI-driven workflow that intelligently drafts, refines, and sends negotiation emails πŸ“§. Using advanced LLM reasoning and multi-angle strategi

Google Sheets Trigger, Gmail, Google Sheets +4
AI & RAG

Consultants, agencies, freelancers, and project managers who want to ensure proposals, emails, and tasks are followed up on time.

Google Sheets Trigger, Agent, OpenAI Chat +6
AI & RAG

This workflow automatically monitors a Google Sheet for failed trades, analyzes each failure using AI and updates the sheet with a probable root cause, confidence level and actionable next steps. It a

Google Sheets Trigger, Agent, Google Gemini Chat +4
AI & RAG

This n8n workflow automates the transition from raw financial trade data to professional client communication. It monitors a Google Sheet for portfolio changes, uses Gemini AI to draft a personalized,

Google Sheets Trigger, Agent, Google Gemini Chat +3