The governing rule is that the model never performs the money action. It decides what the guest wants; deterministic code checks availability, writes the booking, issues the code and takes the payment. Everything the agent can say is filtered against the store's own facts before it leaves.
📅
Real slots, not guesses
Live Availability and Booking
Availability is read from the venue's own booking system at the moment of asking, so the agent offers slots that exist and never one that has already passed. Bookable options arrive as one-tap chips, and a chosen slot becomes a real booking. The model never books anything: it resolves what the guest wants, and deterministic code performs the write.
Stack: Live availability lookups against the booking platform, chip-based slot selection, deterministic booking path separate from the model.
💳
Nobody fronts the money
Group Payment
Group bookings stall on one person being asked to pay for everyone. The agent can put a group on a payment path where the people coming settle their own share online, which removes the most common reason a confirmed-sounding party quietly never happens.
Stack: Payment flow wired to the booking record, built around the constraint that the obvious approach does not work on this platform.
🎟️
Only to the ones who left
Delayed Vouchers
A discount offered mid-conversation discounts guests who were going to book anyway. Instead the lead is held, and a single-use code is spent only on someone who did not book. Voucher issuing is the one part of the system that can reduce revenue and the only part holding bearer instruments, so it is the most tightly guarded: codes are single-use, generated in a controlled batch, and the agent cannot mint one on its own initiative.
Stack: Hold records with a scheduled decision pass, single-use code inventory, send rail separate from the conversation.
🗣️
Five questions, one at a time
Event Intake as Conversation
The party enquiry form became a conversation: occasion, headcount, date and consent asked one at a time, with one-tap answers, and a refusal accepted rather than treated as a dead end. Declining to give a phone number switches the lead to email instead of killing the intake, which is the single change that stopped losing enquiries at the last step.
Stack: Conversational intake with per-field parsers, one-tap chips, graceful degradation on refused fields.
📱
Same agent, other channels
Two-Way SMS and Booking Context
The agent also works the store's text line, with attachments, and it knows which booking a guest is standing on when the widget is opened from their own booking page, so an answer is about their session rather than about the venue in general. It never discusses status, price or changes to an existing booking: those always reach a person.
Stack: Inbound SMS handling on the store line, media attachments served from our own host only, per-booking context injection.
🚨
Built against alert fatigue
Knowing When to Fetch a Human
When a conversation crosses a threshold worth a person, the agent texts one. The restraint is the design: one text per conversation per trigger class, escalation only, a daily ceiling, nothing outside opening hours, and silence while a human already has the thread. The failure this is built against is not a missed alert, it is a manager who mutes their phone after being texted twice about one guest, after which every alert is missed silently and forever.
Stack: Ranked trigger classes with per-session suppression, quiet hours, daily ceiling, automatic silence on human takeover.
✍️
AI writes, a person sends
Event Reply Drafts
Event enquiries carry everything a first reply needs, and the reply sent on day one converts differently from the one sent never. Replies are drafted automatically and never machine-sent: a person reads every word and presses send. A draft cannot contain a price, a discount, a package description or an availability claim, enforced by the same output filter that guards a live conversation, and a draft for a guest who has since been reached is retired rather than sent.
Stack: Scheduled drafting, one record per draft for idempotency, shared output filter, freshness check at send time.
🛡️
It cannot say what is not true
Grounding and Guardrails
Every fact the agent uses is taken from the venue's own live pages rather than from the model's memory, and a drift checker treats that fact file as the single source of truth and reports anywhere else that contradicts it. Only the facts a given turn needs are sent, rather than the whole base every time. An answer with no words in it never ships, and claims about privacy, ages and pricing are gated on the store's real rules.
Stack: Grounded fact base with per-turn retrieval, output violation filter, automated drift checking against the source of truth.