Tutorial: Setting Up an Online Clinic Queue with WhatsApp Integration for IT Practitioners
A technical guide to building an online clinic queue connected to WhatsApp: data schema, booking endpoints, Node.js webhooks, Meta message templates, and operational best practices.
A clinic with 3–5 polyclinics usually receives 60–120 patients per day, and about 40% of them pile up in the 07.00–09.00 range. Without a queue system, the average wait time reaches 45–60 minutes, while phone reservations record a 15–20% no-show rate because patients forget or silently cancel. WhatsApp can close both gaps at once: a registration channel already used by the majority of patients, plus an automatic reminder engine that is far cheaper than SMS. This tutorial discusses setting up a WhatsApp-integrated online queue from scratch — data schema, booking endpoint, webhook, up to message templates that pass Meta review — using Node.js 20 LTS, Express 4.19, MySQL 8.0, and WhatsApp Cloud API v20.0. We used the same pattern when building queues for clinic clients in Yogyakarta and Magelang.
Minimum Architecture Before Writing Code
There are five components that must exist, no more for now: (1) a transactional database for tickets, doctor schedules, and polyclinic quotas; (2) a booking API serving web, kiosk, and WhatsApp; (3) a WhatsApp gateway (official Cloud API or a local provider such as Fonnte/Wablas); (4) a queue worker for sending asynchronous messages; (5) a browser-based counter display that polls ticket status every 5 seconds. All these components are enough to run on one VPS with 2 vCPU / 4 GB RAM for a volume of 500 patients/day.
Ticket numbering should be deterministic and easy for patients to read: {kode_poli}-{YYMMDD}-{urut}, for example A-250614-007. The sequential counter is stored per polyclinic per date in the antrian_counter table and incremented within a SELECT ... FOR UPDATE transaction so there is no duplication when two patients register at the same second. The daily reset happens automatically because the date is part of the key — no special job is needed.
The most common mistake in the field: polyclinic master data and doctor schedules are duplicated in the queue application, then differ from the Clinic SIM. Take master data from the Clinic SIM via a read-only API or replicate the poli and jadwal_dokter tables with synchronization every 15 minutes. If the clinic does not yet have a Clinic SIM, this is the most appropriate point to start organizing data before other services follow.
Separate the message-sending worker from the web process. The WhatsApp webhook must be replied to with HTTP 200 in less than 5 seconds, while calls to the Graph API can take 300–900 ms. The safe pattern: the webhook only validates + pushes the job to Redis 7.2 (BullMQ 5.x), then a separate worker takes the job and sends the message with 3x exponential backoff retry.
Step-by-Step Setup
Step 1, prepare the schema. Core tables: antrian (id, no_tiket, poli_id, dokter_id, tanggal, wa_number, status ENUM('booked','called','served','noshow'), created_at), kuota_poli (poli_id, tanggal, kuota, terpakai), and wa_log (antrian_id, template, status, wa_message_id). Add a unique index on (poli_id, tanggal, no_tiket) and an index on (wa_number, tanggal) to prevent double booking from the same number.
Step 2, create the POST /api/antrian endpoint. Validate three things before insert: the quota is not full, the doctor schedule is active on that date, and the patient does not already have an active ticket that day. Accept the Idempotency-Key header (UUID from the client) and store it in a unique column so double-tapping the button on a phone does not produce two tickets.
Step 3, connect the WhatsApp webhook. The patient types ANTRI A 2025-06-14; the system creates a ticket and immediately replies with the queue number along with the estimated service time (calculated from an average of 8 minutes per patient in that polyclinic).
// Express 4.19 + WhatsApp Cloud API v20.0
app.post('/webhook/wa', express.json(), async (req, res) => {
const msg = req.body.entry?.[0]?.changes?.[0]?.value?.messages?.[0];
if (msg?.type !== 'text') return res.sendStatus(200);
const [cmd, poli, tanggal] = msg.text.body.trim().toUpperCase().split(/\s+/);
if (cmd !== 'ANTRI') return res.sendStatus(200);
const tiket = await antrian.create({ poli, tanggal, wa: msg.from });
await wa.sendText(msg.from, `Tiket ${tiket.no_tiket} - Poli ${poli}, ${tanggal}. Estimasi ${tiket.estimasi}. Datang 15 menit lebih awal.`);
res.sendStatus(200);
});
Step 4, set up two reminder crons: H-1 at 19.00 (attendance confirmation with Yes/Cancel buttons) and on H-day 2 hours before the estimate (notification "your queue is 5 numbers away"). In our client implementation in Magelang, the combination of these two reminders reduced no-shows from 18% to 7% in six weeks. Also provide the POST /api/antrian/:id/cancel endpoint so the slot automatically returns to the quota.
Choosing the WhatsApp Channel and Composing Templates
For a serious clinic, use the official WhatsApp Cloud API. Since July 1, 2025, Meta uses per-message pricing, so utility messages outside the 24-hour customer service window are counted per message — recalculate your queue COGS: two utility messages per patient are enough. The advantages: the number is not at risk of being blocked, there is delivered/read status, and you can use quick reply buttons for attendance confirmation.
Templates must be submitted with the utility category, language id, and at least one variable. Example body that passes review: "Hello {{1}}, your queue ticket {{2}} for polyclinic {{3}} on {{4}}. Estimated service time is {{5}}. Reply YES to confirm or CANCEL to cancel." Avoid promotional words in the utility category — rejections most often occur because the template contains a call to action or a discount.
Local gateways (Fonnte, Wablas, Watzap) may be used for a 1–2 week pilot because setup takes only hours, but run them on a separate number from the clinic’s official number and prepare for migration. The whatsapp-web.js 1.23.0 library is only suitable for internal use (notifications to staff), not for patients — the ban risk is real and there is no SLA. If your clinic needs guidance in choosing an integration path that complies with regulations and volume, our IT consulting and SIM Klinik application development services cover the DIY and Central Java areas.
Best Practices
- Always respond to the webhook with HTTP 200 first, do the heavy processing in a worker — Meta will retry up to 7 times if it receives a status other than 2xx, and duplicate tickets almost always originate here.
- Limit one WhatsApp number to a maximum of 2 active tickets per day; this breaks the queue-scalping practice that is still rampant in busy clinics.
- Store
wa_message_idand delivery status for auditing; when a patient complains about not receiving a ticket, you have objective evidence without arguing. - Show the estimated service time, not just the queue number. Patients adhere more to times than to abstract queue numbers.
- Provide a manual fallback at the counter: one button to issue a ticket without WhatsApp, because 3–5% of patients still arrive without a phone or with an inactive number.
FAQ
Can one WhatsApp number be used for registration and educational broadcasts?
Technically yes, but it is not recommended. Separate the number for queue transactions (utility) and the number for content/promotion (marketing), because template categories and per-message rates differ. Mixing categories is a common reason templates are rejected during review.
What is the minimum infrastructure cost per month?
A VPS with 2 vCPU/4 GB is around IDR 150–250 thousand, domain and SSL are free, WhatsApp costs follow Meta’s per-message pricing plus provider markup if using a BSP. For 100 patients/day with 2 utility messages, WhatsApp channel costs are usually still below IDR 100 thousand per month.
What if the clinic does not yet have a SIM Klinik?
Start with a self-service queue module with a polyclinic table and doctor schedules managed through a simple admin panel. Once volume is stable, migrating to SIM Klinik is much easier because the patient and visit data structures are already in place and do not need to be rebuilt from scratch.
Are patient WhatsApp number data safe from a regulatory perspective?
Store them encrypted, restrict access by role, and record access logs. WhatsApp numbers are personal data, so provide an opt-in mechanism at first registration and an option to stop receiving reminders at any time via a STOP reply.
Online queuing is not a big project — with one booking endpoint, one webhook, and two reminder cron jobs, a clinic with 100 patients/day can already reduce waiting time by 30–40% and cut no-shows by more than half. The key to success actually lies in small disciplines: master data synchronized with SIM Klinik, idempotency, and a worker separate from the web process. If you are preparing queuing for a clinic in Yogyakarta, Magelang, or other DIY areas and want an implementation that connects directly to SIM Klinik, ERP, or pharmacy POS, our team is ready to help from architecture design to go-live. Schedule a short IT consulting session to map requirements and cost estimates.
This article is part of the knowledge base of Nugroho Setiawan, an IT consultant in Jogja (Yogyakarta). Related solutions: PilarPOS Clinic Management System. Need a free consultation?
Komentar
Belum ada komentar. Jadilah yang pertama!
Saya membantu konsultasi IT & pengembangan aplikasi untuk rumah sakit, klinik, dan bisnis di Yogyakarta (DIY), Magelang, dan sekitarnya.