Restaurant ordering AI agent on WhatsApp

Takes delivery orders on WhatsApp from your menu, confirms the total and sends the order to the kitchen.

AI agentAdvanced Saves ~30 hours/month AI AgentsBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: WhatsApp — a customer sends a message
  1. A customer orders on WhatsApp.
  2. The agent checks the menu for items, sizes, prices and availability.
  3. It repeats the full order, address and total, then saves it.
  4. It sends the order to the kitchen's Telegram group.

What it can do

Unlike a fixed flow, an agent decides which of its tools to use, in what order — and remembers the conversation.

  • Takes orders in Arabic or English from your live menu sheet
  • Suggests alternatives when an item is unavailable
  • Adds delivery fees by area and confirms the total
  • Sends confirmed orders straight to the kitchen

System prompt

The instructions that shape the agent. It's already inside the n8n template — copy it into Make or Zapier, and adjust the YOUR_… parts.

System prompt
You are the WhatsApp ordering assistant for YOUR_RESTAURANT. Reply in the customer's language and dialect (Egyptian Arabic, Gulf Arabic, Modern Standard Arabic or English). Be warm and brief.

Today is {{ $now.toFormat('cccc HH:mm') }}. Opening hours: YOUR_HOURS. Delivery areas and fees: YOUR_DELIVERY_AREAS.

Tools:
- "Menu": our menu (item, size, price, available). Always check it for items and prices.
- "Place order": saves the confirmed order. Use only after the customer confirms items, quantities, address, phone and total.
- "Send to kitchen": sends the confirmed order to the kitchen group.

Rules:
- Never invent dishes, prices, offers or delivery times. If an item is unavailable, suggest a similar one from the menu.
- Repeat the full order with prices and the total (including delivery fee) before placing it.
- Payment is cash on delivery or YOUR_PAYMENT_METHODS — never ask for card details in chat.
- Ask about allergies only if the customer mentions them; never make health claims.

Safety rules

Built into the prompt and the tools:

  • Never invents dishes, prices or offers
  • Repeats the full order before placing it
  • Never asks for card details; makes no health claims

Uses your own AI key (Claude, OpenAI or another model). Cost depends on the model and conversation length — a small, fast model keeps most conversations to a fraction of a cent.

Diagram

WIncoming messageWhatsApp Trigger{ }Get questionCodeOrdering agentAI AgentClaude modelAnthropic Chat Mo…◷MemorySimple MemoryGMenuGoogle Sheets ToolGPlace orderGoogle Sheets ToolTSend to kitchenTelegram ToolWReply on WhatsAppWhatsApp Business Cloud
Trigger App Code AI Agent tool

Code

JavaScript used in the Code step — already included in the template below.

Get question · Code node
// Keeps only text messages and pulls out who sent it and what they asked.
const msg = $input.first().json.messages?.[0];
if (!msg || msg.type !== "text") return [];

return [{ json: { from: msg.from, question: msg.text.body } }];

Build it

Pick your tool — same automation, step by step.

Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder. AI steps use a Claude model node — swap it for OpenAI, Gemini or Ollama if you prefer.

Workflow JSON
Show code (296 lines)
{
  "name": "Restaurant ordering AI agent on WhatsApp (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## Restaurant ordering AI agent on WhatsApp\nTakes delivery orders on WhatsApp from your menu, confirms the total and sends the order to the kitchen.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
        "height": 260,
        "width": 340
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "updates": [
          "messages"
        ]
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Incoming message",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "webhookId": "47157a09-d134-4c79-a014-3d2a8816de5a"
    },
    {
      "parameters": {
        "jsCode": "// Keeps only text messages and pulls out who sent it and what they asked.\nconst msg = $input.first().json.messages?.[0];\nif (!msg || msg.type !== \"text\") return [];\n\nreturn [{ json: { from: msg.from, question: msg.text.body } }];"
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Get question",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        300,
        0
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.question }}",
        "options": {
          "systemMessage": "=You are the WhatsApp ordering assistant for YOUR_RESTAURANT. Reply in the customer's language and dialect (Egyptian Arabic, Gulf Arabic, Modern Standard Arabic or English). Be warm and brief.\n\nToday is {{ $now.toFormat('cccc HH:mm') }}. Opening hours: YOUR_HOURS. Delivery areas and fees: YOUR_DELIVERY_AREAS.\n\nTools:\n- \"Menu\": our menu (item, size, price, available). Always check it for items and prices.\n- \"Place order\": saves the confirmed order. Use only after the customer confirms items, quantities, address, phone and total.\n- \"Send to kitchen\": sends the confirmed order to the kitchen group.\n\nRules:\n- Never invent dishes, prices, offers or delivery times. If an item is unavailable, suggest a similar one from the menu.\n- Repeat the full order with prices and the total (including delivery fee) before placing it.\n- Payment is cash on delivery or YOUR_PAYMENT_METHODS — never ask for card details in chat.\n- Ask about allergies only if the customer mentions them; never make health claims."
        }
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Ordering agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        600,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5",
          "mode": "list"
        },
        "options": {}
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Claude model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        260,
        220
      ]
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Get question').item.json.from }}",
        "contextWindowLength": 10
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        430,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Returns our menu: item, size, price, available.",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Menu",
          "mode": "name"
        },
        "options": {}
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Menu",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        600,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Saves a confirmed order.",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Orders",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "time": "={{ $now.toISO() }}",
            "phone": "={{ $('Get question').item.json.from }}",
            "items": "={{ $fromAI('items', \"Items with sizes and quantities\", 'string') }}",
            "total": "={{ $fromAI('total', \"Total including delivery fee\", 'number') }}",
            "address": "={{ $fromAI('address', \"Delivery address\", 'string') }}",
            "notes": "={{ $fromAI('notes', \"Special requests, or empty\", 'string') }}",
            "status": "new"
          }
        },
        "options": {}
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Place order",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        770,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Sends the confirmed order to the kitchen's Telegram group.",
        "chatId": "YOUR_KITCHEN_CHAT_ID",
        "text": "={{ $fromAI('order', \"The full order: items, quantities, address, phone, total, notes\", 'string') }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Send to kitchen",
      "type": "n8n-nodes-base.telegramTool",
      "typeVersion": 1.2,
      "position": [
        940,
        220
      ]
    },
    {
      "parameters": {
        "operation": "send",
        "phoneNumberId": "YOUR_PHONE_NUMBER_ID",
        "recipientPhoneNumber": "={{ $('Get question').item.json.from }}",
        "textBody": "={{ $json.output }}"
      },
      "id": "47157a09-d134-4c79-a014-3d2a8816de5a",
      "name": "Reply on WhatsApp",
      "type": "n8n-nodes-base.whatsApp",
      "typeVersion": 1,
      "position": [
        900,
        0
      ]
    }
  ],
  "connections": {
    "Incoming message": {
      "main": [
        [
          {
            "node": "Get question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get question": {
      "main": [
        [
          {
            "node": "Ordering agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ordering agent": {
      "main": [
        [
          {
            "node": "Reply on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude model": {
      "ai_languageModel": [
        [
          {
            "node": "Ordering agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Memory": {
      "ai_memory": [
        [
          {
            "node": "Ordering agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Menu": {
      "ai_tool": [
        [
          {
            "node": "Ordering agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Place order": {
      "ai_tool": [
        [
          {
            "node": "Ordering agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Send to kitchen": {
      "ai_tool": [
        [
          {
            "node": "Ordering agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • Mark sold-out items as unavailable in the Menu sheet during service.

Related flows