Back to Blog
Use CasesAugmentBLU

Building a Custom Booking System in Laravel That Fits

Why a custom Laravel booking system beats a generic plugin or per-booking SaaS. Availability, double-booking, deposits, reminders and refunds, done properly.

Most businesses that take appointments start with whatever is to hand: a booking plugin bolted onto the website, or a hosted service that looked tidy in the demo. For a while it works. Then the cracks show. The tool will not let a treatment block two rooms at once, or it charges a fee on every single booking, or it cannot tell a sixty-minute appointment from a ninety-minute one, and your staff quietly go back to a paper diary to cope.

This is one of those areas where the gap between "good enough" and "actually fits" is wide, and where a custom build earns its keep more often than people expect. This post covers what makes booking genuinely hard, why generic tools struggle with it, and what Laravel gives you when you do it properly. It also says, plainly, when you should not bother.

Booking looks simple and is not

From the outside, taking a booking is just picking a time. Underneath, it is a tangle of rules that vary by business and rarely fit a template. A three-room physiotherapy clinic is a good example to hold in your head, so let us use one throughout: three treatment rooms, four practitioners (some part-time), and treatments of different lengths, some of which need a particular room or a particular piece of kit.

Now think about everything a booking has to respect.

Availability and capacity. A slot is only free if the practitioner is working, the room is empty, and the treatment fits in the time before the next appointment. A sixty-minute assessment cannot start at 16:30 if the practitioner finishes at 17:00. Capacity is not one number; it is the overlap of several diaries at once.

Resources and staff schedules. People take holidays, work different days, and have lunch, and rooms get used for more than one thing. A treatment that needs the shockwave machine cannot be booked into the room where it lives when another practitioner already has it. A booking consumes more than just a person's time.

Deposits and payments. No-shows cost money, so you may want a deposit, or full payment up front for certain treatments, or nothing at all for a regular who always turns up. The rule is rarely uniform.

Reminders. The single most effective thing you can do to reduce no-shows is remind people, at the right time, with an easy way to confirm or cancel. That sounds trivial and is not, because the reminders have to fire reliably whether or not anyone is logged in.

Cancellations and refunds. Someone cancels at 7am for a 9am appointment. What happens to their deposit? Can the freed slot be offered to someone on a waiting list? Who is allowed to override the policy?

None of these is exotic. The problem is that every business answers them slightly differently, and a generic tool has to guess. Usually it guesses wrong.

Why the generic tools frustrate

There is nothing shameful about starting with an off-the-shelf booking tool. For plenty of businesses it is the right call, and we will come to those. The frustration sets in when your operation has quietly outgrown what the tool was built to do.

A few patterns come up again and again.

The fees mount. A per-booking charge feels painless at twenty bookings a month. At eight hundred it is a meaningful line on the accounts, paid forever on every appointment, including the regulars who would have come anyway. You are renting access to your own customers.

The rules are rigid. The tool offers the booking model its designers imagined, and your business does not quite match it. You want a deposit on new patients but not returning ones, or a treatment that occupies two rooms, or a fifteen-minute buffer between appointments for cleaning. The tool says no, or only with a paid add-on that half works.

The branding is limited. The booking page looks like the tool, not like you. Customers click through to a third-party domain that breaks the trust you spent years building.

You do not control the data. Your bookings, your customer history, your no-show patterns, all of it sits in someone else's system, exportable as a CSV if you are lucky. You cannot ask it questions it was not designed to answer, and if the provider changes its pricing or shuts down, your operational history goes with it.

Individually these are irritations. Together they are the sound of a tool you have outgrown.

What Laravel gives you

Laravel is not a booking product. It is the foundation you build a booking system on, shaped to exactly how your business works, so the system fits your rules rather than the other way round. Here is what that looks like in practice.

Model availability and resources as they actually are

