Digital Queue System for Services

A crowded waiting room, customers asking "what number are we on?" every five minutes, and staff shouting names across the counter are signs that the service flow has outgrown pen a...

Digital Queue System for Services

A crowded waiting room, customers asking "what number are we on?" every five minutes, and staff shouting names across the counter are signs that the service flow has outgrown pen and paper. It is not only about comfort: people who wait without any sense of progress are more likely to walk out. This guide covers how to design a simple, reliable digital queue system, whether you are choosing a product or building one yourself.

Parts of a Digital Queue System

  • Ticket issuing: a kiosk or tablet on site, a mobile web page reached through a QR code at the door, or registration by staff.
  • Calling: the agent presses "call next" and the system assigns the next ticket by order and service type.
  • Display screen: a TV in the waiting area showing which number each counter is serving, ideally with an audio announcement.
  • Notifications: a text message when the customer is a few places away, so they can wait outside.
  • Metrics: average wait time, average service time, and peak hours.

You rarely need all five on day one. A one-bay repair shop might only need tickets and a display. A multi-department clinic needs service types and reporting.

Choosing a Type of Solution

OptionProsConsGood fit
Physical ticket dispenser + displayFamiliar, no phone neededHardware and paper costs, hard to analyzePublic offices with visitors of all ages
Queue SaaS (e.g. Qminder, Waitwhile)Live within days, no server to runMonthly fee, data held by a vendor, limited customizationBusinesses that want to start this week
Custom web appFully tailored, links to customer or patient recordsNeeds a developer and hostingClinics and shops that already run other systems
Messaging-based queueNothing to install for customersNeeds an official messaging APIAppointment-driven services

The criterion most people forget is integration. If a clinic's ticket is not linked to the patient record, staff still retype identity details into another system.

Design the Flow Before the App

  1. Define service types. In a repair shop, for example: "Quick Service" (A), "Major Repair" (B), "Pickup and Payment" (C). Each has its own number sequence.
  2. Map counters to services. Counter 1 handles A and B; counter 2 handles only C. That drives the "call next" logic.
  3. Define ticket statuses. At minimum: waiting, called, serving, done, skipped.
  4. Decide the no-show rule. Can a skipped customer be recalled, and how many times?
  5. Decide when numbers reset. Usually daily, back to A-001.

Table Design

services : id, code (A/B/C), name, avg_minutes
counters : id, name, is_active
counter_service : counter_id, service_id
tickets  : id, service_id, number, queue_date, status,
           counter_id (nullable), customer_phone (nullable),
           called_at, served_at, finished_at, created_at
UNIQUE (service_id, queue_date, number)

The timestamp columns feed every metric you will want later, so keep them. For the reasoning behind relations like these, see designing a simple ERD for your app.

Issuing Collision-Free Numbers in Laravel

The classic bug in home-grown queue apps is two customers receiving the same number because they tapped at the same moment. The app reads the last number, adds one, and saves, with no locking. The fix is a database transaction with lockForUpdate, backed by a unique index.

use App\Models\Service;
use App\Models\Ticket;
use Illuminate\Support\Facades\DB;

public function take(Service $service): Ticket
{
    return DB::transaction(function () use ($service) {
        // Lock the service row so concurrent requests wait their turn
        Service::whereKey($service->id)->lockForUpdate()->first();

        $last = Ticket::where('service_id', $service->id)
            ->whereDate('queue_date', today())
            ->max('number');

        return Ticket::create([
            'service_id' => $service->id,
            'queue_date' => today(),
            'number'     => ($last ?? 0) + 1,
            'status'     => 'waiting',
        ]);
    }, attempts: 3);
}

"Call next" works the same way: inside a transaction, select the oldest waiting ticket for services that counter handles with lockForUpdate(), then set it to called with the counter id and called_at. Without the lock, two counters can call the same person.

Display Screen and Announcements

The simplest display is a page that polls a JSON endpoint every three to five seconds. For a single site with a few counters, that is perfectly adequate and easy to maintain. For instant updates, use Laravel broadcasting with Laravel Reverb or a hosted service such as Pusher.

For audio, the browser's built-in Web Speech API (speechSynthesis) works with many languages, or you can stitch together recorded clips. Browsers block autoplay audio until the user interacts, so add an "enable sound" button staff press once each morning.

Wait Estimates and Notifications

A simple estimate is people ahead multiplied by average service time, divided by active counters. For example, 6 people ahead, 8 minutes per service, and 2 counters gives about 24 minutes. Show a range ("about 20-30 minutes") rather than a precise figure, and compute the average from the last two to four weeks of data. Send "you are 3 places away" messages through SMS or an official messaging API from a queued job, as covered in Laravel queues and jobs, so the call button stays fast.

Metrics That Matter

  • Wait time: called_at minus created_at, averaged and maximum per hour.
  • Service time: finished_at minus served_at, per service type and counter.
  • No-show rate: share of skipped tickets. A high rate may mean estimates are scaring people off.
  • Hourly distribution: tickets per hour across the week, to decide when to open an extra counter.

Common Mistakes

  • Duplicate numbers from missing locks. Fix: transactions, row locks, and a unique index.
  • Display dies when the internet drops. Add a backup connection or run a local server where connectivity is unreliable.
  • No priority lane. Seniors, pregnant visitors, or booked appointments need their own service type, not manual queue-jumping.
  • Agents forget to press "done", corrupting service-time data. Fix: "call next" automatically closes that counter's previous ticket.

Launch Checklist

  • Concurrent ticket requests tested from several devices at once.
  • The display is readable from the back row.
  • Audio is clear and enabled in the browser.
  • Staff are trained on call, recall, skip, and done.
  • A paper fallback exists for power or network outages.
  • Daily number reset runs automatically.

A queue system is often the first step toward wider digitization, such as customer records or point of sale. If you want a head start, the Laravel service apps in the GudangCode catalog are worth studying as a base.

Yudhi
Written by
Yudhi
Founder & Lead Developer, GudangCode

Yudhi is the founder of GudangCode and a Laravel developer who has built dozens of ready-to-use business information systems — from POS and HRIS to management apps. He writes guides and articles on GudangCode to help Indonesian developers run, understand, and deploy Laravel source code correctly.

LaravelPHPMySQLSistem Informasi Bisnis See all articles by Yudhi
Want the full source code & apps?

Sign up free to download ready-to-use business applications, information systems, and Laravel source code.

Sign Up Free & Download
Sistem Antrian Digital Sistem Informasi
Share this article
Back to Blog
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →

Get Full Access Now!

Join our membership and unlock exclusive access to all premium features. Fast, easy, and ready to use instantly.

Join Membership Now