Harsh Shah All projects

Case study

BizBot

An agentic AI receptionist for appointment-based businesses. A customer messages a salon, a dental clinic, a yoga studio, a vet, an auto shop or a tutor over SMS, WhatsApp or web chat, and an LLM agent with booking tools checks availability, books, cancels and reschedules against the business's live schedule. The owner controls everything the agent knows from one dashboard and can take any conversation over from the AI at any point.

Role
Founder and sole engineer
Period
2025 – present
Stack
OpenClaw agent runtime, Groq gpt-oss-120b, n8n, Next.js, Supabase Postgres, Twilio
Status
Live in production · 13 paying customers

Visit site

The problem

Every appointment business has the same bottleneck: the front desk. The stylist is mid-cut, the hygienist is mid-cleaning, the instructor is mid-class, and the message that would have been a booking goes unanswered. Putting a language model on the line is the obvious fix, and it fails in three predictable ways.

BizBot had to solve all three for many businesses at once, from a one-chair barber to a multi-staff clinic, without one tenant ever seeing another's data.

What I built

A multi-tenant platform with an agentic core: a tool-calling LLM agent, a deterministic conversation engine beside it, a shared set of booking tools, and an owner dashboard that is the single source of truth for everything the AI says.

In production

From the businesses running on BizBot. Client data is under contract, so these are rounded.

13paying businesses live, across 3 industries
13 monthslongest-running customer
2,380bookings created by the agent to date
220/mobookings a month at the current run rate
40%of those bookings taken outside business hours
1,000/moconversations a month across the client base
83%of conversations finished without an owner stepping in
7 smedian time to first reply
380reschedules and cancellations handled without the owner
2,180appointment reminders sent

How it works

Three planes. The deterministic path verifies the message signature, resolves the tenant, checks whether the owner has taken over, and lets the state machine choose a tool. The agentic path hands the message to the OpenClaw agent, whose calls to the model are metered per tenant and whose tool calls are validated before they run. The owner control plane keeps the agent's context current. Every real action lands in the same five tools.

deterministic path · no model agentic path · LLM tool calling owner control plane SMS · web chat Twilio · Telnyx · Meta verify + resolve signature · tenant HITL gate owner takeover router.js state machine WhatsApp linked SIM OpenClaw agent prompt · skills · memory Groq gpt-oss-120b budget + rate proxy openclaw-call validated tool args booking tools n8n webhooks check-availability create-booking lookup-bookings cancel-booking reschedule-booking Postgres RLS · EXCLUDE Google Calendar free/busy · events Resend confirmations reminders every 15 min owner dashboard staff · hours · FAQs sync poller · 30 s re-renders the agent
Figure 1: two ways in, one set of tools, and an owner who controls what the agent knows.

Bookings split by how a business schedules. A solo practitioner's booking checks Google Calendar free/busy and writes the event. A business with capacity or several staff books through one Postgres function that checks qualification, staff hours, time off and overlap, then upserts the customer and inserts the booking in a single transaction, with an exclusion constraint that turns two customers racing for the same slot into a clean "slot unavailable" rather than a double booking. Availability answers carry a reason as well as slots, so the agent can say the business is closed or the only qualified stylist is off, instead of calling a closed shop fully booked.

Owners can also run the business by message: a WhatsApp owner mode accepts a strict allowlist of seven operations (confirm, complete, reschedule or cancel a booking, add an FAQ, toggle hours, toggle a service), authenticated by the owner's number and audited whether allowed or refused.

The hard part

Getting a language model to take real actions against a live business, reliably and safely, when every argument it passes started as a stranger's free text.

Choosing and measuring the model

Tool calling is where models fail quietly. Llama 3.3 70B and Llama 4 Scout 17B both dropped or malformed booking tool calls; gpt-oss-120b formatted them reliably, at roughly a tenth of the cost of a frontier model and about $0.001 a call. To iterate on prompts without guessing, I built a 53-scenario eval harness that drives the real model through an agent loop against the real booking tools and asserts on three things: which tools were called, what the reply said, and what actually landed in Postgres. A pass means the booking exists, not that the model said it did. Scenarios cover happy paths, ambiguous dates, time formats, returning customers, partial information, out-of-scope requests and eleven adversarial cases. Prompt iteration took the suite from 36 of 53 on the first full run to 46 of 53 in a day.

Keeping the agent grounded

A prompt is a snapshot, and a business changes daily. A staff member given Wednesdays off while the prompt still said nobody was off is exactly the failure owners notice. The fix was architectural: every dashboard write stamps a sync request, a poller re-renders the workspace from live data and restarts the agent, and a test fails if a new config write forgets to ask. Anything time-sensitive, like open slots, is fetched by a tool at the moment of the question.

Making tool calls injection-proof

The agent's tool transport is a shell command, and the first skill recipes had the model build a request line with the customer's words inside it, so a quote or a $(...) in a message could reach the shell. Tool calls now go through a single script that receives the JSON on a quoted heredoc, where the shell expands nothing and a JSON string cannot end the block, validates every field per action, and holds the webhook URL and secret itself. Tests push hostile values through a real shell: the old recipe executes them, the new one delivers them as data.

Numbers

Measured in the repo, each next to what produced it.

46 / 53agent eval scenarios passing, asserting on real database writes, up from 36
~$0.015per model call on gpt-oss-120b, capped by a per-tenant daily budget
30 sfrom an owner's dashboard edit to a re-rendered agent
5booking tools shared by the agent and the state machine
18industry presets that prefill services, prices and FAQs
484behavioural test assertions, 41 node:test files, all passing (17 Sep 2026)

What I would do differently