In a Laravel application you describe your world in proper data structures using Eloquent, Laravel's way of working with the database. Practitioners, rooms, treatments, equipment, opening hours, each becomes a model with its own rules and relationships. A treatment knows how long it takes, which room types it needs and whether it requires a particular machine; a practitioner knows their working pattern and their holidays. Availability is then something the system works out from all of these at once, not a fixed grid you maintain by hand.

This is the part generic tools cannot match, because they cannot know in advance that your ninety-minute sports massage needs the larger room and a fifteen-minute gap after it. Your system can, because it was built knowing.

Stop double-booking with the database, properly

Here is the bit that separates a real booking system from a toy. Two people are looking at the same 10am slot. Both click "book" within a second of each other. Exactly one of them must get it, and the other must be told, cleanly, to pick another time. Get this wrong and you have two patients in reception expecting the same physiotherapist.

The wrong way to solve it is to check whether the slot is free and then write the booking, because between the check and the write, the other person can slip in. The right way is to let the database enforce it. The booking runs inside a database transaction that locks the relevant rows while it works: it re-checks the slot is genuinely free, writes the booking, and only then releases the lock. The second request waits its turn, finds the slot gone and is sent to choose again. This is boring, well-trodden engineering, and it is exactly the kind of correctness an off-the-shelf plugin often skimps on. It holds up under a Monday-morning rush when fifty people hit the site at once.

Send reminders through queued jobs

When a booking is made, the system schedules its reminders there and then as queued jobs: background tasks that sit on a queue and run at the right moment without anyone watching. A reminder goes out the evening before by email or text, with a one-tap link to confirm, reschedule or cancel. Because this work happens on a separate background worker, it never slows the booking itself, and it never depends on someone being logged in.

This is the highest-return feature in the whole system. A clinic losing even a handful of appointments a week to no-shows is losing real money, and a well-timed reminder recovers most of them. We have written about why this background work wants its own infrastructure in Laravel queues and dedicated workers; once reminders matter to your revenue, you want them on something reliable rather than squeezed in beside everything else.

Take deposits with Stripe or GoCardless

Payment fits naturally into the booking flow. For card payments, Laravel works cleanly with Stripe; for recurring bank debit, GoCardless. The customer pays a deposit, or the full fee, as they book. The payment is recorded against the booking, the slot is confirmed only once the money clears, and a refund on cancellation comes from the same screen staff already use. You decide the rules, deposit for new patients, nothing for regulars, full payment for a course of treatment, and the system follows them, because you wrote them.

Give staff a Filament admin and customers a clean portal

Your team gets a FilamentPHP admin: a tidy, professional screen showing the day's diary, each practitioner's schedule and every room. They can block out a holiday, drop in a walk-in, move an appointment, see a patient's history and issue a refund, all without touching code. Customers get a separate, plainly branded portal where they book, reschedule, cancel within your policy and see their past appointments. Both sides read from and write to the same single source of truth, so the diary your staff see and the availability your customers see can never drift apart.

Sync to the calendars people already use

Most staff live in a calendar already, so the system meets them there. Bookings can be pushed straight into Google Calendar, or sent as ICS files, the standard calendar format that drops into Outlook, Apple Calendar and almost anything else. Customers get a calendar invitation with their appointment. The booking system stays the authority; the calendars are simply a convenient window onto it.

A morning at the clinic

Follow a single booking through the three-room clinic. It is Tuesday, and a new patient wants a sixty-minute initial assessment. The system looks across all four practitioners, their hours that week, the three rooms and the gaps already booked, and offers only the slots where a qualified practitioner, a suitable room and a clear hour all line up. The patient picks 11am Thursday with a particular physiotherapist.

Because they are new, the clinic's rule asks for a deposit, so they pay it by card through Stripe as they book. The moment payment clears, the booking runs inside a transaction that locks the slot, confirms it is still free and writes it down. Had someone grabbed 11am a second earlier, this patient would have been shown the next free time instead, with no awkward double-up. The booking lands in the practitioner's Google Calendar, and two reminder jobs are quietly scheduled.

