Skip to main content

Interactive Menu Bot

Build a complete menu-driven bot using the Menu System nodes. This tutorial covers nested menus, pagination, search, multi-step forms, and confirmation dialogs.


What We're Building

A restaurant ordering bot with:

  • 📋 Main menu with categories
  • 🍕 Category menus with food items
  • 📄 Paginated lists for long menus
  • 🔍 Search to find dishes
  • ✍️ Order wizard with customer details
  • Confirmation before placing order

Part 1: Main Menu

Step 1.1: Create the Entry Point

  1. Add a Command Trigger node

    • Command: /order
  2. Add a menu.show node

    • Menu ID: main_menu
    • Title:
    🍽️ Welcome to FoodBot!

    Choose a category to browse:
  3. Create an Array node for menu items:

    [
    {"text": "🍕 Pizza", "callback_data": "category_pizza"},
    {"text": "🍔 Burgers", "callback_data": "category_burgers"},
    {"text": "🍜 Pasta", "callback_data": "category_pasta"},
    {"text": "🥗 Salads", "callback_data": "category_salads"},
    {"text": "🔍 Search", "callback_data": "search"},
    {"text": "🛒 View Cart", "callback_data": "view_cart"}
    ]
  4. Connect:

    • Command Trigger → menu.show
    • Array → menu.show's Items input
Result
🍽️ Welcome to FoodBot!

Choose a category to browse:

[🍕 Pizza] [🍔 Burgers]
[🍜 Pasta] [🥗 Salads]
[🔍 Search] [🛒 View Cart]

Step 1.2: Route Menu Selections

  1. Add a menu.router node after menu.show

  2. Configure routes:

    [
    {"pattern": "^category_(.+)$", "output": "Category", "params": ["category"]},
    {"pattern": "^search$", "output": "Search"},
    {"pattern": "^view_cart$", "output": "ViewCart"}
    ]
  3. Connect:

    • menu.show's Selected → menu.router's In

Now clicks are routed:

  • category_pizzaCategory output with Params.category = "pizza"
  • searchSearch output
  • view_cartViewCart output

Part 2: Category Menus (Paginated)

Step 2.1: Load Category Items

  1. Add a Get Variable node

    • Variable: global.menu_items
  2. Add a Filter node

    • Input: Menu items array
    • Expression: item.category == Params.category

This filters items to the selected category.

Step 2.2: Display Paginated List

  1. Add a menu.paginated_list node:

    • Menu ID: category_{{Params.category}}
    • Title:
    🍽️ {{upper(Params.category)}}

    Page {{page}} of {{total_pages}}
    • Page Size: 5
    • Item Template: {{emoji}} {{name}} - ${{price}}
    • Callback Template: item_{{id}}
  2. Connect:

    • Filtered items → Items input
    • Router's Category output → paginated_list
Result
🍽️ PIZZA

Page 1 of 3

[🍕 Margherita - $12.99]
[🍕 Pepperoni - $14.99]
[🍕 Hawaiian - $15.99]
[🍕 BBQ Chicken - $16.99]
[🍕 Veggie Supreme - $13.99]

[⬅️] [📄 1/3] [➡️]
[⬅️ Back]

Step 2.3: Handle Item Selection

  1. Add another menu.router for item selection:

    [
    {"pattern": "^item_(.+)$", "output": "ShowItem", "params": ["item_id"]}
    ]
  2. Connect paginated_list's Selected → router


Part 3: Item Details & Add to Cart

Step 3.1: Show Item Details

  1. Add a Filter to find the specific item:

    • Expression: item.id == Params.item_id
  2. Add a Send Message (or menu.show) with item details:

    {{SelectedItem.emoji}} {{SelectedItem.name}}

    📝 {{SelectedItem.description}}

    💰 Price: ${{SelectedItem.price}}
  3. Add buttons:

    [
    {"text": "🛒 Add to Cart", "callback_data": "add_{{SelectedItem.id}}"},
    {"text": "⬅️ Back to Menu", "callback_data": "back"}
    ]

Step 3.2: Add to Cart Flow

  1. Add a Get Variable: var.cart (default: [])

  2. Add a Transform to append item:

    • Expression: append(var.cart, SelectedItem)
  3. Add a Set Variable: var.cart = transformed array

  4. Add a menu.confirm:

    • Title: ✅ Added {{SelectedItem.name}} to cart!\n\nContinue shopping?
    • Yes Text: Keep Shopping
    • No Text: View Cart

