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
-
Add a Command Trigger node
- Command:
/order
- Command:
-
Add a menu.show node
- Menu ID:
main_menu - Title:
🍽️ Welcome to FoodBot!
Choose a category to browse: - Menu ID:
-
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"}
] -
Connect:
- Command Trigger → menu.show
- Array → menu.show's Items input
🍽️ Welcome to FoodBot!
Choose a category to browse:
[🍕 Pizza] [🍔 Burgers]
[🍜 Pasta] [🥗 Salads]
[🔍 Search] [🛒 View Cart]
Step 1.2: Route Menu Selections
-
Add a menu.router node after menu.show
-
Configure routes:
[
{"pattern": "^category_(.+)$", "output": "Category", "params": ["category"]},
{"pattern": "^search$", "output": "Search"},
{"pattern": "^view_cart$", "output": "ViewCart"}
] -
Connect:
- menu.show's Selected → menu.router's In
Now clicks are routed:
category_pizza→ Category output withParams.category = "pizza"search→ Search outputview_cart→ ViewCart output
Part 2: Category Menus (Paginated)
Step 2.1: Load Category Items
-
Add a Get Variable node
- Variable:
global.menu_items
- Variable:
-
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
-
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}}
- Menu ID:
-
Connect:
- Filtered items → Items input
- Router's Category output → paginated_list
🍽️ 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
-
Add another menu.router for item selection:
[
{"pattern": "^item_(.+)$", "output": "ShowItem", "params": ["item_id"]}
] -
Connect paginated_list's Selected → router
Part 3: Item Details & Add to Cart
Step 3.1: Show Item Details
-
Add a Filter to find the specific item:
- Expression:
item.id == Params.item_id
- Expression:
-
Add a Send Message (or menu.show) with item details:
{{SelectedItem.emoji}} {{SelectedItem.name}}
📝 {{SelectedItem.description}}
💰 Price: ${{SelectedItem.price}} -
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
-
Add a Get Variable:
var.cart(default:[]) -
Add a Transform to append item:
- Expression:
append(var.cart, SelectedItem)
- Expression:
-
Add a Set Variable:
var.cart= transformed array -
Add a menu.confirm:
- Title:
✅ Added {{SelectedItem.name}} to cart!\n\nContinue shopping? - Yes Text:
Keep Shopping - No Text:
View Cart
- Title:
Part 4: Search Functionality
Step 4.1: Create Search Menu
-
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}}
- Menu ID:
-
Connect all menu items to the Items input
Step 4.2: Handle Search Results
-
Connect Selected output to the same Item Details flow
-
Connect Cancelled output back to Main Menu
User types "pepperoni" → sees matching items instantly!
Part 5: Order Wizard
Step 5.1: Create the Checkout Wizard
-
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
- Wizard ID:
-
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:
- Use conditional logic after the "delivery" step
- Skip address step if
delivery == "pickup"
(Or make address optional with required: false)
Step 5.3: Process Completed Order
-
Connect wizard's Completed output to:
-
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)}} -
Clear the cart: Set Variable
var.cart=[]
Part 6: Confirmation Dialog
Using menu.confirm
Throughout the bot, use menu.confirm for:
-
Cart clearing:
🗑️ Clear your entire cart? -
Order cancellation:
❌ Cancel this order?
This cannot be undone.(Use
destructive: truefor warning style) -
Remove item from cart:
Remove {{item.name}} from cart?
Complete Flow Diagram
Nodes Used
| Category | Nodes |
|---|---|
| Menu | menu.show, menu.paginated_list, menu.router, menu.search, menu.wizard, menu.confirm |
| Data | Array, Filter, Get Variable, Set Variable, Transform |
| Actions | Send Message |
| Triggers | Command |
Enhancement Ideas
- Order History: Store orders in database, add
/orderscommand - Favorites: Let users save favorite items
- Promo Codes: Add a text step to wizard for discount codes
- Admin Panel: Notify admin on new orders
- Delivery Tracking: Add order status updates
Tips & Best Practices
Menu IDs like main_menu, pizza_category help with debugging.
Use {{page}}, {{total_pages}}, {{Params.x}} in titles for dynamic content.
Always connect Back and Cancelled outputs to prevent dead ends.
Test: Main → Category → Item → Add → Cart → Checkout → Back to Main
Next Steps
- Variables → — Store cart and order data
- Subflows → — Reuse common patterns
- E-commerce Bot → — More advanced commerce features