On Wednesday evening the first reminder goes out: a short email with the details and a link to confirm or cancel. The patient taps confirm. On Thursday morning the practitioner glances at their usual calendar and sees the 11am already there. Reception opens the Filament admin, sees the day laid out room by room, and knows the deposit is paid. Nobody re-keyed anything, nobody double-booked, and the no-show a paper diary might have invited never happened. Had the patient cancelled in time, the deposit could be refunded from that same screen in a couple of clicks, and the freed slot offered to the next person waiting.

When you should not build this

We would be doing you a disservice if we pretended a custom build is always the answer. It is not.

If you take a handful of bookings a week, your rules are simple, one resource, fixed-length appointments, no deposits, and a per-booking fee on a cheap hosted tool barely registers, then build nothing. Use the off-the-shelf tool, spend the money elsewhere and get on with your work. The same is true for a brand-new venture still finding out what its booking rules even are; settle those with something cheap and disposable first, then build once you know.

Custom starts to pay off when the picture changes. When volume is high enough that per-booking fees have become a real cost. When your rules genuinely do not fit any tool you have tried, and you are forever working around it. When booking is not a side feature but the core of how the business runs, so the data and the experience are worth owning. When you need it joined up with your accounts, your patient records and your reporting rather than stranded in a silo. At that point a tool that fits, that you control, and that does not tax every appointment, repays the build cost in a year or two and keeps paying after.

It is also worth saying that booking often wants to feel live: slots vanishing as others take them, a diary that updates without a refresh. That is very achievable, and we cover the approach in real-time features with Laravel and WebSockets if it matters to your customers.

If you take bookings and the tool you are using has started to feel like a constraint rather than a help, it is worth a conversation. Book a free, no-obligation consultation and we will look honestly at what you are using, tell you plainly whether an off-the-shelf tool would still serve you better, and if not, show you what a system built around your actual rules would look like. There is more on how we approach this kind of work on our custom Laravel development page. AugmentBLU is an Edinburgh studio working with businesses across Scotland and the UK, and we reply within 24 hours.

Frequently asked questions

How do you stop two customers booking the same slot at once?

With the database, not the application code. When a slot is taken, the booking runs inside a transaction that locks the relevant rows, checks the slot is still free, then writes the booking. A second request for the same slot waits, finds it gone and is told to pick another. It is reliable even under load.

Is a custom booking system worth it over a plugin or SaaS?

It depends on volume and how odd your rules are. If a cheap tool fits and the per-booking fee is small, keep it. Once fees scale with bookings, the rules do not fit, or booking is central to your business, a custom Laravel build usually pays back within a year or two and you own the data.

Can a Laravel booking system send automatic reminders?

Yes. Reminders are queued jobs scheduled when a booking is made, then sent by email or SMS a set time before the appointment. Because they run on a background worker they never slow the booking itself, and a reminder the day before, with an easy way to confirm or cancel, is the single most effective way to cut no-shows.

How do you take a deposit when someone books online?

Through Stripe for cards or GoCardless for bank debit, both of which Laravel works with cleanly. The customer pays a deposit or the full amount as they book, the payment is recorded against the booking, and the slot is only confirmed once payment clears. Refunds on cancellation can be issued from the same admin screen.

Can staff manage availability and bookings themselves?

Yes. Staff get a FilamentPHP admin showing the diary, each practitioner's schedule and every room or resource. They can block out holidays, add walk-ins, move appointments and issue refunds without touching code. Customers get a separate clean portal to book, reschedule and see their history. Both read from the same single source of truth.

Will a custom booking system sync with our calendars?

Yes. Bookings can be pushed to Google Calendar or sent as ICS files that drop straight into Outlook, Apple Calendar or anything that reads the format. Staff see appointments in the diary they already use, and customers get a calendar invitation with the booking. The system stays the source of truth; the calendars are a convenient view.