The phone rings while you are with a client, messages pile up asking "any slot tomorrow at 3?", and on Saturday two customers show up for the same appointment. Meanwhile, someone else books and never arrives, leaving a staff member idle. Salons, clinics, photo studios, repair shops, and sports venues all know this pattern.
An online reservation app fixes most of it: customers pick a slot themselves, the slot locks instantly, and reminders go out automatically. This guide covers the features that matter, the business rules to settle first, how to reduce no-shows, and the technical design for developers building one in Laravel.
Start With Your Booking Model
| Model | Typical business | What is booked | Special needs |
|---|---|---|---|
| Staff-based | Salons, barbers, therapists | A person's time | Choose staff, per-staff schedules, per-service duration |
| Resource-based | Courts, meeting rooms, studios | A room or equipment | Fixed slots, peak pricing |
| Queue-based | Walk-in clinics, auto service | A place in a time window | Per-session quota, wait estimates |
| Capacity-based | Fitness classes, workshops | A seat in a session | Attendee limit, waitlist |
Many businesses mix models. A repair shop may use a queue for quick jobs and mechanic-based slots for major work. Write your model down before you choose or build anything.
Features That Actually Matter
Real-time calendar that locks slots
Slots must lock the moment a customer confirms, not when an admin approves later. Show only slots that fit the service length: a 30-minute haircut and a 90-minute coloring session should not share the same slot logic.
Per-staff and per-service availability
If customers can choose a stylist or therapist, the system must know each person's working hours, days off, and skills.
Automatic reminders with a reschedule link
Send a reminder the day before and a few hours before, by SMS, WhatsApp, or email. Include a one-tap reschedule link. People who cannot make it are more likely to move the booking than vanish if moving is easy.
Deposits
A deposit filters out casual bookings and protects part of your revenue if someone does not show. For long or high-value services it is usually worth it. Stripe, PayPal, and Square all support taking a deposit at checkout.
Reviews and simple reports
Request a review automatically when a booking is marked done. Track peak hours, popular services, no-show rate, and staff utilization to plan shifts and off-peak promotions.
Reducing No-Shows
- Require a deposit above a certain service length or value.
- Send two reminders with "Confirm" and "Reschedule" buttons.
- Set a clear cancellation window, for example 6 hours, after which the deposit is kept.
- Record no-show history per customer and require full prepayment after repeat no-shows.
- Offer a waitlist so freed slots can be filled quickly.
For Developers: Tables and Double-Booking Protection
A staff-based system needs at least services, staff, staff_schedules, bookings, and customers. If relationships are new to you, start with designing a simple ERD.
CREATE TABLE bookings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
customer_id BIGINT UNSIGNED NOT NULL,
staff_id BIGINT UNSIGNED NOT NULL,
service_id BIGINT UNSIGNED NOT NULL,
starts_at DATETIME NOT NULL,
ends_at DATETIME NOT NULL,
status ENUM('pending','confirmed','done','cancelled','no_show') DEFAULT 'pending',
deposit_amount DECIMAL(12,2) DEFAULT 0,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
INDEX idx_staff_time (staff_id, starts_at, ends_at)
);
Two bookings overlap when existing_start < new_end and existing_end > new_start. Run the check inside a transaction with a row lock so two simultaneous requests cannot both pass:
use Illuminate\Support\Facades\DB;
$booking = DB::transaction(function () use ($data, $start, $end) {
// Lock the staff row so parallel requests queue here
Staff::whereKey($data['staff_id'])->lockForUpdate()->firstOrFail();
$clash = Booking::where('staff_id', $data['staff_id'])
->whereIn('status', ['pending', 'confirmed'])
->where('starts_at', '<', $end)
->where('ends_at', '>', $start)
->exists();
abort_if($clash, 409, 'That slot was just taken. Please pick another time.');
return Booking::create($data + ['starts_at' => $start, 'ends_at' => $end]);
});
Without the lock, two requests can both run exists() before either saves, and both succeed. Send reminders from scheduled queued jobs, not inside the request, as described in Laravel queues and jobs. Store times in UTC and convert to the customer's time zone for display, especially if you serve more than one region.
Common Mistakes and Fixes
- Phone bookings kept in a paper diary. Every booking, however it arrives, goes into the same system.
- No reschedule option. Customers cancel or simply do not come. Put a reschedule link in every reminder.
- Long booking forms. Ask only for name, phone, service, and time. Accounts can come later.
- No buffer time. Add cleanup or prep minutes so staff do not run late all day.
- Statuses never updated. Without accurate done and no-show statuses, your reports are useless.
Rolling It Out Without Confusing Customers
Moving from phone and chat bookings to an online calendar works best in stages, so regulars are not lost and staff are not overwhelmed.
- Clean up your service list. Write each service name, its real duration (time it a few times), the price, and which staff can do it.
- Set hours and rules. Opening hours, breaks, days off, the cancellation window, and the deposit amount. Rules are harder to change once customers are used to them.
- Run a one-week internal test. Ask staff and a few friends to book through the system and note every confusing step.
- Move existing appointments in. Enter every upcoming booking from your diary or chat history before you share the link.
- Share the booking link everywhere. Your website, Google Business Profile, social media bio, and automatic message replies.
- Review after a month. Compare no-shows and the volume of booking messages with the previous month, then adjust reminders or deposits.
During the transition, keep accepting bookings by phone or chat, but have staff enter them into the system immediately. Over time, reply to booking requests with the link instead.
One more point for developers: decide early how cancellations free up a slot. A cancelled booking should change status rather than be deleted, so your no-show and cancellation reports stay accurate, and the overlap query above already ignores cancelled rows because it filters on pending and confirmed only.
Checklist for Choosing or Building a Booking App
- Supports your booking model.
- Slots lock automatically and are safe from double booking.
- Different durations per service, including buffers.
- Automatic reminders with a reschedule link.
- Configurable deposits and cancellation policy.
- Reports on no-shows, peak hours, and utilization.
- Customer data can be exported.
- Booking pages work well on a phone.
Even a one-person business benefits, because clients can book themselves without interrupting your work. For a broader view of moving operations online, see how to digitize a small business.