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.
This page is a deep dive into the pay-before-book pattern. It covers the reasoning behind each design decision, shows the Console dashboard steps explicitly, and includes a complete conversation trace so you can see exactly what the agent says at each turn. The companion recipe is shorter; start there if you want just the code. Come back here for the full picture.
Read Anatomy of a workflow agent before going further — this walkthrough assumes that vocabulary.
What you are building
An agent that takes a customer from "I'd like to book" to "payment confirmed" without leaving the conversation. The flow:
- Customer names the service and preferred slot.
- Agent checks Google Calendar for real-time availability.
- Agent presents the price (from the knowledge base) and generates a Stripe payment link.
- Slot is held for 15 minutes while the customer pays.
- On payment confirmation, the booking is locked in and the customer receives a confirmation.
- If the slot expires before payment, the agent releases it and offers alternatives.
The 15-minute hold prevents double-bookings on high-demand schedules. The Stripe integration means no payment details flow through your chat infrastructure — the customer pays via Stripe's hosted page and the agent receives a webhook confirmation.
Prerequisites
- BimpeAI account with an active API key (
sk_…) - A Stripe account with a publishable and secret key (live or test)
- A Google Calendar dedicated to bookings
- A WhatsApp Business number or web chat widget embed ready to deploy
Step 1: Find the booking-payment workflow
import { BimpeAI } from "@bimpeai/sdk";
const bimpe = new BimpeAI({ apiKey: process.env.BIMPEAI_API_KEY! });
const page = await bimpe.workflows.list({ scope: "public", search: "booking payment" });
const workflow = page.data[0];
console.log(workflow.id, workflow.name);
// Inspect the full workflow — the flows property lists the conversation branches
const full = await bimpe.workflows.retrieve(workflow.id);
console.log("Branches:", full.flows?.map((f) => f.name));import os
from bimpeai import BimpeAI
client = BimpeAI(api_key=os.environ["BIMPEAI_API_KEY"])
page = client.workflows.list(scope="public", search="booking payment")
workflow = page.data[0]
print(workflow.id, workflow.name)
# Inspect the full workflow — the flows property lists the conversation branches
full = client.workflows.retrieve(workflow.id)
if full.flows:
print("Branches:", [f.name for f in full.flows])The booking-payment workflow's conversation branches typically include: availability check, payment link generation, slot-hold timeout, payment confirmation, and cancellation. If you see fewer branches, double-check that you retrieved the right workflow — search terms like "payment" alone can match generic e-commerce workflows that lack the slot-hold logic.
Step 2: Create the agent
The system prompt carries the business rules that make this specific: the hold duration, the policy on overbooking, and the instruction to never confirm without payment.
const agent = await bimpe.agents.create(
{
name: "Clarity Coaching — booking agent",
description: "Booking agent for Clarity Coaching that takes payment before confirming sessions.",
system_prompt:
"You are the booking agent for Clarity Coaching. " +
"Help the customer choose a service, find an available slot, and complete payment before the booking is confirmed. " +
"Step 1: Ask the customer which service they want and their preferred date and time. " +
"Step 2: Check availability via Google Calendar. If available, name the price from the Service pricing knowledge base and send the Stripe payment link. " +
"Tell the customer their slot is held for 15 minutes. " +
"Step 3: Wait for Stripe payment confirmation. Only confirm the booking once payment is received. " +
"If 15 minutes pass without payment, tell the customer the slot has been released and offer the next two available slots. " +
"Step 4: On payment confirmation, state the booking details — service, date, time, and a confirmation reference — and ask if the customer wants a calendar invite sent to their email. " +
"Never confirm a booking without Stripe payment confirmation. " +
"Never share internal calendar IDs or Stripe payment intent IDs with the customer.",
agent_workflow_id: workflow.id,
language: "en",
timezone: "Europe/London",
},
{ idempotencyKey: "create-clarity-coaching-agent-v1" },
);
console.log("Agent ID:", agent.id);agent = client.agents.create(
name="Clarity Coaching — booking agent",
description="Booking agent for Clarity Coaching that takes payment before confirming sessions.",
system_prompt=(
"You are the booking agent for Clarity Coaching. "
"Help the customer choose a service, find an available slot, and complete payment before the booking is confirmed. "
"Step 1: Ask the customer which service they want and their preferred date and time. "
"Step 2: Check availability via Google Calendar. If available, name the price from the Service pricing knowledge base and send the Stripe payment link. "
"Tell the customer their slot is held for 15 minutes. "
"Step 3: Wait for Stripe payment confirmation. Only confirm the booking once payment is received. "
"If 15 minutes pass without payment, tell the customer the slot has been released and offer the next two available slots. "
"Step 4: On payment confirmation, state the booking details — service, date, time, and a confirmation reference — and ask if the customer wants a calendar invite sent to their email. "
"Never confirm a booking without Stripe payment confirmation. "
"Never share internal calendar IDs or Stripe payment intent IDs with the customer."
),
agent_workflow_id=workflow.id,
language="en",
timezone="Europe/London",
idempotency_key="create-clarity-coaching-agent-v1",
)
print("Agent ID:", agent.id)The numbered steps in the system prompt are deliberate. The booking-payment workflow is stateful — it advances through the steps in order — and the numbered prompt aligns the agent's language model interpretation with the workflow's state machine, so the two stay in sync as the conversation progresses.
Step 3: Add knowledge bases
Pricing and booking policy are the two knowledge bases that prevent the agent from inventing details under customer pressure.
// Service pricing — the source of truth for all prices quoted in conversation
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Service pricing",
content:
"Discovery call (30 min): £0 — free, no payment required\n" +
"Clarity session (60 min): £120 — 1-to-1 coaching\n" +
"Deep-dive session (90 min): £175 — 1-to-1 coaching, includes written summary\n" +
"Group session (60 min, up to 4 people): £220 — price is per group, not per person\n" +
"Half-day intensive (3 hours): £380 — 1-to-1, includes pre-session questionnaire\n" +
"All prices include VAT. Payment is required at booking for all sessions except the discovery call.\n" +
"Stripe is the only accepted payment method.",
});
// Cancellation and refund policy
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Booking and cancellation policy",
content:
"Slot hold: 15 minutes from payment link generation. Slot released automatically if unpaid.\n" +
"Reschedule: free if requested more than 24 hours before the session start time.\n" +
"Cancellation more than 48 hours before the session: full refund via Stripe (3–5 business days).\n" +
"Cancellation 24–48 hours before the session: 50% refund via Stripe.\n" +
"Cancellation within 24 hours: no refund. The session can be rescheduled once at no charge within 30 days.\n" +
"No-show: no refund, no rescheduling credit.\n" +
"Refunds are processed by the business, not by the agent — direct customers to hello@claritycoaching.example.com for disputes.",
});# Service pricing — the source of truth for all prices quoted in conversation
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Service pricing",
"content": (
"Discovery call (30 min): £0 — free, no payment required\n"
"Clarity session (60 min): £120 — 1-to-1 coaching\n"
"Deep-dive session (90 min): £175 — 1-to-1 coaching, includes written summary\n"
"Group session (60 min, up to 4 people): £220 — price is per group, not per person\n"
"Half-day intensive (3 hours): £380 — 1-to-1, includes pre-session questionnaire\n"
"All prices include VAT. Payment is required at booking for all sessions except the discovery call.\n"
"Stripe is the only accepted payment method."
),
})
# Cancellation and refund policy
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Booking and cancellation policy",
"content": (
"Slot hold: 15 minutes from payment link generation. Slot released automatically if unpaid.\n"
"Reschedule: free if requested more than 24 hours before the session start time.\n"
"Cancellation more than 48 hours before the session: full refund via Stripe (3–5 business days).\n"
"Cancellation 24–48 hours before the session: 50% refund via Stripe.\n"
"Cancellation within 24 hours: no refund. The session can be rescheduled once at no charge within 30 days.\n"
"No-show: no refund, no rescheduling credit.\n"
"Refunds are processed by the business, not by the agent — direct customers to hello@claritycoaching.example.com for disputes."
),
})When pricing changes, call knowledgeBases.update on the pricing KB and pass the updated content. The agent uses the new prices in every subsequent conversation from that point forward — no redeploy, no agent recreation.
Step 4: Connect Google Calendar and Stripe (Console dashboard)
Google Calendar, Stripe, and channel connections are dashboard-only
The BimpeAI API cannot create integrations or channel connections. Google Calendar, Stripe, WhatsApp, and web chat are all connected in the Console dashboard. The SDK can list active connections (integrations.list, channels.list) but cannot create or modify them. If either Google Calendar or Stripe is missing from integrations.list, the agent will refuse to proceed with bookings rather than guessing.
Connect Google Calendar
- Open Console dashboard → Agents → select "Clarity Coaching — booking agent".
- Under Integrations, click Add integration and choose Google Calendar.
- Sign in with the Google account that owns the coaching calendar and grant access.
- Select the specific calendar to use for bookings. The agent checks this calendar for conflicts and creates events on confirmed bookings.
- Status should show Connected.
Connect Stripe
- Under Integrations, click Add integration and choose Stripe.
- Enter your Stripe publishable key and secret key (or connect via Stripe OAuth).
- The Console configures the Stripe webhook endpoint automatically. This webhook is how the agent receives payment confirmation — do not disable it.
- In test mode, use Stripe's test keys (
pk_test_…,sk_test_…) to validate the full payment flow before going live. - Status should show Connected.
Connect messaging channels
- Under Channels, click Add channel → WhatsApp and follow the Meta Business verification flow.
- Under Channels, click Add channel → Web Chat and copy the embed snippet onto your booking page.
Verify programmatically
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", "Stripe"]
// 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', 'Stripe']
# Channels: ['whatsapp', 'webchat']Both integrations must appear before you open bookings. A missing Stripe connection means payment links will never generate, and a missing Calendar connection means the agent cannot check availability.
Conversation walkthrough
A complete WhatsApp booking conversation, from first message to payment confirmed.
Customer: Hi, I'd like to book a coaching session please.
Agent: Hello! I'd love to help you book with Clarity Coaching. We offer a few different session types:
- Clarity session (60 min): £120
- Deep-dive session (90 min): £175 — includes a written summary
- Group session (60 min, up to 4 people): £220
- Half-day intensive (3 hours): £380
Which would suit you best, and do you have a preferred date and time?
Customer: The 60-minute one, next Thursday at 10am.
Agent: Let me check availability for Thursday 26 June at 10:00 am. (checks Google Calendar) That slot is available.
The Clarity session is £120 including VAT. I'll hold the slot for 15 minutes while you pay. Here's your secure payment link:
Once payment is confirmed, your booking is locked in. The link expires in 15 minutes.
Customer: (pays via Stripe)
Agent: Payment received — thank you! Your Clarity session with Clarity Coaching is confirmed:
Date: Thursday 26 June Time: 10:00 am (60 minutes) Reference: CLR-8412
Would you like a calendar invite sent to your email address?
Customer: Yes please — it's alex@example.com.
Agent: Done. A calendar invite has been sent to alex@example.com. We look forward to seeing you on Thursday. If anything changes, message us here at least 24 hours in advance to reschedule at no charge.
The critical moments in this flow: the agent named the price before generating the payment link (from the pricing KB), stated the 15-minute hold explicitly, waited for Stripe's webhook confirmation before saying "confirmed", and offered the calendar invite only after payment — not before.
The timeout branch
If the customer does not pay within 15 minutes:
Agent: The payment link for your 10:00 am slot has expired and the slot has been released. I can offer you:
- Thursday 26 June at 11:30 am
- Friday 27 June at 9:00 am
Would either of those work, or would you prefer a different time?
This branch is handled by the workflow's slot-hold timeout flow, not by the agent improvising. The agent does not re-send the original payment link — it generates a fresh one for the new slot.
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: "booking payment" });
const workflow = page.data[0];
// 2. Create agent
const agent = await bimpe.agents.create(
{
name: "Clarity Coaching — booking agent",
system_prompt:
"You handle bookings for Clarity Coaching. Check availability, hold the slot for 15 minutes, " +
"send a Stripe payment link, and confirm only after payment is received. " +
"Never confirm without Stripe payment confirmation.",
agent_workflow_id: workflow.id,
language: "en",
timezone: "Europe/London",
},
{ idempotencyKey: "create-clarity-coaching-agent-v1" },
);
// 3. Add knowledge bases
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Service pricing",
content:
"Clarity session (60 min): £120.\n" +
"Deep-dive session (90 min): £175 — includes written summary.\n" +
"Group session (60 min, up to 4): £220.\n" +
"Half-day intensive (3 hours): £380.\n" +
"All prices include VAT. Stripe only.",
});
await bimpe.agents.knowledgeBases.create(agent.id, {
type: "text",
name: "Booking and cancellation policy",
content:
"Slot held 15 minutes after payment link sent; released if unpaid.\n" +
"Reschedule free with 24+ hours notice.\n" +
"Cancellation >48 hours: full refund. 24–48 hours: 50% refund. <24 hours: no refund.\n" +
"Disputes: hello@claritycoaching.example.com",
});
// 4. Verify integrations (Google Calendar + Stripe must be connected in 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));
// 5. Stream a WhatsApp booking conversation (once channels are live)
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}`);
}
}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="booking payment")
workflow = page.data[0]
# 2. Create agent
agent = client.agents.create(
name="Clarity Coaching — booking agent",
system_prompt=(
"You handle bookings for Clarity Coaching. Check availability, hold the slot for 15 minutes, "
"send a Stripe payment link, and confirm only after payment is received. "
"Never confirm without Stripe payment confirmation."
),
agent_workflow_id=workflow.id,
language="en",
timezone="Europe/London",
idempotency_key="create-clarity-coaching-agent-v1",
)
# 3. Add knowledge bases
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Service pricing",
"content": (
"Clarity session (60 min): £120.\n"
"Deep-dive session (90 min): £175 — includes written summary.\n"
"Group session (60 min, up to 4): £220.\n"
"Half-day intensive (3 hours): £380.\n"
"All prices include VAT. Stripe only."
),
})
client.agents.knowledge_bases.create(agent.id, {
"type": "text",
"name": "Booking and cancellation policy",
"content": (
"Slot held 15 minutes after payment link sent; released if unpaid.\n"
"Reschedule free with 24+ hours notice.\n"
"Cancellation >48 hours: full refund. 24–48 hours: 50% refund. <24 hours: no refund.\n"
"Disputes: hello@claritycoaching.example.com"
),
})
# 4. Verify integrations (Google Calendar + Stripe must be connected in 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])
# 5. Stream a WhatsApp booking conversation (once channels are live)
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}")Demo tips
What judges and visitors should see when you demonstrate this agent:
Open the web chat widget and ask for a "60-minute session next Tuesday at 2pm." The agent should check Calendar, name the price (£120), generate the Stripe payment link, and tell the customer the slot is held for 15 minutes — all in a single turn. This shows the integration is live, not mocked.
Click the Stripe payment link. Use Stripe test card 4242 4242 4242 4242 with any future expiry and CVC to complete payment. Immediately after payment, the agent should send the confirmation message with a reference number, unprompted. That near-instant webhook turnaround is the most impressive single moment of this demo.
Then show the timeout path: start a new conversation, get to the payment link, and wait (or advance the clock in Stripe test mode). After 15 minutes the agent proactively messages "the slot has been released" and offers alternatives. This shows that the state machine is running correctly on the server, not just the agent making it up.
If running in Stripe test mode, keep a browser tab open to the Stripe dashboard showing events in real time so observers can see the webhook fire and the payment land.
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.
Deep dive: fraud alert verification (voice)
Full end-to-end walkthrough — build the outbound fraud verification agent via the SDK, configure the voice channel and escalation path in the Console dashboard, and trace a complete verification call including the human-escalation branch.