
Ask Lovable, v0 or Claude Code for "a booking site" and you get the same three screens almost every time: pick a service, pick a date, pick a time. For a barber that's right. For a padel club it's wrong, because players choose a court on a grid. For a car rental it's wrong, because the unit is a day and the car is gone from Tuesday to Friday. For a Pilates studio it's wrong, because a slot is a seat in a room that fills up.
The flow follows what the business sells, and in practice there are about seven shapes. This article walks through each one: the order of the screens, the Opencals Storefront API calls behind it, the mistake AI builders make with it, and a prompt you can paste. Six of the seven have an MIT-licensed Next.js template you can click through before you build anything.
Five questions decide a booking flow: what is booked (a person, a unit, a seat), one-to-one or group, fixed or variable length, where it happens, and what extras come with it. Those answers produce seven common shapes: staff-first, guided intake, class timetable, unit grid, hourly room with extras, day-range rental and nightly stay. On Opencals all seven run on the same API with one API key, and the AI builder only changes the screens. You can build and test any of them on a free development store; it runs in test mode and can't take real payments until you switch it to production.
Five questions decide the flow before any design does
A booking flow is the order of screens a customer goes through to reserve something and pay for it. Its shape comes from the business, not from the design: what's being booked, how many people share it, how long it lasts, where it happens and what's sold with it. Answer those five questions first and the screens mostly follow. Restyling a booking page is cheap. Changing its flow after launch isn't.
- What is booked? A person's time (a stylist, a physio, a tutor), a unit (a court, a room, a car, a villa), or a seat in something that runs anyway (a class).
- One-to-one or group? A group product has a capacity, so the page needs "4 spots left" and a party size.
- Fixed or variable length? A 45-minute cut is fixed. A studio session of one to four hours isn't, and neither is a three-day rental.
- Where? Several locations usually means picking the location first. An online location means a video link after booking. A mobile service means asking for an address at checkout.
- What comes with it? Add-ons such as a beard oil, a second camera or an airport transfer, priced per booking or per hour or day.
Here's how the answers combine:
| Booked by | Screen order | The call that matters | Live example | |
|---|---|---|---|---|
| 1. Staff-first (salon, barber, coach) | A person's time | Service → person or "anyone" → day → time → extras | current-availabilities with staffMemberId | Haar, Frisor |
| 2. Guided intake (clinic, therapy) | A practitioner, after triage | Who it's for → what's wrong → clinic or video → clinician → time → notes | Checkout questions and file uploads | Clear Care |
| 3. Class timetable (school, studio) | A seat | Timetable → class → party size | maxAttendees − attendees | Fluently, Tensile |
| 4. Unit grid (courts, bays, lanes) | A unit for a length | Club → day → units × time grid → length | One merged availability read per unit, cached | VOLT |
| 5. Hourly room with extras (podcast, photo studio) | A room by the hour | Room → length → start time → extras | duration on availability, per-hour add-ons | No template yet |
| 6. Day-range rental (cars, boats, gear) | Whole days | Item → pick-up and return → deliver or collect → extras | Merged ranges without duration, checked on the client | NOIR Drive |
| 7. Nightly stay (villas, cabins) | Nights, fixed check-in | Villa → check-in and check-out → guests → extras | Fixed times the server enforces | Bali Villa Rentals |
Every prompt below leaves out services, prices, staff and opening hours on purpose. Those live in the Opencals dashboard and come back from the API, so when the business adds a treatment or a court it shows up on the site without anyone prompting again. The prompt's job is to name the shape and the look.
1. A barber books a person, so the person comes early
At a barbershop the regulars want their barber, and everyone else wants the soonest chair. So the flow is: service, then a barber or "Anyone available", then a day, then a time, then extras, then checkout.
Behind it, the page loads the service by its slug. If the service has variants ("Skin fade", "Scissor cut"), the customer picks one first, and every later call uses the variant id. The date picker calls current-availability-ranges once to grey out days with nothing free, and nearest-availability to open on the first bookable day. The time grid calls current-availabilities for the chosen date. For "Anyone", a slot is bookable if its staffMemberIds list isn't empty, and you leave staffMemberId out of the booking so the engine assigns someone.
Where AI builders go wrong: they hard-code the barbers into a dropdown, or put the barber choice after the time, which means a regular finds a slot and then discovers it's with someone else.
Build a booking site for Hook & Blade, a three-chair barbershop in Manchester.
Services, prices and barbers come from Opencals. Flow: service, then a barber
or "Anyone available", then day, then time, then extras, then checkout.
Dark and editorial: near-black, brass accents, a condensed serif for headings.
Mobile first; most people book from their phone on the way home.The Frisor template is this shape with add-ons in the same transaction, and Haar is the salon version with multi-location booking.
2. A clinic patient starts from what's wrong, not from a department
A salon customer knows they want a colour. A patient often doesn't know whether a sore knee is physiotherapy or orthopaedics, and making them choose a department on the first screen loses a lot of them there. The guided-intake shape asks who the visit is for (me, my child, someone else), what's going on, and in clinic or by video, then recommends visits with their next free times. After that it behaves like the staff-first flow: clinician or "first available", day, time.
What's different is the end. Pre-visit notes are checkout questions, answered per visit so a family booking keeps each patient's notes apart, and photos go up through a presigned upload URL. Departments are product collections, clinicians are staff members, and the video option is the store's online location, whose meeting link comes back on the booked appointment.
Where AI builders go wrong: they build a symptom checker. The Clear Care template deliberately doesn't: its Care Finder maps needs and keywords from a config file to visits, says plainly that it isn't a diagnosis tool, and shows an emergency notice on every booking screen. Opencals also isn't a medical record system. There's no charting, so a clinic that needs clinical notes and insurance billing keeps those in its practice software.
Build a booking site for Northside Physio, a two-location physiotherapy clinic
in Leeds. Patients start from what's going on (back, knee, sports injury,
not sure), then choose in clinic or video, then a physio or "first available",
then a time. Show the store's pre-visit questions after the time is picked.
Calm and clinical: white, soft blue, lots of space. Add an emergency notice
to every booking screen.3. A class sells seats, so the timetable is the booking page
For a language school or a reformer studio, nobody browses "services" and then looks for a time. They look at the week and pick a class that's on. The timetable is the catalogue: rows of sessions, each showing the teacher or instructor and how many places are left, and a click goes straight to party size and checkout.
Each availability slot for a group product carries maxAttendees and attendees, so places left is the difference. The booking sends numberOfAttendees, and if the product allows guests, friends' emails can go on the appointment without each needing an account. Fluently models a language as a collection and levels (A1, A2, B1) as variants of a group class. Tensile goes further for a Pilates studio: one variant per class, studio and instructor, capped at the number of reformer beds in that room.
The gotcha nobody expects: once a class is full, the API stops returning that slot. To the page, a full Tuesday class simply isn't there. Tensile handles it by learning each series' weekly pattern, showing the missing week as Full, and linking to the next class of the same type with space. You'll want the same, because a timetable with mysterious gaps reads as "the studio is closed".
That's also a real limit to know about: Opencals has no waitlist. When a class fills, the best the page can do is point people at the next one.
Build a booking site for Tensile, a reformer Pilates studio in Berlin with
three studios. The home page is a weekly timetable across all studios:
class, instructor, studio, and places left as small dots. A full class shows
as Full and links to the next class of the same type with space. Clicking
a class goes to party size, extras, then checkout. Warm plaster background,
technical sans-serif, spring colours for intensity.4. A padel club sells courts, and the grid is the product
Players don't want a list. They want to see every court at once and spot the free 19:30. So the club page is a grid: courts down the rows, half hours across, and a duration choice (60, 90, 120 minutes) when you tap a cell. VOLT adds a hall map, every court drawn from above and lit by whether it's free at the chosen time, over the same data.
There's no endpoint that returns availability for several courts in one call, so the naive build asks for every court on every day from the browser and runs into the rate limit by Tuesday. VOLT reads each court's merged free ranges for the whole booking window once, from a server route, caches it for 60 seconds, and drops the cache after a booking, cancel or reschedule. The browser then draws the grid, the hall map and every "free from 18:00" from those spans, so changing day, sport or game length makes no API calls at all.
GET /storefront/products/{courtId}/current-availability-ranges?timezone=Europe/MadridWhen the player picks a length, the page asks that one court again with duration (in seconds) to confirm the block fits, then books the slot it gets back.
Where AI builders go wrong: they create a "60-minute court" and a "90-minute court" as separate products. Separate products don't block each other, so the same court gets sold twice for overlapping times. One court, one bookable unit, with variable length switched on.
Build a booking site for VOLT, a padel and squash club with three locations.
Booking starts from one club (remember the last one). Show a courts × time
grid for the chosen day, half-hour columns, a now-line, and a length picker
(60/90/120) when a free cell is tapped. Read each court's availability ranges
once on the server and cache them; don't fetch slots per cell. Then players,
racket hire, details, payment. Dark, sporty, one hot orange accent.5. A studio hour is sold with its extras attached
A podcast or photo studio sells a room by the hour, and the extras cost more the longer the session runs: a second camera, an engineer, editing. The flow puts length first, because it changes which start times are possible: room, then one to four hours, then a start time, then extras, then pay.
The studio is a single product with custom duration on: a one-hour base (3600 seconds), a maximum length, and an hourly price. Passing duration to the availability call means only start times with enough free room after them come back, so a three-hour session never offers 16:00 when the room is booked at 18:00. Add-ons come in two kinds: fixed (an on-screen logo, quantity one) and duration-multiplied, where a per-hour extra on a three-hour booking becomes three units without anyone doing the sum. The cart total is the price; the page can show an estimate but shouldn't calculate the bill itself.
What I haven't solved: if the base unit is an hour, nobody can start at half past. Make the base thirty minutes and it works, but every half hour is then priced separately, which may not match the studio's rate card. VOLT hits the same trade-off with courts. There's no template for this shape yet; the podcast studio API walkthrough covers the model in detail.
Build a booking site for a podcast studio group in London with five studios,
each with its own hourly rate. Flow: studio (photo cards), then session
length 1–4 hours, then a start time for that length, then extras (second
camera, on-screen logo, cloud storage), then checkout. Show the cart total,
not a price you calculate. Warm, low light, lots of photography.6. A car is rented by the day, midnight to midnight
Rentals flip the picker. Instead of one day and a list of times, the customer chooses a pick-up date and a return date on a range calendar, and the car is blocked for the whole span. Then: delivered to an address or collected from the garage, a handover window, per-day extras like an excess waiver, a fixed extra like a child seat, a driving licence upload, and pay.
In NOIR Drive each car is its own product with a one-day base duration, variable length and a 24/7 schedule, so bookings can cross midnight. The calendar reads the car's merged availability ranges without a duration and checks on the client that pick-up to return fits inside one free range. The slot runs from 00:00 on the pick-up date to 00:00 on the return date in the store's timezone, sent as UTC: in Dubai, 10 to 13 November becomes 20:00 on the 9th to 20:00 on the 12th, three days at the daily rate. The handover window and flight number travel as custom attributes on the appointment, and the security deposit is a required checkbox question, taken at handover rather than charged online.
Where AI builders go wrong: they make one "Range Rover" product with each car as a variant. Variants don't block each other, so the same physical car can be booked twice. One car, one product.
Build a car rental site for NOIR Drive in Dubai, twelve cars. Each car page
has a two-month range calendar on live availability. Flow: car, pick-up and
return dates, deliver to my address or collect at the garage, a handover
window, per-day extras and fixed extras, licence upload, then payment.
Show days × daily rate + extras as an estimate; the cart total is the price.
All black, cinematic, champagne accents.7. A villa is booked by the night, and check-in time isn't up for discussion
A stay looks like a rental, but it's counted in nights and it starts and ends at fixed times. Check-in at 15:00, check-out at 11:00, and the day a guest leaves can be the day the next one arrives.
In Bali Villa Rentals each villa has a one-night base with variable length and fixed check-in and check-out times, and the server enforces them: a booking from the site that doesn't start at the fixed check-in or end at the fixed check-out gets a 400. A 15:00-to-11:00 stay of four nights is shorter than 96 hours and is still charged as four nights. Adults, children, infants, arrival time and flight number are custom attributes. Bookings the owner makes in the dashboard are exempt, so a guest can still be given an early check-in by hand.
Build a villa booking site for a resort of ten villas in East Bali. Each
villa page shows nightly rate, a check-in/check-out calendar on live
availability, and fixed times (check-in 15:00, check-out 11:00). Ask for
adults, children, infants and arrival time before checkout, then offer
airport transfer and floating breakfast. Changing dates keeps the same number
of nights. Warm, quiet, rice-field photography, a serif for headings.When one business is two shapes
Plenty of businesses sell two shapes at once, and the generated site usually only builds one. VOLT sells courts on the grid and coached sessions through the staff-first flow, both ending in the same checkout. NOIR Drive rents cars by the day and sells chauffeur packages with real drivers. Tensile has the class timetable plus private and duet sessions that share one private room.
The fix is to say so in the prompt: "Two ways in: book a court, or book a coach." Name both shapes and the builder will make two entry points into one checkout, instead of forcing coaching sessions into a court grid.
What none of these shapes should do in the browser
The shapes differ on screen. Underneath, the same handful of rules hold, and they're the ones a generated site breaks most often:
- Nothing about the catalogue is hard-coded. Services, prices, durations, staff, locations and hours come from the API.
- No availability, pricing, discount or tax logic in the app. Render what comes back; the cart's totals are the truth.
- The slot is held, not just picked. Booking a slot puts it in a cart with an
expiresAt. Show the countdown, extend the hold while the customer is typing, and start over cleanly if it runs out. - Times are UTC in the API. Convert for display and send back exactly what you received. Rentals and stays are where this slips most.
- The API key stays on the server. Lovable keeps it in connector credentials, v0 in an environment variable, Base44 in an app secret, a Next.js app in server code.
- Real errors reach the customer. "That time was just booked, please choose another" beats "Something went wrong". Show the API's message and keep the customer's selections.
- Cache the catalogue and use availability ranges. Behind a builder's gateway every visitor can share one IP, and Lovable sites get about 120 requests a minute in total. The grid in shape 4 is where this matters most.
And the limits worth hearing before you pick: Opencals has no waitlist, the booking engine is hosted rather than open source (the templates, skills and SDK are MIT), and a development store is free for as long as you like but runs in test mode, so it can't take real money until you switch it to production.
Where to paste these prompts
The prompts work as written once the builder knows how to talk to Opencals. That's a one-time setup per builder: a custom connector with two knowledge files in Lovable, the open-source agent skills in v0, Claude Code, Cursor or Codex, and a backend function plus two skills in Base44. The guide to building a booking site with AI has the setup for each, and the skills and knowledge files already cover these shapes, so a short prompt is enough.
To test against real data without typing it in, create a development store and pick a seed dataset. Several match the templates above (salon, barbershop, padel club, medical clinic, villa resort, reformer studio), so the site your builder generates has services, staff and bookings on it from the first preview.
Frisor: staff-first barbershop
Barber picker, add-ons in one transaction, customer accounts.
Clear Care: guided clinic intake
Care Finder, video visits, pre-visit notes and photo uploads.
Tensile: class timetable
Places left per class, Full classes, privates sharing a room.
VOLT: court grid
Hall map and courts × time grid from one cached read per court.
NOIR Drive: day-range rental
Range calendar, delivery or collection, per-day extras.
Bali Villa Rentals: nightly stays
Fixed check-in and check-out, turnover days, guest details.
If you're building for a business that doesn't fit any of these seven, I'd like to hear what it is. The next shape I'm watching is a package builder, where choosing one thing changes which rooms or people are offered next, and I don't think any template does that well yet.
Frequently Asked Questions
Get started
Ready to transform your service business?
Join 150+ businesses already using Opencals. Start on a free development store with every feature unlocked, and only pay once you go live.





