← Back to portfolio

AI Booking Platform Case Study

A booking engine where a business is configuration, not code. Clients book from a live calendar, and an AI assistant answers from a knowledge base the admin manages, with citations, and never acts without the user's confirmation.

A one-minute walkthrough: the calendar (filter, month, list, one-click booking and undo), a cited answer, a confirmation card, then an admin changes a booking rule and the assistant quotes the new value.

The problem

An appointment business lives in two places: a calendar that takes the booking but can't answer "what does it cost?" or "how late can I cancel?", and a person who answers all of that by hand, one email at a time. A chatbot could take the questions instead, but giving one the power to book or cancel risks changing a real person's appointment on a wrong guess.

Every one of those businesses has the same need, so I built it once instead of per business: a client gets a straight answer from the business's own information, and any change is prepared for them but never runs until they confirm it.

How it fits together

Every change to data goes through one service layer that holds the rules. The pages, the REST routes, and the assistant all use it, so the assistant can't do anything a click couldn't. Its tools only return proposals, and confirming one calls the same REST API a click would.

How the booking platform fits together The browser talks to route handlers, which call one service layer that holds the rules and talks to PostgreSQL. The assistant uses retrieval over pgvector for answers and can only send proposals to the same service layer. BrowserReact UI Route handlersZod, auth, rate limits Service layerrules + transactions AI assistantrole-scoped tools + LLM Retrievalrole-filtered search PostgreSQLPrisma + pgvector proposals only asks
  1. Pages and clicks: the browser calls route handlers (Zod validation, auth, rate limits), which call the service layer (rules and transactions), which writes to PostgreSQL.
  2. The assistant: it searches the knowledge base through retrieval that only returns what the asking role may read, and it can only send proposals to the same service layer.

Four decisions

The assistant prepares, the user confirms

A booking assistant that acts on a wrong guess is worse than none. Its tools return proposals that appear as a confirmation card, and only the user's click runs them. Tool output sent from the browser is discarded, so nobody can forge a proposal.

Trade-off: one extra click and slower than a fully autonomous agent, in exchange for an assistant that can't double-book or cancel the wrong appointment. A live suite of about 120 checks (prompt injection, data leaks, scope, ambiguity, Arabic, multi-turn) gates every prompt change.

A knowledge base the admin controls

Admins add, edit, upload, and hide documents in the app, file them under their own sections, and choose which roles can read each one. Retrieval filters on that in SQL, so the assistant never cites something the asking role can't see.

The booking rules are part of it: change the cancellation cutoff in Settings and the assistant quotes the new value straight away, as in the video.

Trade-off: every edit is chunked and embedded, and switching the embedding model means re-indexing everything.

A business is configuration, not code

The code never names its domain. What clients, providers, and appointments are called, the field labels, the landing copy, the assistant's scope, the demo personas, the knowledge base, and the sample data all come from one domain profile. Two ship side by side, a business-setup consultancy and a university, and a unit test fails if a profile's words leak into code. The engine grew out of my AI Appointment Scheduler.

Trade-off: generic names in the code (client, provider, admin) and some up-front abstraction, paid back by switching business without touching code.

Sharing an admin login with strangers

The guest admin's password is on the landing page, so I treat it as anyone on the internet. It can't lift the abuse limits or close registration, its knowledge changes expire after 24 hours, and settings and document saves carry the version they loaded, so a stale form gets a conflict instead of overwriting a newer change.

Trade-off: visitors share the guest accounts and their daily limits, and a visitor can still make a mess until the weekly reset. A real deployment has none of this, because the demo accounts are switched off.

Smaller details

  • Security: roles and account deactivation are re-checked from the database on every request, every input is validated with Zod, passwords are hashed with bcrypt, the site sends a nonce-based CSP and HSTS, and the database connection uses TLS with certificate verification. Knowledge uploads are text-only and size-capped, with an adversarial test suite behind them.
  • Concurrency: a slot is claimed with a conditional update and the client's row is locked, so two people can't book the same slot or beat the per-advisor limit. Race tests run against a real Postgres.
  • Changing the database while it's live: expand-then-contract migrations keep the running site working while the schema changes.
  • Abuse and cost: per-user and per-IP rate limits, a shared daily budget for assistant requests on the demo, and a prompt layout that keeps about 93% of the assistant's input served from cache.
  • Tested: 340 unit, 302 integration (against Postgres), and 84 end-to-end tests, with automated accessibility scans in light and dark.

What I'd do for a real business

The demo features are switched off or inert on a real deployment, and nothing has to be deleted: turn off the demo accounts, start from an empty database with a first admin, and load the business's own knowledge base. Around that: an always-on instance with an uptime alert and a custom domain, a managed Postgres with point-in-time restore, a spending limit on the AI model, and a privacy policy that names who processes what (the host, the AI provider, which sees chat messages and the passages it retrieves, and the embedding provider). Before inviting users, the business's own questions go through the retrieval eval, each role is tried once on the live site, and a restore from backup is tested.

What it doesn't cover yet

  • The retrieval eval and the live assistant suite describe the content files, so they don't cover documents an admin creates or edits in the app.
  • The live assistant suite covers the consultancy profile only; the university profile has its own knowledge base but no restored scenario set yet.
  • There's no email yet, so no password recovery either, and no multi-factor sign-in. Reminders and notifications appear in the app.
  • One business per deployment, and no payments.
  • It's a public demo: the guest accounts are shared with every visitor and reset weekly, so it shows the product rather than running a business.

Want to see more?

The code is private. I'm happy to walk through it on a call.