Part 4: Search Functionality

Step 4.1: Create Search Menu

  1. Connect Router's Search output to a menu.search node:

    • Menu ID: food_search
    • Title: 🔍 Search our menu:
    • Search Fields: ["name", "description"]
    • Max Results: 5
    • Item Template: {{emoji}} {{name}} - ${{price}}
  2. Connect all menu items to the Items input

Step 4.2: Handle Search Results

  1. Connect Selected output to the same Item Details flow

  2. Connect Cancelled output back to Main Menu

Try It

User types "pepperoni" → sees matching items instantly!


Part 5: Order Wizard

Step 5.1: Create the Checkout Wizard

  1. Add a menu.wizard node:

    • Wizard ID: checkout
    • Steps:
    [
    {
    "id": "name",
    "title": "👤 What's your name?",
    "type": "text",
    "required": true,
    "validation": "len(value) >= 2",
    "error_message": "Name must be at least 2 characters"
    },
    {
    "id": "phone",
    "title": "📱 Your phone number:",
    "type": "text",
    "required": true,
    "validation": "len(value) >= 10",
    "error_message": "Please enter a valid phone number"
    },
    {
    "id": "delivery",
    "title": "🚗 Choose delivery type:",
    "type": "select",
    "options": [
    {"key": "delivery", "label": "🏠 Home Delivery (+$3)"},
    {"key": "pickup", "label": "🏃 Self Pickup (Free)"}
    ]
    },
    {
    "id": "address",
    "title": "📍 Delivery address:",
    "type": "text",
    "required": true,
    "validation": "len(value) >= 10",
    "error_message": "Please enter your complete address"
    },
    {
    "id": "confirm",
    "title": "✅ Confirm your order?",
    "type": "confirm"
    }
    ]
    • Show Progress: true
  2. Connect Router's Checkout output → wizard

Step 5.2: Skip Address for Pickup

Notice the address step has required: true. For a more advanced flow:

  1. Use conditional logic after the "delivery" step
  2. Skip address step if delivery == "pickup"

(Or make address optional with required: false)

Step 5.3: Process Completed Order

  1. Connect wizard's Completed output to:

  2. Send Message with order summary:

    🎉 Order Confirmed!

    👤 Name: {{FormData.name}}
    📱 Phone: {{FormData.phone}}
    🚗 Type: {{FormData.delivery}}
    {{if FormData.delivery == "delivery"}}📍 Address: {{FormData.address}}{{end}}

    🛒 Items:
    {{#each var.cart}}
    • {{name}} - ${{price}}
    {{/each}}

    💰 Total: ${{sum(var.cart, "price")}}

    Your order ID: ORD-{{randomInt(10000, 99999)}}
  3. Clear the cart: Set Variable var.cart = []


Part 6: Confirmation Dialog

Using menu.confirm

Throughout the bot, use menu.confirm for:

  1. Cart clearing:

    🗑️ Clear your entire cart?
  2. Order cancellation:

    ❌ Cancel this order?

    This cannot be undone.

    (Use destructive: true for warning style)

  3. Remove item from cart:

    Remove {{item.name}} from cart?

Complete Flow Diagram


Nodes Used

CategoryNodes
Menumenu.show, menu.paginated_list, menu.router, menu.search, menu.wizard, menu.confirm
DataArray, Filter, Get Variable, Set Variable, Transform
ActionsSend Message
TriggersCommand

Enhancement Ideas

  1. Order History: Store orders in database, add /orders command
  2. Favorites: Let users save favorite items
  3. Promo Codes: Add a text step to wizard for discount codes
  4. Admin Panel: Notify admin on new orders
  5. Delivery Tracking: Add order status updates

Tips & Best Practices

Use Meaningful IDs

Menu IDs like main_menu, pizza_category help with debugging.

Template Variables

Use {{page}}, {{total_pages}}, {{Params.x}} in titles for dynamic content.

Handle All Paths

Always connect Back and Cancelled outputs to prevent dead ends.

Test Navigation

Test: Main → Category → Item → Add → Cart → Checkout → Back to Main


Next Steps