Deep dive: restaurant ordering & table booking
Full end-to-end walkthrough — build the agent and knowledge bases via the SDK, connect WhatsApp, web chat, and Google Calendar from the Console dashboard, then trace a complete diner conversation from first message to confirmed booking.
This page is longer than a recipe. It covers every decision point, shows the dashboard configuration steps explicitly, and includes a full conversation walkthrough so you can see what "good" looks like before you go live. If you want just the essentials, start with the recipe and come back here when you need the deeper treatment.
Before you read further, skim Anatomy of a workflow agent. That page defines the five-step pattern this walkthrough follows.
What you are building
A production-grade restaurant agent that handles every inbound diner interaction without human intervention:
- Menu questions, including dietary filtering (vegan, gluten-free, allergens)
- Real-time table availability via Google Calendar
- Table bookings with upsell prompts
- Takeout orders with order summaries
- Cancellations and rescheduling
The agent is live on WhatsApp and an embedded web chat widget simultaneously. Google Calendar supplies live slot availability so the agent never double-books.
Prerequisites
- BimpeAI account with an active API key (
sk_…) - A Google Calendar set up for your restaurant's reservations (any Google account will do)
- A WhatsApp Business number (approval takes up to 24 hours — start this early)
Step 1: Find the restaurant workflow
Search the public workflow library for a restaurant workflow. The scope: "public" filter restricts results to the shared library.
import { BimpeAI } from "@bimpeai/sdk";
const bimpe = new BimpeAI({ apiKey: process.env.BIMPEAI_API_KEY! });
const page = await bimpe.workflows.list({ scope: "public", search: "restaurant" });
const workflow = page.data[0];
console.log(workflow.id, workflow.name);
// Retrieve the full workflow to read its system_prompt, rules, and flows before committing
const full = await bimpe.workflows.retrieve(workflow.id);
console.log("System prompt preview:", full.system_prompt?.slice(0, 200));import os
from bimpeai import BimpeAI
client = BimpeAI(api_key=os.environ["BIMPEAI_API_KEY"])
page = client.workflows.list(scope="public", search="restaurant")
workflow = page.data[0]
print(workflow.id, workflow.name)
# Retrieve the full workflow to read its system_prompt, rules, and flows before committing
full = client.workflows.retrieve(workflow.id)
print("System prompt preview:", (full.system_prompt or "")[:200])Inspect full.flows to understand the conversation branches the workflow encodes — you will reference those branches when you build knowledge base content and craft the system prompt override in the next step.
Step 2: Create the agent
Create the agent and wire it to the workflow. The system_prompt you supply here overlays the workflow's default prompt, so keep it domain-specific: prices, upsell rule, and the dietary filtering instruction.
const agent = await bimpe.agents.create(
{
name: "The Orchard — restaurant assistant",
description: "Handles table bookings, takeout orders, and menu questions for The Orchard.",
system_prompt:
"You are the digital assistant for The Orchard restaurant. " +
"Help guests browse the menu, filter by dietary requirement, book a table, or place a takeout order. " +
"When a guest adds a main course to their order, offer a complementary side or dessert. " +
"For table bookings, always confirm the party size and preferred time before checking availability. " +
"If a slot is unavailable, offer the next two available slots. " +
"Never quote a price that is not in the Menu knowledge base. " +
"Groups of nine or more require a call to the restaurant directly — do not attempt to book these online.",
agent_workflow_id: workflow.id,
language: "en",
timezone: "Europe/London",
},
{ idempotencyKey: "create-the-orchard-agent-v1" },
);
console.log("Agent ID:", agent.id);agent = client.agents.create(
name="The Orchard — restaurant assistant",
description="Handles table bookings, takeout orders, and menu questions for The Orchard.",
system_prompt=(
"You are the digital assistant for The Orchard restaurant. "
"Help guests browse the menu, filter by dietary requirement, book a table, or place a takeout order. "
"When a guest adds a main course to their order, offer a complementary side or dessert. "
"For table bookings, always confirm the party size and preferred time before checking availability. "
"If a slot is unavailable, offer the next two available slots. "
"Never quote a price that is not in the Menu knowledge base. "
"Groups of nine or more require a call to the restaurant directly — do not attempt to book these online."
),
agent_workflow_id=workflow.id,
language="en",
timezone="Europe/London",
idempotency_key="create-the-orchard-agent-v1",
)
print("Agent ID:", agent.id)The idempotencyKey / idempotency_key means a retried deploy never creates a second agent. Keep it stable across deploys; change it only when you genuinely want a fresh agent.
Step 3: Build the knowledge bases
Two knowledge bases give the agent grounded, accurate answers. The menu KB prevents hallucinated prices. The booking policy KB handles the rules the agent must enforce without inventing them.
// Full menu with dietary flags
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Menu",
content:
"STARTERS\n" +
" Bruschetta al pomodoro — £6.50 — vegan; gluten-free bread available on request\n" +
" Burrata with heritage tomatoes — £9.00 — vegetarian, gluten-free\n" +
" Crispy chicken wings — £8.00 — contains gluten; no vegan/vegetarian option\n" +
" Soup of the day — £5.50 — ask staff for today's allergen info\n" +
"\n" +
"MAINS\n" +
" Margherita pizza — £13.00 — vegetarian; gluten-free base +£2\n" +
" Spicy 'nduja pizza — £15.00 — contains gluten, dairy\n" +
" Pasta arrabbiata — £11.50 — vegan; contains gluten\n" +
" Pappardelle al ragù — £14.00 — contains gluten, dairy\n" +
" Grilled sea bass — £18.50 — gluten-free\n" +
" Roasted cauliflower steak — £13.00 — vegan, gluten-free\n" +
"\n" +
"SIDES\n" +
" Truffle fries — £4.50 — vegan; contains gluten\n" +
" Seasonal salad — £4.00 — vegan, gluten-free\n" +
" Garlic sourdough — £3.50 — vegan; contains gluten\n" +
"\n" +
"DESSERTS\n" +
" Tiramisu — £6.50 — vegetarian; contains dairy, gluten, alcohol\n" +
" Panna cotta — £5.50 — vegetarian, gluten-free; contains dairy\n" +
" Lemon sorbet — £4.50 — vegan, gluten-free\n" +
"\n" +
"DRINKS\n" +
" House red / white / rosé (175 ml) — £6.00\n" +
" Sparkling water (500 ml) — £2.50\n" +
" Still water (500 ml) — £2.50\n" +
" Soft drinks — £2.50\n",
});
// Booking policy and operating hours
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Booking policy",
content:
"Operating hours: Tuesday to Sunday, 12:00–14:30 (lunch) and 18:00–22:00 (dinner). Closed Monday.\n" +
"Maximum party size for online booking: 8. Groups of 9 or more must call +44 20 1234 5678.\n" +
"Groups of 6–8 require a £10 per head deposit, paid at the restaurant on arrival.\n" +
"Cancellations must be made at least 24 hours in advance to avoid a £10 per head no-show charge.\n" +
"Takeout orders: available Tuesday to Saturday, 12:00–14:00 and 17:30–21:30. Collection only.\n" +
"Takeout minimum order: £15. Estimated collection time: 20–30 minutes.\n",
});
// Optionally: point to the live online menu as a URL source
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "url",
name: "Allergen guide",
url: "https://theorchard.example.com/allergens",
});# Full menu with dietary flags
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Menu",
"content": (
"STARTERS\n"
" Bruschetta al pomodoro — £6.50 — vegan; gluten-free bread available on request\n"
" Burrata with heritage tomatoes — £9.00 — vegetarian, gluten-free\n"
" Crispy chicken wings — £8.00 — contains gluten; no vegan/vegetarian option\n"
" Soup of the day — £5.50 — ask staff for today's allergen info\n"
"\n"
"MAINS\n"
" Margherita pizza — £13.00 — vegetarian; gluten-free base +£2\n"
" Spicy 'nduja pizza — £15.00 — contains gluten, dairy\n"
" Pasta arrabbiata — £11.50 — vegan; contains gluten\n"
" Pappardelle al ragù — £14.00 — contains gluten, dairy\n"
" Grilled sea bass — £18.50 — gluten-free\n"
" Roasted cauliflower steak — £13.00 — vegan, gluten-free\n"
"\n"
"SIDES\n"
" Truffle fries — £4.50 — vegan; contains gluten\n"
" Seasonal salad — £4.00 — vegan, gluten-free\n"
" Garlic sourdough — £3.50 — vegan; contains gluten\n"
"\n"
"DESSERTS\n"
" Tiramisu — £6.50 — vegetarian; contains dairy, gluten, alcohol\n"
" Panna cotta — £5.50 — vegetarian, gluten-free; contains dairy\n"
" Lemon sorbet — £4.50 — vegan, gluten-free\n"
"\n"
"DRINKS\n"
" House red / white / rosé (175 ml) — £6.00\n"
" Sparkling water (500 ml) — £2.50\n"
" Still water (500 ml) — £2.50\n"
" Soft drinks — £2.50\n"
),
})
# Booking policy and operating hours
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Booking policy",
"content": (
"Operating hours: Tuesday to Sunday, 12:00–14:30 (lunch) and 18:00–22:00 (dinner). Closed Monday.\n"
"Maximum party size for online booking: 8. Groups of 9 or more must call +44 20 1234 5678.\n"
"Groups of 6–8 require a £10 per head deposit, paid at the restaurant on arrival.\n"
"Cancellations must be made at least 24 hours in advance to avoid a £10 per head no-show charge.\n"
"Takeout orders: available Tuesday to Saturday, 12:00–14:00 and 17:30–21:30. Collection only.\n"
"Takeout minimum order: £15. Estimated collection time: 20–30 minutes.\n"
),
})
# Optionally: point to the live online menu as a URL source
client.agents.knowledge_bases.create(agent.id, {
"type": "url",
"name": "Allergen guide",
"url": "https://theorchard.example.com/allergens",
})Keep the menu KB current. When the kitchen changes a price or removes a dish, call knowledgeBases.update with the revised content rather than recreating the KB — the agent picks up the change immediately without any downtime.
Step 4: Inspect conversation flows
Before connecting channels, read back the conversation flows the workflow has installed. These are read-only — you cannot create or modify them via the API — but inspecting them tells you what branches exist and what the agent will do at each turn.
const flows = await bimpe.agents.conversationFlows.list(agent.id);
for (const flow of flows) {
console.log(flow.name, "—", flow.description ?? "(no description)");
}flows = client.agents.conversation_flows.list(agent.id)
for flow in flows:
print(flow.name, "—", flow.description or "(no description)")A well-configured restaurant workflow typically includes flows for menu browsing, table booking, takeout ordering, and cancellation. If a flow you expected is missing, verify the workflow id — you may have picked a generic support workflow rather than the restaurant one.
Step 5: Connect channels and Google Calendar (Console dashboard)
Channels and integrations are dashboard-only — the API cannot create them
The BimpeAI API cannot create channel connections, purchase phone numbers, or connect integrations such as Google Calendar. Everything in this step is done in the Console dashboard. The SDK lets you list what is already connected (channels.list, integrations.list) but cannot modify those connections.
Connect WhatsApp
- Open Console dashboard and navigate to Agents.
- Select "The Orchard — restaurant assistant".
- Under Channels, click Add channel and choose WhatsApp.
- Follow the Meta Business verification flow. You will need your WhatsApp Business API access token and the phone number ID from your Meta Business Manager.
- Once approved, the channel status changes to Active. Inbound WhatsApp messages now reach the agent automatically.
Connect web chat
- Under Channels, click Add channel and choose Web Chat.
- Copy the generated embed snippet.
- Paste the snippet into your restaurant website's HTML, just before the closing
</body>tag. The widget appears in the bottom-right corner. - Optionally configure the widget's brand colour, greeting message, and operating hours directly in the Console.
Connect Google Calendar
- Under Integrations, click Add integration and choose Google Calendar.
- Authorise access to the calendar you use for restaurant reservations. The agent checks this calendar for availability when a guest requests a table.
- Confirm the integration status shows Connected.
Without Google Calendar connected, the agent cannot check real-time availability and will refuse to make booking commitments — it will tell the guest to call the restaurant directly instead.
Verify programmatically
After completing the dashboard steps, verify the connections from your code before going live:
const integrations = await bimpe.agents.integrations.list(agent.id);
const channels = await bimpe.agents.channels.list(agent.id);
console.log("Integrations:", integrations.map((i) => i.name));
console.log("Channels:", channels.map((c) => c.type));
// Expected:
// Integrations: ["Google Calendar"]
// Channels: ["whatsapp", "webchat"]integrations = client.agents.integrations.list(agent.id)
channels = client.agents.channels.list(agent.id)
print("Integrations:", [i.name for i in integrations])
print("Channels:", [c.type for c in channels])
# Expected:
# Integrations: ['Google Calendar']
# Channels: ['whatsapp', 'webchat']Do not open the restaurant for service until both appear.
Conversation walkthrough
This is what a full booking conversation looks like end to end. The agent role is the agent's response; the customer role is a real WhatsApp message.
Customer: Hi, do you have a table for 2 this Friday evening?
Agent: Hello! I'd love to help you book a table at The Orchard. Let me check availability for this Friday evening for two people — do you have a preferred time: early (around 18:00) or later (around 20:00)?
Customer: 7pm would be perfect.
Agent: Great — let me check 7:00 pm for two on Friday. (checks Google Calendar) We have a table available at 7:00 pm. Shall I confirm that booking for you? I'll need your name and a contact number in case we need to reach you.
Customer: Yes please — it's Sarah Chen, 07700 900123.
Agent: Perfect, Sarah. Your table for two at The Orchard is confirmed for Friday at 7:00 pm. Your booking reference is TBO-2847. If anything changes, just message us here at least 24 hours in advance. Would you like to see the menu in advance?
Customer: Yes — any vegan options?
Agent: We have several great vegan options tonight:
Starters: Bruschetta al pomodoro (£6.50) — the gluten-free bread is available on request.
Mains: Pasta arrabbiata (£11.50) and Roasted cauliflower steak (£13.00), both gluten-free.
Sides: Truffle fries, Seasonal salad, and Garlic sourdough — all vegan.
Dessert: Lemon sorbet (£4.50).
Would you like to pre-order, or browse the full menu when you arrive?
Customer: We'll decide on the night, thanks!
Agent: Lovely — see you Friday at 7:00 pm, Sarah. We look forward to welcoming you.
Notice what happened: the agent confirmed the party size before checking availability, offered the next slot proactively (the system prompt rule), confirmed with a reference number, and upsold the pre-order opportunity without being pushy. Those behaviours come from the system prompt and the booking policy KB working together.
Streaming live conversations
Once you are live, you can stream incoming messages from any active conversation on your server:
// List recent WhatsApp conversations
const conversations = await bimpe.conversations.list(agent.id, { channel: "whatsapp" });
if (conversations.data.length > 0) {
const conv = conversations.data[0];
const controller = new AbortController();
for await (const event of bimpe.conversations.messages.stream(agent.id, conv.id, {
signal: controller.signal,
})) {
console.log(`[${event.role}] ${event.message}`);
}
}# List recent WhatsApp conversations
conversations = client.conversations.list(agent.id, channel="whatsapp")
if conversations.data:
conv = conversations.data[0]
for event in client.conversations.messages.stream(agent.id, conv.id):
print(f"[{event.role}] {event.message}")The stream reconnects automatically on a dropped connection and resumes from the last delivered message id. You will not miss a message or see one twice.
Full runnable example
import { BimpeAI } from "@bimpeai/sdk";
const bimpe = new BimpeAI({ apiKey: process.env.BIMPEAI_API_KEY! });
// 1. Find workflow
const page = await bimpe.workflows.list({ scope: "public", search: "restaurant" });
const workflow = page.data[0];
// 2. Create agent
const agent = await bimpe.agents.create(
{
name: "The Orchard — restaurant assistant",
system_prompt:
"You are the digital assistant for The Orchard restaurant. " +
"Help guests browse the menu, filter by dietary requirement, book a table, or place a takeout order. " +
"When a guest adds a main course to their order, offer a complementary side or dessert. " +
"For table bookings, always confirm the party size and preferred time before checking availability. " +
"If a slot is unavailable, offer the next two available slots. " +
"Never quote a price not in the Menu knowledge base. " +
"Groups of nine or more must call the restaurant directly.",
agent_workflow_id: workflow.id,
language: "en",
timezone: "Europe/London",
},
{ idempotencyKey: "create-the-orchard-agent-v1" },
);
// 3. Add knowledge bases
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Menu",
content:
"MAINS: Margherita pizza £13.00 (vegetarian), Pasta arrabbiata £11.50 (vegan), " +
"Grilled sea bass £18.50 (gluten-free), Roasted cauliflower steak £13.00 (vegan, gluten-free).\n" +
"DESSERTS: Tiramisu £6.50, Lemon sorbet £4.50 (vegan).",
});
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Booking policy",
content:
"Open Tue–Sun, lunch 12:00–14:30, dinner 18:00–22:00. Max 8 guests for online booking. " +
"Cancel 24 hours in advance. Takeout min £15, collection only.",
});
// 4. Inspect conversation flows (read-only)
const flows = await bimpe.agents.conversationFlows.list(agent.id);
console.log("Flows installed:", flows.map((f) => f.name));
// 5. Verify channels and integrations (connected in the dashboard)
const integrations = await bimpe.agents.integrations.list(agent.id);
const channels = await bimpe.agents.channels.list(agent.id);
console.log("Agent ID:", agent.id);
console.log("Integrations:", integrations.map((i) => i.name));
console.log("Channels:", channels.map((c) => c.type));import os
from bimpeai import BimpeAI
client = BimpeAI(api_key=os.environ["BIMPEAI_API_KEY"])
# 1. Find workflow
page = client.workflows.list(scope="public", search="restaurant")
workflow = page.data[0]
# 2. Create agent
agent = client.agents.create(
name="The Orchard — restaurant assistant",
system_prompt=(
"You are the digital assistant for The Orchard restaurant. "
"Help guests browse the menu, filter by dietary requirement, book a table, or place a takeout order. "
"When a guest adds a main course to their order, offer a complementary side or dessert. "
"For table bookings, always confirm the party size and preferred time before checking availability. "
"If a slot is unavailable, offer the next two available slots. "
"Never quote a price not in the Menu knowledge base. "
"Groups of nine or more must call the restaurant directly."
),
agent_workflow_id=workflow.id,
language="en",
timezone="Europe/London",
idempotency_key="create-the-orchard-agent-v1",
)
# 3. Add knowledge bases
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Menu",
"content": (
"MAINS: Margherita pizza £13.00 (vegetarian), Pasta arrabbiata £11.50 (vegan), "
"Grilled sea bass £18.50 (gluten-free), Roasted cauliflower steak £13.00 (vegan, gluten-free).\n"
"DESSERTS: Tiramisu £6.50, Lemon sorbet £4.50 (vegan)."
),
})
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Booking policy",
"content": (
"Open Tue–Sun, lunch 12:00–14:30, dinner 18:00–22:00. Max 8 guests for online booking. "
"Cancel 24 hours in advance. Takeout min £15, collection only."
),
})
# 4. Inspect conversation flows (read-only)
flows = client.agents.conversation_flows.list(agent.id)
print("Flows installed:", [f.name for f in flows])
# 5. Verify channels and integrations (connected in the dashboard)
integrations = client.agents.integrations.list(agent.id)
channels = client.agents.channels.list(agent.id)
print("Agent ID:", agent.id)
print("Integrations:", [i.name for i in integrations])
print("Channels:", [c.type for c in channels])Demo tips
What judges and visitors should see when you demonstrate this agent:
Start by showing the web chat widget embedded on a restaurant website. Open it and type "Do you have anything vegan on the menu?" — the agent should respond immediately with specific dishes and prices from the knowledge base, with no generic placeholder text. This demonstrates that the KB is live.
Then send "Can I book a table for 3 on Saturday evening?" to show the booking flow. The agent should ask for a preferred time, check Google Calendar, and return with a confirmed slot or an alternative — not a vague "I'll check and get back to you." If the Calendar integration is working, the response is deterministic and references real availability.
Finish by showing the WhatsApp channel: send the same booking message from a phone. The agent's behaviour should be identical across both channels, because channels in BimpeAI are presentation layers — the same workflow and knowledge base serve both.
The most impressive moment for a live demo is a dietary edge case: "I'm coeliac — what can I eat?" The agent should enumerate the genuinely gluten-free options from the KB without guessing, and offer the gluten-free pizza base modification without prompting. That answer comes entirely from grounded knowledge base content, not from the model improvising.
Subscription Management Agent
Handle plan upgrades, downgrades, cancellations, billing queries, and payment method updates via web chat or inbound voice — backed by Stripe.
Deep dive: booking with Stripe payment
Full end-to-end walkthrough — build the pay-before-book agent via the SDK, connect Google Calendar and Stripe from the Console dashboard, and trace a complete conversation from slot selection to payment confirmation.