AutomationFlowsAI & RAG › Qsr Inventory Reorder Alert (pos Export to Manager Approval)

Qsr Inventory Reorder Alert (pos Export to Manager Approval)

QSR Inventory Reorder Alert (POS Export to Manager Approval). Uses readWriteFile, openAi, slack, emailSend. Event-driven trigger; 11 nodes.

Event trigger★★★★☆ complexityAI-powered11 nodesRead Write FileOpenAISlackEmail Send
AI & RAG Trigger: Event Nodes: 11 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Emailsend → OpenAI 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
{
  "name": "QSR Inventory Reorder Alert (POS Export to Manager Approval)",
  "nodes": [
    {
      "parameters": {},
      "id": "trigger-manual",
      "name": "Run Once (manual)",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        120,
        220
      ],
      "notes": "Demo entry point. In production, swap for the Schedule Trigger below."
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24,
              "triggerAtHour": 9,
              "triggerAtMinute": 0
            }
          ]
        }
      },
      "id": "trigger-schedule",
      "name": "Daily 09:00 Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        120,
        380
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "fileSelector": "/var/qsr/inbox/{{ $now.format('yyyy_MM_dd') }}_inventory.csv",
        "options": {}
      },
      "id": "read-file",
      "name": "Read Daily POS Export",
      "type": "n8n-nodes-base.readWriteFile",
      "typeVersion": 1,
      "position": [
        360,
        300
      ],
      "notes": "Manager drops the POS export here. We chose file-drop over POS API write-access so we cannot break the live POS. For the demo, point this at the sample_pos_export.csv on your machine."
    },
    {
      "parameters": {
        "operation": "csv",
        "options": {
          "headerRow": true
        }
      },
      "id": "extract-csv",
      "name": "Parse CSV Rows",
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        580,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Aggregate all parsed rows into a single payload for the LLM.\nconst rows = $input.all().map(i => i.json);\nreturn [{ json: { rows } }];"
      },
      "id": "code-aggregate",
      "name": "Aggregate rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        800,
        300
      ]
    },
    {
      "parameters": {
        "resource": "chat",
        "operation": "message",
        "modelId": {
          "__rl": true,
          "value": "gpt-4o-mini",
          "mode": "list"
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You normalize messy QSR POS inventory rows. Input is JSON: { rows: [...] } where each row has sku_raw, item_name_raw, uom, on_hand, par_level. Output a JSON array {canonical_sku, canonical_name, on_hand_units, par_units, deficit, action_required}. Rules: (1) Treat 'Napkins 500pk', 'Napkin 500-count', 'NPK-500', 'NAP500' as the same canonical SKU; collapse duplicates by summing on_hand. (2) Convert uom to a single canonical unit per item. (3) If on_hand or par_level is missing on ANY of the duplicate rows, set action_required='manager_input_needed'. (4) deficit = max(0, par_units - on_hand_units). (5) action_required='reorder' if deficit > 0 AND no missing input, else 'ok'. Return ONLY valid JSON. No prose, no code fences."
            },
            {
              "role": "user",
              "content": "={{ JSON.stringify($json.rows) }}"
            }
          ]
        },
        "options": {
          "temperature": 0
        }
      },
      "id": "openai-normalize",
      "name": "AI Normalize + Deficit Calc",
      "type": "n8n-nodes-base.openAi",
      "typeVersion": 1.8,
      "position": [
        1020,
        300
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const raw = ($input.first().json.message?.content) || $input.first().json.text || $input.first().json.output || '[]';\nlet rows;\ntry { rows = JSON.parse(raw); } catch(e) {\n  const m = raw.match(/\\[[\\s\\S]*\\]/);\n  rows = m ? JSON.parse(m[0]) : [];\n}\nconst reorder = rows.filter(r => r.action_required === 'reorder');\nconst needs_input = rows.filter(r => r.action_required === 'manager_input_needed');\nreturn [{ json: { reorder, needs_input, count_reorder: reorder.length, count_needs_input: needs_input.length } }];"
      },
      "id": "code-split",
      "name": "Split: Reorder vs Needs Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1240,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "has-reorder",
              "leftValue": "={{ $json.count_reorder }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "if-reorder",
      "name": "Anything to reorder?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1460,
        300
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "post",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "#qsr-reorder",
          "mode": "name"
        },
        "text": "=:warning: *Low Stock Alert* \u2014 {{ $json.count_reorder }} items below par.\n\n```\n{{ $json.reorder.map(r => `${r.canonical_name}: on_hand=${r.on_hand_units}, par=${r.par_units}, deficit=${r.deficit}`).join('\\n') }}\n```\n\nApprove to add to today's vendor draft: <APPROVE_LINK> \u00b7 Decline: <DECLINE_LINK>\n\n_This message is a draft only. No order will be placed until a manager clicks Approve._",
        "otherOptions": {}
      },
      "id": "slack-alert",
      "name": "Slack Alert (Manager Approval)",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1680,
        200
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fromEmail": "ops@example-qsr.com",
        "toEmail": "manager@example-qsr.com",
        "subject": "=QSR reorder draft \u2014 {{ $json.count_reorder }} items need approval",
        "emailFormat": "text",
        "text": "={{ $json.reorder.map(r => `- ${r.canonical_name}: on_hand=${r.on_hand_units}, par=${r.par_units}, reorder ${r.deficit}`).join('\\n') }}\n\nReply APPROVE to send to US Foods draft. Reply DECLINE to ignore.\nNo order is placed automatically.",
        "options": {}
      },
      "id": "email-alert",
      "name": "Email Alert (fallback)",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1680,
        360
      ],
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// HUMAN-IN-THE-LOOP CHECKPOINT.\n// This workflow intentionally STOPS here. It never writes to the POS, never confirms\n// a vendor order, never moves money. A manager must approve via the Slack button\n// or email reply. Approval triggers a separate workflow (out of scope for this demo)\n// that drafts the vendor order.\nreturn $input.all();"
      },
      "id": "code-stop",
      "name": "Human Approval Required (Stop)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1900,
        280
      ]
    }
  ],
  "connections": {
    "Run Once (manual)": {
      "main": [
        [
          {
            "node": "Read Daily POS Export",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily 09:00 Trigger": {
      "main": [
        [
          {
            "node": "Read Daily POS Export",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Daily POS Export": {
      "main": [
        [
          {
            "node": "Parse CSV Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse CSV Rows": {
      "main": [
        [
          {
            "node": "Aggregate rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate rows": {
      "main": [
        [
          {
            "node": "AI Normalize + Deficit Calc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Normalize + Deficit Calc": {
      "main": [
        [
          {
            "node": "Split: Reorder vs Needs Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split: Reorder vs Needs Input": {
      "main": [
        [
          {
            "node": "Anything to reorder?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anything to reorder?": {
      "main": [
        [
          {
            "node": "Slack Alert (Manager Approval)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email Alert (fallback)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Human Approval Required (Stop)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Alert (Manager Approval)": {
      "main": [
        [
          {
            "node": "Human Approval Required (Stop)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Alert (fallback)": {
      "main": [
        [
          {
            "node": "Human Approval Required (Stop)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    "qsr",
    "inventory",
    "human-in-the-loop",
    "openclaw"
  ],
  "meta": {
    "n8n_min_version": "1.50.0",
    "version_notes": "Demo workflow. Manual or daily-schedule trigger -> read CSV from disk -> AI SKU normalization -> deficit math -> manager-approval alert (Slack + email). No POS writes, no auto-orders. Replace REPLACE_WITH_YOUR_*_CRED_ID with your own n8n credential IDs."
  }
}

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

QSR Inventory Reorder Alert (POS Export to Manager Approval). Uses readWriteFile, openAi, slack, emailSend. Event-driven trigger; 11 nodes.

Source: https://github.com/IgorGanapolsky/qsr-n8n-workflow-vault-site/blob/50ff02fd3f52f794f97d91889ee6bd3f6f5c45c5/demo/inventory_reorder_alert_n8n.json — 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

Description This workflow automates a personalized pre-arrival guest experience for hotels by combining Google Sheets, OpenAI, Email, and Slack. It detects upcoming check-ins, maintains unified guest

OpenAI, Email Send, Error Trigger +2
AI & RAG

Businesses using Jotform to collect customer feedback who want to automate sentiment analysis, email responses, and internal reporting — especially eCommerce, support, or CX teams looking to scale wit

Jot Form Trigger, OpenAI, Email Send +2
AI & RAG

Grain Real Estate Land Showcase v1. Uses formTrigger, httpRequest, openAi, emailSend. Event-driven trigger; 13 nodes.

Form Trigger, HTTP Request, OpenAI +3
AI & RAG

Complete AI-powered sales system Automates lead capture, qualification, and follow-up from multiple channels. AI INTELLIGENCE:

Gmail Trigger, Google Sheets, OpenAI +3
AI & RAG

Goal: This workflow demonstrates the full fluidX THE EYE integration — starting a live session, inviting both the customer (via SMS) and the service agent (via email), and then accessing the media (ph

Form Trigger, Google Drive, Email Send +3