AutomationFlowsEmail & Gmail › Manage Inventory Reorders with Google Sheets, Slack, and Gmail

Manage Inventory Reorders with Google Sheets, Slack, and Gmail

Byiamvaar @iamvaar on n8n.io

This workflow monitors inventory from Google Sheets on a schedule, calculates reorder quantities using vendor lead times, logs pending reorders back to Google Sheets, and posts grouped reorder requests to Slack, then sends vendor reorder emails via Gmail when an authorized Slack…

Cron / scheduled trigger★★★★★ complexity31 nodesGoogle SheetsSlackSlack TriggerGmail
Email & Gmail Trigger: Cron / scheduled Nodes: 31 Complexity: ★★★★★ Added:
Manage Inventory Reorders with Google Sheets, Slack, and Gmail — n8n workflow card showing Google Sheets, Slack, Slack Trigger integration

This workflow corresponds to n8n.io template #16473 — 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": "6uBlcsNurf1OeI3R",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automated Inventory Reorder & Vendor Notification Pipeline",
  "tags": [],
  "nodes": [
    {
      "id": "d009c30d-067d-48b0-8b92-a7e556c10f3c",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12032,
        992
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Automated Inventory Reorder & Vendor Notification Pipeline\n### How it works\n\n1. The workflow pulls stock and vendor data from Google Sheets on a schedule.\n2. It processes and merges this data, filtering for necessary reorders.\n3. Reorder information is aggregated, updated in a Google Sheet, and pushed to Slack.\n4. A secondary Slack-triggered flow handles customer queries by emailing internal users and updating a spreadsheet log.\n\n### Setup steps\n\n- - [ ] Configure Google Sheets credentials for all sheet nodes.\n- - [ ] Set up a Slack bot token and connect it to the Slack trigger/notification nodes.\n- - [ ] Link your Gmail account for the email notification node.\n- - [ ] Map the specific sheet IDs and ranges for all Google Sheets integrations.\n\n### Customization\n\nUsers can modify the reorder threshold logic in the merge nodes or adjust the Slack channel IDs for different notification routing."
      },
      "typeVersion": 1
    },
    {
      "id": "f1c4e530-a110-42b5-978c-2c83f4961889",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12592,
        1008
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 464,
        "content": "## Initiate and fetch sheets\n\nTrigger and data retrieval from Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "be0bd4fd-1bc2-47e7-9037-ae5c5b27bf9d",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13040,
        992
      ],
      "parameters": {
        "color": 7,
        "height": 496,
        "content": "## Format source data\n\nFormatting data from product and vendor sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "03861f00-b76c-4834-a666-0c190f261b08",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13312,
        1104
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 272,
        "content": "## Merge and evaluate conditions\n\nMerging and initial processing logic."
      },
      "typeVersion": 1
    },
    {
      "id": "00c293b4-1819-4719-9988-760f708f006f",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13984,
        1072
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Prepare data for aggregation\n\nStandardizing and aggregating data for reorders."
      },
      "typeVersion": 1
    },
    {
      "id": "f5337a58-8b3f-4d3b-aa24-1a8b9696bd56",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        14432,
        1072
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 304,
        "content": "## Fetch and process reorders\n\nFetching reorder sheet and processing updates."
      },
      "typeVersion": 1
    },
    {
      "id": "fd130145-edef-4038-a2b3-6540fee3e800",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        14864,
        1088
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 272,
        "content": "## Update records and notify Slack\n\nWriting reorder updates and notifying Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "a6ac2d9d-6cea-4b56-bf51-598684bf20a6",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12592,
        1648
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 272,
        "content": "## Slack message trigger flow\n\nSlack event trigger and filtering."
      },
      "typeVersion": 1
    },
    {
      "id": "a9416ccc-fda4-4fff-b560-e0e75daa8f87",
      "name": "Sticky Note17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13040,
        1648
      ],
      "parameters": {
        "color": 7,
        "width": 1088,
        "height": 288,
        "content": "## Email notification and status update\n\nFetching context and sending email notifications."
      },
      "typeVersion": 1
    },
    {
      "id": "740499fd-50d9-4e21-9eba-ed8cf6745061",
      "name": "When Scheduled",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        12640,
        1216
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "c329ac19-9792-43b3-bbbd-ad1c6188bfa0",
      "name": "Fetch Product Inventory",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        12864,
        1120
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "581dac77-6b11-471c-93c0-14199adb52da",
      "name": "Fetch Vendor Catalog",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        12864,
        1312
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "370c40db-20a6-4504-8915-a0250f046f5c",
      "name": "Prepare Product Stock Data",
      "type": "n8n-nodes-base.set",
      "position": [
        13088,
        1120
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "86fd3392-4557-4516-b8cb-a832612e185c",
              "name": "row_number",
              "type": "number",
              "value": "={{ $json.row_number }}"
            },
            {
              "id": "a2587fd9-4452-46fc-ba40-c50e16244914",
              "name": "Product ID",
              "type": "string",
              "value": "={{ $json['Product ID'] }}"
            },
            {
              "id": "663e9b28-57ad-41a3-bea3-62787635da6d",
              "name": "Vendor ID",
              "type": "string",
              "value": "={{ $json['Vendor ID'] }}"
            },
            {
              "id": "6abbbcc7-0f70-4411-9742-dbefa0d34411",
              "name": "Product Name",
              "type": "string",
              "value": "={{ $json['Product Name'] }}"
            },
            {
              "id": "dabadaff-d6d2-4899-9c16-cf33f410b83f",
              "name": "Current Stock",
              "type": "number",
              "value": "={{ $json['Current Stock'] }}"
            },
            {
              "id": "d11cb88b-12b1-4c09-adc9-f0d6ad0c5d28",
              "name": "Last 7 Days Sales",
              "type": "number",
              "value": "={{ $json['Last 7 Days Sales'] }}"
            },
            {
              "id": "065d6fc7-5275-4797-a80f-de07bb218f91",
              "name": "Current Run away",
              "type": "string",
              "value": "={{ Math.round($json['Current Stock'] / ($json['Last 7 Days Sales'] / 7)) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "18642a3b-da72-4154-8342-b2a09e09b770",
      "name": "Prepare Vendor Delivery Data",
      "type": "n8n-nodes-base.set",
      "position": [
        13088,
        1312
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "373219f8-4e74-48aa-93a6-def3f227a974",
              "name": "Vendor ID",
              "type": "string",
              "value": "={{ $json[\"Vendor ID\"] }}"
            },
            {
              "id": "bf04d7cc-1012-433c-8600-a4c5a662058e",
              "name": "Vendor Name",
              "type": "string",
              "value": "={{ $json[\"Vendor Name\"] }}"
            },
            {
              "id": "179185f3-fc1e-4d9d-b926-6327e758dc99",
              "name": "Email Address",
              "type": "string",
              "value": "={{ $json[\"Email Address\"] }}"
            },
            {
              "id": "8df7b4e0-a31e-4796-9b82-b0e7a49f975d",
              "name": "Avg Delivery Time",
              "type": "string",
              "value": "={{ $json[\"Avg. Delivery Time\"] }}"
            },
            {
              "id": "7cdf33f9-0697-4f00-8c1d-f800e13571d2",
              "name": "Offered Product IDs",
              "type": "string",
              "value": "={{ $json[\"Offered Product IDs\"] }}"
            },
            {
              "id": "b7cb475c-8e7f-484b-a2f6-1da2631d39eb",
              "name": "Product Descriptions",
              "type": "string",
              "value": "={{ $json[\"Product Descriptions\"] }}"
            },
            {
              "id": "fd3d8b46-d131-4441-9194-b7f29c387f7a",
              "name": "Safe Hold time",
              "type": "string",
              "value": "={{ $json[\"Avg. Delivery Time\"] +2 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f5ef58e1-b121-4970-acb5-e3b0414c28a0",
      "name": "Merge Inventory and Vendors",
      "type": "n8n-nodes-base.merge",
      "position": [
        13360,
        1216
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "fieldsToMatchString": "[\"Vendor ID\"]"
      },
      "typeVersion": 3.2
    },
    {
      "id": "cadb68e2-a9fa-4b79-9ed5-2e15583169db",
      "name": "Calculate Reorder Needs",
      "type": "n8n-nodes-base.code",
      "position": [
        13584,
        1216
      ],
      "parameters": {
        "jsCode": "for (const item of $input.all()) {\n  const json = item.json;\n  const currentStock = Number(json['Current Stock']) || 0;\n  const last7DaysSales = Number(json['Last 7 Days Sales']) || 0;\n  const dailySales = last7DaysSales / 7;\n  const rawDeliveryTime = json['Avg Delivery Time'] || json['Avg. Delivery Time'] || json['Avg. Delivery Time '] || \"0\";\n  const leadTime = parseFloat(rawDeliveryTime.toString().replace(/[^0-9.]/g, '')) || 0;\n  const dynamicBuffer = Math.max(2, Math.min(14, Math.round(14 * dailySales)));\n  const targetStock = (leadTime + dynamicBuffer) * dailySales;\n  json['Reorder Quantity'] = Math.max(0, Math.ceil(targetStock - currentStock));\n  json['Debug - Clean Lead Time'] = leadTime;\n  json['Debug - Daily Sales'] = Number(dailySales.toFixed(2));\n  json['Debug - Target Stock'] = Number(targetStock.toFixed(2));\n}\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "d6cdc09f-ec03-4df7-b20f-e385f2a82c31",
      "name": "If Reorder Needed",
      "type": "n8n-nodes-base.if",
      "position": [
        13808,
        1216
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2ce6c0d9-377e-419f-93b0-3df022321c89",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json['Reorder Quantity'] }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "cf506143-d1c6-4d7b-8d39-bada509b11c8",
      "name": "Format Reorder Data",
      "type": "n8n-nodes-base.set",
      "position": [
        14032,
        1200
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fca8dec5-b0e4-4a6a-a574-675c9f0cb571",
              "name": "row_number",
              "type": "number",
              "value": "={{ $json.row_number }}"
            },
            {
              "id": "d2af2257-3f80-4242-ac23-9cedf24f8d85",
              "name": "product_string",
              "type": "string",
              "value": "={{ $json[\"Product ID\"] }} ({{ $json[\"Product Name\"] }})"
            },
            {
              "id": "a523512c-4a01-4449-8a5f-e736a9a49568",
              "name": "Product ID",
              "type": "string",
              "value": "={{ $json[\"Product ID\"] }}"
            },
            {
              "id": "0032b7a8-13d6-48ef-a594-054faadefa22",
              "name": "Vendor ID",
              "type": "string",
              "value": "={{ $json[\"Vendor ID\"] }}"
            },
            {
              "id": "b509b378-b9bd-4482-beb5-9f80bef827a9",
              "name": "Product Name",
              "type": "string",
              "value": "={{ $json[\"Product Name\"] }}"
            },
            {
              "id": "94bb26ad-f6fd-460f-8ece-d7ebaad9a61b",
              "name": "Current Stock",
              "type": "number",
              "value": "={{ $json[\"Current Stock\"] }}"
            },
            {
              "id": "0a65ae84-3154-4bcf-8c5f-4c0a8fcd1075",
              "name": "Last 7 Days Sales",
              "type": "number",
              "value": "={{ $json[\"Last 7 Days Sales\"] }}"
            },
            {
              "id": "aac91f83-4484-43c4-8115-75b9893dedac",
              "name": "Current Run away",
              "type": "string",
              "value": "={{ $json[\"Current Run away\"] }}"
            },
            {
              "id": "9f9fa635-41a0-4678-8038-d94f6ae27aee",
              "name": "Vendor Name",
              "type": "string",
              "value": "={{ $json[\"Vendor Name\"] }}"
            },
            {
              "id": "7d51b3cc-48c8-4709-9df7-d90aee0f9c87",
              "name": "Email Address",
              "type": "string",
              "value": "={{ $json[\"Email Address\"] }}"
            },
            {
              "id": "a9427f50-7044-4114-9788-ed5341ae5b9b",
              "name": "Avg Delivery Time",
              "type": "string",
              "value": "={{ $json[\"Avg Delivery Time\"] }}"
            },
            {
              "id": "56e7b1cc-6f3f-4fd8-b23b-69ba45fd26ea",
              "name": "Offered Product IDs",
              "type": "string",
              "value": "={{ $json[\"Offered Product IDs\"] }}"
            },
            {
              "id": "0ae28021-3065-48dc-8cea-93b3ff4834c9",
              "name": "Product Descriptions",
              "type": "string",
              "value": "={{ $json[\"Product Descriptions\"] }}"
            },
            {
              "id": "735f0d2e-4677-4dfa-a9af-82f82297d1e8",
              "name": "Safe Hold time",
              "type": "string",
              "value": "={{ $json[\"Safe Hold time\"] }}"
            },
            {
              "id": "64d0c84c-6a4e-4e55-b132-220c477b34bb",
              "name": "Reorder Quantity",
              "type": "number",
              "value": "={{ $json[\"Reorder Quantity\"] }}"
            },
            {
              "id": "c1a8256d-a53f-49f3-9ceb-3350374cf1d1",
              "name": "Debug - Clean Lead Time",
              "type": "number",
              "value": "={{ $json[\"Debug - Clean Lead Time\"] }}"
            },
            {
              "id": "8205d1b8-03d2-450a-98dc-60484f36301c",
              "name": "Debug - Daily Sales",
              "type": "number",
              "value": "={{ $json[\"Debug - Daily Sales\"] }}"
            },
            {
              "id": "977988c9-066e-4271-b7f7-e898d89258fa",
              "name": "Debug - Target Stock",
              "type": "number",
              "value": "={{ $json[\"Debug - Target Stock\"] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b6fadeca-ca34-4315-8001-d78f24be77a8",
      "name": "Aggregate Reorder Items",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        14256,
        1200
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "a3ffd1e6-8812-4c25-82ae-a671a5ab1959",
      "name": "Fetch Existing Reorders",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        14480,
        1200
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "5d22c97f-a638-48c7-9153-896d0e8e635f",
      "name": "Filter Reorder Records",
      "type": "n8n-nodes-base.code",
      "position": [
        14688,
        1200
      ],
      "parameters": {
        "jsCode": "// --- PHASE 1: DATA GATHERING & FILTERING ---\n\n// 1. Grab the historical data (flowing directly into this node)\nconst orderHistory = $input.all(); \n\n// 2. Reach back to grab the fresh low-stock alerts\n// IMPORTANT: Ensure \"Edit Fields1\" matches the exact name of your node!\nconst incomingOrders = $items(\"Format Reorder Data\"); \n\nconst COOLDOWN_DAYS = 14;\nconst now = new Date();\nconst itemsToOrder = [];\n\n// 3. Loop through low stock items to filter out ineligible ones\nfor (const item of incomingOrders) {\n    const productString = item.json.product_string; \n\n    const recentlyOrdered = orderHistory.some(historyItem => {\n        // If the product strings don't match, skip to the next row\n        if (historyItem.json.product_string !== productString) return false;\n\n        // Make sure we are reading the status in lowercase just to be safe\n        const currentStatus = (historyItem.json.status || \"\").toLowerCase();\n\n        // SHIELD 1: The Status Check\n        // If the order is currently pending or sent, do not reorder it!\n        if (currentStatus === 'pending' || currentStatus === 'sent') {\n            return true; // Flag as \"recently ordered\" to block it\n        }\n\n        // SHIELD 2: The 14-Day Date Check\n        // If it is completely finished, check if it was ordered recently\n        if (currentStatus === 'completed' && historyItem.json.reorder_date) {\n            const orderDate = new Date(historyItem.json.reorder_date);\n            \n            // Safety check to ensure n8n can actually read the date format\n            if (!isNaN(orderDate.getTime())) {\n                const daysSinceOrder = (now - orderDate) / (1000 * 60 * 60 * 24);\n                return daysSinceOrder <= COOLDOWN_DAYS; \n            }\n        }\n\n        // If it's not pending/sent, and it's older than 14 days, let it through!\n        return false;\n    });\n\n    // If it passed both shields, push it to our final array to be ordered\n    if (!recentlyOrdered) {\n        itemsToOrder.push(item);\n    }\n}\n\n\n// --- PHASE 2: VENDOR GROUPING ---\n\nconst groupedVendors = {};\n\n// Iterate through the filtered, clean items to group them by Vendor\nitemsToOrder.forEach(item => {\n    const data = item.json;\n    const vendorId = data[\"Vendor ID\"];\n\n    // Initialize vendor group\n    if (!groupedVendors[vendorId]) {\n        groupedVendors[vendorId] = {\n            \"Vendor ID\": vendorId,\n            \"Vendor Name\": data[\"Vendor Name\"],\n            \"Email Address\": data[\"Email Address\"],\n            \"Products String\": [],\n            \"Total Reorder Quantity\": 0,\n            \"Structured Line Items\": []\n        };\n    }\n\n    // Use existing product_string if available, else build it\n    const productString =\n        data.product_string ||\n        `${data[\"Product ID\"]} (${data[\"Product Name\"]})`;\n\n    groupedVendors[vendorId][\"Products String\"].push(productString);\n\n    // Aggregate reorder quantity\n    groupedVendors[vendorId][\"Total Reorder Quantity\"] += Number(data[\"Reorder Quantity\"] || 0);\n\n    // Structured items\n    groupedVendors[vendorId][\"Structured Line Items\"].push({\n        \"Product ID\": data[\"Product ID\"],\n        \"Product Name\": data[\"Product Name\"],\n        \"Reorder Quantity\": data[\"Reorder Quantity\"],\n        \"Current Stock\": data[\"Current Stock\"]\n    });\n});\n\n\n// --- PHASE 3: FINAL OUTPUT FORMATTING ---\n\n// Convert back to n8n output format\nconst result = Object.values(groupedVendors).map(vendor => {\n    vendor[\"Products String\"] = vendor[\"Products String\"].join(\", \");\n\n    return {\n        json: vendor\n    };\n});\n\n// Output the clean, grouped list!\nreturn result;"
      },
      "typeVersion": 2
    },
    {
      "id": "0891948d-5fbd-48a2-a8eb-0ed5e6eb7b65",
      "name": "Prepare Reorder Payload",
      "type": "n8n-nodes-base.set",
      "position": [
        14912,
        1200
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2aea43e7-a433-47f8-b41b-f1b9c1c5e801",
              "name": "product_reorder_id",
              "type": "string",
              "value": "={{ 'p' + $now.valueOf() + Math.random().toString(36).substring(2, 6) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "966a9a53-a385-44d9-a42a-31ab8cfc3507",
      "name": "Upsert Reorder in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        15136,
        1200
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "pending",
            "Vendor ID": "={{ $('Filter Reorder Records').item.json[\"Vendor ID\"] }}",
            "Vendor Name": "={{ $('Filter Reorder Records').item.json[\"Vendor Name\"] }}",
            "reorder_date": "={{ $now }}",
            "Email Address": "={{ $('Filter Reorder Records').item.json[\"Email Address\"] }}",
            "product_string": "={{ $('Filter Reorder Records').item.json[\"Products String\"] }}",
            "product_reorder_id": "={{ 'p' + $now.valueOf() + Math.random().toString(36).substring(2, 6) }}",
            "Total Reorder Quantity": "={{ $('Filter Reorder Records').item.json[\"Total Reorder Quantity\"] }}",
            "products_with_quantity": "={{\n$('Filter Reorder Records').item.json[\"Structured Line Items\"]\n.map((item, index) => \n`${index + 1}. ${item[\"Product Name\"]}\n   Product ID: ${item[\"Product ID\"]}\n   Reorder Quantity: ${item[\"Reorder Quantity\"]}\n   Current Stock: ${item[\"Current Stock\"]}`\n)\n.join('\\n\\n')\n}}"
          },
          "schema": [
            {
              "id": "product_reorder_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "product_reorder_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Vendor ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Vendor ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Vendor Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Vendor Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email Address",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email Address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "product_string",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "product_string",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "products_with_quantity",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "products_with_quantity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Reorder Quantity",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Total Reorder Quantity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reorder_date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reorder_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "product_reorder_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k/edit?usp=drivesdk",
          "cachedResultName": "Product_Reorder_Data"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "35e32d58-c554-46dd-9ecf-421a326ca747",
      "name": "Post Reorder to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        15360,
        1200
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *Stock Restock Update Required* \ud83d\udea8\n\n\ud83c\udfe2 *Vendor:* {{ $json[\"Vendor Name\"] }}\n\ud83c\udd94 *Vendor ID:* {{ $json[\"Vendor ID\"] }}\n\ud83d\udce7 *Contact:* {{ $json[\"Email Address\"] }}\n\ud83d\udd16 *Reorder ID:* {{ $json[\"product_reorder_id\"] }}\n\n\ud83d\udce6 *Products Requiring Restock:*\n\n{{ $json[\"products_with_quantity\"] }}\n\n\ud83d\uded2 *Total Reorder Quantity:* *{{ $json[\"Total Reorder Quantity\"] }} units*\n\n\u26a0\ufe0f Stock levels for the above items require replenishment to maintain inventory availability.\n",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "445ab1e6-0aa9-4c39-81a2-ad441aedb809",
      "name": "When Slack Message Received",
      "type": "n8n-nodes-base.slackTrigger",
      "position": [
        12640,
        1760
      ],
      "parameters": {
        "options": {
          "resolveIds": false
        },
        "trigger": [
          "reaction_added"
        ],
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "73717657-b04f-40c6-b536-d32bd0be7b31",
      "name": "If Approval Came from the valid authority user?",
      "type": "n8n-nodes-base.if",
      "position": [
        12864,
        1760
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "54555b44-d132-4866-a1e2-7e9e96189fcb",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.user }}",
              "rightValue": "enter-your-authoritive-person-slack-id-here"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "6e3b9d74-f916-45bd-9135-f21b0cbe406c",
      "name": "Fetch Slack Thread Replies",
      "type": "n8n-nodes-base.slack",
      "position": [
        13088,
        1760
      ],
      "parameters": {
        "ts": "={{ $json.item.ts }}",
        "filters": {},
        "resource": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        },
        "operation": "replies"
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "f9fd3ba0-4515-445d-af28-bc897b7f6e29",
      "name": "Parse Slack Message Data",
      "type": "n8n-nodes-base.code",
      "notes": "// Extract the text from the incoming item\nconst inputText = $input.first().json.text;\n\n// Execute the regex match\nconst match = inputText.match(/ReorderID:\\*\\s*([^\\n_]+)/);\nconst reorderId = match ? match[1].trim() : null;\n\n// n8n requires returning an array of objects wrapped in a 'json' key\nreturn [\n  {\n    json: {\n      reorderId: reorderId\n    }\n  }\n];",
      "position": [
        13312,
        1760
      ],
      "parameters": {
        "jsCode": "// 1. Map over all incoming items safely\nreturn $input.all().map(item => {\n  const inputText = item.json.text || \"\";\n  \n  // 2. Extract the ID\n  const match = inputText.match(/Reorder ID:\\*\\s*([^\\n_]+)/);\n  const reorderId = match ? match[1].trim() : null;\n  \n  // 3. Return the structurally correct n8n object\n  return {\n    json: {\n      reorderId: reorderId\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "c308a08f-01bb-45f1-8817-975f47fae975",
      "name": "Get Reorder Sheet Row",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        13536,
        1760
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.reorderId }}",
              "lookupColumn": "product_reorder_id"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k/edit?usp=drivesdk",
          "cachedResultName": "Product_Reorder_Data"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "99134749-16ae-45af-83fb-abcebe671c80",
      "name": "Send Email via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        13760,
        1760
      ],
      "parameters": {
        "sendTo": "={{ $json[\"Email Address\"] }}",
        "message": "=<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Product Reorder Email</title>\n\n<style>\nbody {\n    margin: 0;\n    padding: 0;\n    background-color: #f5f7fb;\n    font-family: Arial, Helvetica, sans-serif;\n}\n\n.wrapper {\n    width: 100%;\n    padding: 40px 20px;\n}\n\n.container {\n    max-width: 650px;\n    margin: auto;\n    background: #ffffff;\n    border-radius: 14px;\n    overflow: hidden;\n    box-shadow: 0 8px 30px rgba(0,0,0,0.08);\n}\n\n.header {\n    background: #111827;\n    padding: 28px;\n    text-align: center;\n}\n\n.header h1 {\n    color: white;\n    margin: 0;\n    font-size: 24px;\n    font-weight: 600;\n}\n\n.content {\n    padding: 35px;\n    color: #374151;\n    line-height: 1.7;\n}\n\n.greeting {\n    font-size: 16px;\n    margin-bottom: 25px;\n}\n\n.info-box {\n    background: #f9fafb;\n    border: 1px solid #e5e7eb;\n    border-radius: 10px;\n    padding: 18px;\n    margin: 25px 0;\n}\n\n.info-row {\n    margin-bottom: 12px;\n}\n\n.label {\n    font-weight: 600;\n    color: #111827;\n}\n\n.products-box {\n    background: #f3f4f6;\n    border-radius: 10px;\n    padding: 18px;\n    border-left: 4px solid #111827;\n    margin-top: 20px;\n    white-space: pre-line;\n}\n\n.note {\n    margin-top: 30px;\n}\n\n.footer {\n    padding: 25px;\n    text-align: center;\n    border-top: 1px solid #e5e7eb;\n    color: #6b7280;\n    font-size: 13px;\n}\n</style>\n</head>\n\n<body>\n\n<div class=\"wrapper\">\n\n<div class=\"container\">\n\n    <div class=\"header\">\n        <h1>Inventory Reorder Request</h1>\n    </div>\n\n    <div class=\"content\">\n\n        <div class=\"greeting\">\n            Dear {{ $json[\"Vendor Name\"] }},\n        </div>\n\n        <p>\n            Please process the following product reorder for our inventory.\n        </p>\n\n        <div class=\"info-box\">\n\n            <div class=\"info-row\">\n                <span class=\"label\">Order Reference:</span>\n                {{ $json.product_reorder_id }}\n            </div>\n\n            <div class=\"info-row\">\n                <span class=\"label\">Total Items to Reorder:</span>\n                {{ $json[\"Total Reorder Quantity\"] }}\n            </div>\n\n        </div>\n\n        <div>\n            <span class=\"label\">Reorder Details</span>\n\n            <div class=\"products-box\">\n                {{ $json.products_with_quantity }}\n            </div>\n        </div>\n\n        <div class=\"note\">\n            Please confirm receipt of this order and provide an estimated delivery date.\n        </div>\n\n        <p style=\"margin-top:30px;\">\n            Thank you,<br>\n            <strong>Enter Your Company Name</strong>\n        </p>\n\n    </div>\n\n    <div class=\"footer\">\n        Inventory Management System \u2022 Enter-your-company-name-here\n    </div>\n\n</div>\n\n</div>\n\n</body>\n</html>",
        "options": {},
        "subject": "=Purchase Reorder Request.... {{$json.product_reorder_id}}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "fca4daa7-68b5-4ec1-8880-786796ce3fb1",
      "name": "Update Reorder Status in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        13984,
        1760
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "SENT",
            "reorder_date": "={{ $now }}",
            "product_reorder_id": "={{ $('Get Reorder Sheet Row').item.json.product_reorder_id }}"
          },
          "schema": [
            {
              "id": "product_reorder_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "product_reorder_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Vendor ID",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Vendor ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Vendor Name",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Vendor Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email Address",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Email Address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "product_string",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "product_string",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "products_with_quantity",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "products_with_quantity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Total Reorder Quantity",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Total Reorder Quantity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reorder_date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reorder_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "product_reorder_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1vGmcxlf5NTgyd0fKt7O5Qwbu4ijLsfAiG6Tg-C7w81k/edit?usp=drivesdk",
          "cachedResultName": "Product_Reorder_Data"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    }
  ],
  "active": true,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "e461f988-1676-491a-9942-e74530d2d7a3",
  "nodeGroups": [],
  "connections": {
    "When Scheduled": {
      "main": [
        [
          {
            "node": "Fetch Product Inventory",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Vendor Catalog",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Reorder Needed": {
      "main": [
        [
          {
            "node": "Format Reorder Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Reorder Data": {
      "main": [
        [
          {
            "node": "Aggregate Reorder Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Vendor Catalog": {
      "main": [
        [
          {
            "node": "Prepare Vendor Delivery Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Email via Gmail": {
      "main": [
        [
          {
            "node": "Update Reorder Status in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Reorder Sheet Row": {
      "main": [
        [
          {
            "node": "Send Email via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Reorder Records": {
      "main": [
        [
          {
            "node": "Prepare Reorder Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Reorder Items": {
      "main": [
        [
          {
            "node": "Fetch Existing Reorders",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Reorder Needs": {
      "main": [
        [
          {
            "node": "If Reorder Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Existing Reorders": {
      "main": [
        [
          {
            "node": "Filter Reorder Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Product Inventory": {
      "main": [
        [
          {
            "node": "Prepare Product Stock Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Reorder Payload": {
      "main": [
        [
          {
            "node": "Upsert Reorder in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Slack Message Data": {
      "main": [
        [
          {
            "node": "Get Reorder Sheet Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert Reorder in Sheets": {
      "main": [
        [
          {
            "node": "Post Reorder to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Slack Thread Replies": {
      "main": [
        [
          {
            "node": "Parse Slack Message Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Product Stock Data": {
      "main": [
        [
          {
            "node": "Merge Inventory and Vendors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Inventory and Vendors": {
      "main": [
        [
          {
            "node": "Calculate Reorder Needs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Slack Message Received": {
      "main": [
        [
          {
            "node": "If Approval Came from the valid authority user?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Vendor Delivery Data": {
      "main": [
        [
          {
            "node": "Merge Inventory and Vendors",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "If Approval Came from the valid authority user?": {
      "main": [
        [
          {
            "node": "Fetch Slack Thread Replies",
            "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 monitors inventory from Google Sheets on a schedule, calculates reorder quantities using vendor lead times, logs pending reorders back to Google Sheets, and posts grouped reorder requests to Slack, then sends vendor reorder emails via Gmail when an authorized Slack…

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

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

This workflow automatically scans AWS accounts for orphaned resources (unattached EBS volumes, old snapshots &gt;90 days, unassociated Elastic IPs) that waste money. It calculates cost impact, validat

Slack, Gmail, AWS Lambda +1
Email & Gmail

Streamline IT and operations change management by automating approval routing, Jira issue creation, audit logging, and real-time Slack alerts. This workflow ensures faster reviews, traceable approvals

Monday.com, Slack, Jira +2
Email & Gmail

Streamline IT and operations change management by automating approval routing, Jira issue creation, audit logging, and real-time Slack alerts. This workflow ensures faster reviews, traceable approvals

Monday.com, Slack, Jira +2
Email & Gmail

Automate your GoHighLevel (GHL) pipeline tracking and deal management process. This workflow fetches all opportunities, calculates the time spent in each stage, logs historical pipeline data in Google

High Level, Google Sheets, Gmail +1
Email & Gmail

Automatically consolidate Zendesk and Freshdesk ticket data into a unified performance dashboard with KPI calculations, Google Sheets logging, real-time Slack alerts, and weekly Gmail email reports. P

Slack, Gmail, Zendesk +2