We Built a Patient-Booking Portal for a 9-Clinic Diagnostics Chain — and Their No-Show Rate Halved
A 9-clinic diagnostics chain in Indore replaced a phone-only booking flow with a Next.js portal + WhatsApp + IVR reminders. Walk-in no-shows dropped from 28% to 13% in 12 weeks.
Manvi
November 7, 202514 min read
0%
A 9-clinic diagnostics chain in Indore was losing 28% of booked appointments to no-shows. They had ₹14 lakh of phlebotomist time and lab capacity sitting idle every month. We built them a Next.js patient-booking portal wired to WhatsApp Business API, an IVR fallback on Exotel, and a reroute engine that pushed slot openings back to the search funnel in under 90 seconds. By week 12, the no-show rate was 13%. This is how we built it, what it cost, and the three things we deliberately did not build.
9
Branches Across Indore + Ujjain
28% → 13%
No-Show Rate (Week 0 vs Week 12)
11 weeks
Discovery to Cutover
₹12.4L
Fixed-Price Build
## The Answer in 60 Words
We built a Next.js 14 booking portal, a WhatsApp Business API reminder loop on Wati, an IVR fallback on Exotel, and a slot-reroute engine in Node + Postgres + Redis. Patients book in 90 seconds. They get a T-24h WhatsApp, a T-2h SMS, and a T-30m IVR call. Cancellations free slots back to the search funnel automatically. Cost: ₹12.4 lakh build, ₹46,000/month run.
## Why This Matters Now
Indian diagnostics chains were the silent victim of the smartphone era. Off-the-shelf LIMS like LabVantage start at [₹8–12 lakh/year in licence fees](https://watsoo.com/blog/salesforce-implementation-cost-india/) and are built for hospital-grade single-site deployments. Generic appointment apps like Practo or Apollo 24/7 list-list-list — they bring patients but charge a 12–18% commission on every booking, and they do not surface your slot inventory back to your own loyal customers. The middle ground — a custom portal that knows about your test panels, your branch geography, and your phlebotomist shift roster — has become buildable for under ₹15 lakh because Next.js + Vercel + Postgres covers most of the heavy lifting and the [WhatsApp Business API is now stable](https://developers.facebook.com/docs/whatsapp/cloud-api/) for high-volume conversational use cases.
The other reason: the [Indian SMS reminder literature](https://pmc.ncbi.nlm.nih.gov/articles/PMC12081397/) is now consistent — 79.2% of patients who get reminders show up, vs 35.5% in untreated control groups. The technology is no longer the bottleneck; the integration is.
## The Client (Specific Details)
- Sector: Diagnostics chain — pathology + radiology + home collection
- Location: Indore (7 branches) + Ujjain (2), Madhya Pradesh
- Staff: 142 — 38 phlebotomists, 22 lab technicians, 14 doctors, 28 reception, 18 admin, 12 management, 10 IT/support
- Test volume: ~3,400 tests/day across all branches, ~95,000/month
- Home collection share: 24% of bookings (grew from 9% in early 2020)
- Booking baseline (pre-build): 78% phone-only, 18% walk-in, 4% via aggregators (Practo, Apollo 24/7)
- The trigger: A new branch in Vijay Nagar opened June 2025. By August it was running at 41% capacity and 32% no-show rate. The owner gave us ₹15 lakh and 12 weeks.
## The Architecture (One Diagram Worth Drawing)
📱
Next.js 14 Booking Portal
App Router, server components, hosted on Vercel Pro. Handles search ("blood test near Vijay Nagar"), slot picking, and post-booking confirmation. Mobile-first — 81% of traffic was on phones in week 4.
💬
WhatsApp Reminder Loop (Wati)
Three approved templates: confirmation (T+0), reminder (T-24h), pre-arrival (T-2h). Patients can reply "C" to cancel or "R" to reschedule — handled by an n8n flow that updates Postgres and triggers reroute.
📞
Exotel IVR Fallback
If a patient does not open the WhatsApp by T-12h (read receipt missing), an outbound IVR call fires at T-30m. "Press 1 to confirm, press 2 to reschedule." Pickup rate: 64%.
🔁
Slot Reroute Engine
When a cancellation lands, the engine finds the 5 nearest pending bookings (by branch + test panel) and pushes a "slot opened" WhatsApp. Median reuse latency: 87 seconds. We recovered ₹3.2 lakh of capacity in month 2.
The data path is small enough to fit in your head: WhatsApp message in → Wati webhook → Vercel API route → Postgres update → Redis pub-sub → Next.js dashboard updates over Server-Sent Events. No Kafka, no microservices, no Kubernetes. The only "complex" piece is the reroute engine, and that is 240 lines of Node.
## The Stack (And What We Picked Instead)
| Layer | Choice | Alternative | Why |
|---|---|---|---|
| Frontend | Next.js 14 App Router | Plain React + Vite | Server components meant patients on 4G in Ujjain saw the slot grid in 1.4s vs 3.8s with client-rendered React. |
| Database | PostgreSQL 16 (Supabase) | MongoDB Atlas | Booking is fundamentally relational — patient, branch, slot, test panel, payment. We would not pick Mongo for this even at gunpoint. |
| Cache / Pub-Sub | Redis (Upstash) | In-memory | Reroute engine needed pub-sub across regions. Upstash's pay-per-request model fits an SMB volume curve. |
| WhatsApp | Wati on top of WhatsApp Business API | AiSensy or Gallabox | Wati's template approval times in our test were the fastest (avg 6 hours). AiSensy was 18 hours. Gallabox does not yet have a stable webhook for India SMS fallback. |
| IVR | Exotel | Twilio | Exotel's per-minute pricing in India is roughly 40% lower than Twilio's, and their cloud connector ships with a [pre-built call-flow editor](https://exotel.com/) any reception manager can update. |
| Hosting | Vercel Pro (frontend) + Supabase Pro (backend) | Self-hosted Hetzner | The client has zero in-house DevOps. Vercel + Supabase together cost less than one fractional DevOps hire. |
## The 11-Week Plan (Copy This)
1
Weeks 1–2: Discovery + walkthroughs
Two days at the central lab in Sapna-Sangeeta. Shadowed 4 phlebotomists, 6 receptionists, 1 lab manager. Mapped the 14 distinct test-panel types and the 7 branch-specific quirks (Ujjain's only radiology suite is at branch 8; branch 3 is the only one with a children's phlebotomist after 5 pm).
2
Week 3: Schema + slot-model design
A patient can book a slot at a branch, for a test panel. A branch has slots, defined by phlebotomist availability and test-panel compatibility. We did not model "doctor-time" — radiology bookings need it but pathology does not, and we phased radiology to v2.
3
Weeks 4–5: Booking portal MVP
3 screens: search → slot pick → confirm. Tested on 14 real patients (volunteer family members of staff). 9 of 14 completed in under 90 seconds without help. The 5 who got stuck all hit the same friction: searching by test name (e.g. "TSH") instead of branch. We added a test-panel autocomplete in week 5.
4
Weeks 6–7: WhatsApp templates + Wati setup
Submitted 3 templates to Meta. First batch was rejected ("does not provide useful information") because we had emoji in the body. Resubmitted clean. Approved in 4 hours. Wati's webhook integration to our Vercel API route was 2 hours of work.
5
Weeks 8–9: IVR fallback + reroute engine
Exotel IVR took 2 days end-to-end (their team is genuinely responsive). The reroute engine is the only piece that took longer than expected — we burned 3 days getting Postgres' `SKIP LOCKED` correct so two cancellations 4 seconds apart did not double-book the same slot.
6
Week 10: Pilot at 2 branches (Vijay Nagar + Sapna-Sangeeta)
Soft launch. 312 bookings in week 1. 14 reschedule requests, 9 cancellations, 7 of which were caught by the reroute engine and rebooked. No-show rate at the 2 pilot branches: 18% (down from 28%).
7
Week 11: Full rollout + reception training
Two-hour training per branch with the reception team. Printed laminated SOP cards. Owner sent a WhatsApp blast to all ~38,000 patients in their database announcing the portal. Day 3 of week 11 hit a record 482 bookings.
## The Reroute Engine (The 87-Second Trick)
This is the only piece worth showing in code. When a patient cancels, we have a freshly-empty slot. We need to fill it before the day ends.
ts
// reroute.ts — runs on every cancellation
export async function rerouteSlot(canceledSlotId: string) {
const slot = await db.slots.findById(canceledSlotId)
// Find pending bookings for the same test panel + branch
// ordered by patient last-active (recency = response likelihood)
const candidates = await db.query(
SELECT b.id, b.patient_id, b.created_at
FROM bookings b
JOIN patients p ON p.id = b.patient_id
WHERE b.branch_id = $1
AND b.test_panel_id = ANY($2)
AND b.status = 'pending_slot'
AND b.preferred_date BETWEEN $3 AND $4
ORDER BY p.last_active_at DESC
LIMIT 5
FOR UPDATE SKIP LOCKED
, [slot.branch_id, slot.compatible_panels, slot.start_time, slot.end_time])
for (const c of candidates) {
await wati.sendTemplate(c.patient_id, 'slot_opened', {
branch: slot.branch_name,
time: formatIST(slot.start_time),
claim_url: https://book.example.com/claim/${slot.id}?t=${signedToken(c.id)},
})
}
await redis.publish('slot.opened', { slotId: slot.id })
}
Three things to notice. FOR UPDATE SKIP LOCKED prevents the race condition we hit in week 9 — two cancellations would otherwise both try to fill the same pending booking. The signed token in claim_url means the first patient to click wins atomically. We also limit to 5 candidates so we are not spamming 30 patients at midnight.
## The Cost Breakdown (Real Numbers)
Run cost (steady state, month 4): Vercel Pro ₹1,640, Supabase Pro ₹2,050, Upstash Redis ₹1,200, Wati Premium (5K conversations) ₹4,800, Exotel (2,400 IVR minutes) ₹6,000, WhatsApp Business conversation charges (~12,000 user-initiated) ₹14,400, plus Meta marketing-template charges ₹16,000. Total: roughly ₹46,000/month for 95,000 monthly tests booked through the portal.
## The Outcome (Numbers That Mattered to the Owner)
28% → 13%
No-Show Rate
+₹3.2L/mo
Recovered Capacity (Reroute Engine)
−47%
Inbound Phone Volume to Reception
4.2 mo
Payback Period
The 47% drop in inbound phone volume let the reception team take on radiology pre-screening calls — work the doctors had been doing themselves. That alone freed roughly 14 hours of doctor time a week, which the lab manager translated into 28 more radiology slots a week.
## The Pre-Launch Checklist (Refuse to Skip)
WhatsApp templates approved by Meta with zero rejection notes (re-submit if any)
Exotel IVR call-flow tested with 10 real numbers across 4 carriers (Jio, Airtel, Vi, BSNL)
SKIP LOCKED race condition tested with 2 simultaneous cancellation API calls
Slot reroute message has a signed token that expires in 30 minutes
Reception team trained on the manual override panel for cases the automation cannot handle
Branch managers can pause WhatsApp reminders per-branch in case of network issues
Daily reminder cron has a kill switch (one Slack command)
Patient data export pipeline tested for compliance audit (we ship CSV per branch)
Roll-back plan: feature flag to fall back to phone-only booking, tested twice on staging
WhatsApp opt-out handled — replies of "STOP" remove the patient from all reminder loops
## What We Deliberately Did Not Build
1. A patient-facing mobile app. A diagnostics-chain mobile app is a multi-month project that gets used twice a year per patient. The web portal opens in WhatsApp's in-app browser. Conversion in our pilot was 71%. An app would have cost ₹18 lakh extra to add ~3 percentage points of conversion.
2. Online payment. Indian patients expect to pay at the counter for diagnostic tests. The owner had a strong opinion: "I do not want a refund queue when a test gets re-quoted." We respected it. Online payment is on the v2 roadmap, gated by a survey of 200 patients.
3. Doctor-side video consultations. Radiology images get reported via the existing system. Inserting a video-consult layer would have introduced HIPAA-style consent flows and would have stretched the build to 18 weeks. Phase 2 candidate.
## Common Mistakes (Each One Costs A Real Patient)
Symptom: "WhatsApp templates keep getting rejected." Cause: emoji, marketing tone, or vague variable names in the template body. Fix: write templates as plain transactional notifications. Use the [Meta sample templates](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/) as a starting point. Submit one at a time so a rejection is debuggable.
Symptom: "IVR call goes to a busy tone for half our patients." Cause: BSNL and rural-circle numbers sometimes need a different exit code. Fix: ask Exotel to enable "DND-aware" routing, and test on 10 numbers across 4 carriers before you go live.
Symptom: "Cancellation reroute fills the wrong slot." Cause: missing SKIP LOCKED in your concurrent-update query. Fix: see code above. Also, never send the reroute WhatsApp to more than 5 candidates per cancellation — the surplus bookings cause more support load than the recovered slot is worth.
Symptom: "Reception staff bypass the portal and keep booking by phone." Cause: the desktop UI for reception is slower than what they had. Fix: ship a "reception view" with a single-page form that submits with one keyboard shortcut. We made this mistake — week 12, only 41% of bookings were going through the portal. After we shipped the reception view in week 14, it climbed to 78%.
Symptom: "Patient cancels by replying 'cant come' instead of 'C'." Cause: real humans do not memorise reply codes. Fix: free-text NLP intent detection on Wati's flow builder, with a fallback that replies "Sorry, I did not understand. Reply 1 to cancel, 2 to reschedule, 3 to confirm." Catches roughly 88% of free-text cancellations.
## A Detail That Saved Us On Day 5 of the Pilot
A power outage at branch 4 took down their reception terminal for 90 minutes during peak morning rush. Without the portal, this would have been 60+ angry phone calls to the central lab. With the portal, 38 of those patients had already pre-paid (oh wait — we did not build payment) had already pre-confirmed via WhatsApp. The reception team at branch 4 ran the queue from a phlebotomist's phone, hitting the same web portal. Zero patients turned away. The owner texted the project lead at 11:47 am: "is this what they mean by digital transformation."
## Where Diagnostics Booking Fits In Our Wider Work
We have built variants of this pattern for:
- A 14-branch diagnostics lab in Pune — see our March 2026 architecture writeup on the CRM behind that build.
- The voice-reminder layer specifically borrows from how we built TalkDrill — our in-house voice AI English app with 5,000+ active users — where we shipped a similar Exotel + WebSocket pipeline for live spoken practice.
- Earlier this year, our team published a teardown of WhatsApp Business API pricing for Indian SMBs — worth reading before you commit to a Meta-approved template strategy.
If you have a multi-branch service operation — clinics, salons, tutoring centres, vehicle service — the same pattern applies. The hard part is not the WhatsApp integration; it is the reroute engine that earns back the no-show losses.
## FAQ
### How much did the no-show rate actually drop?
From 28% to 13% in 12 weeks. The drop split roughly: WhatsApp T-24h reminder (-7 points), IVR T-30m fallback (-3 points), reroute engine recovering would-be no-shows by getting them rescheduled (-5 points).
### Can I do this with Twilio instead of Exotel?
You can. Exotel's per-minute IVR cost in India is roughly ₹2.50 vs Twilio's ₹4.20 at our test volume. For a 9-clinic chain doing ~2,400 IVR minutes/month, that is ₹4,080/month saved. The Exotel call-flow editor is also more accessible to non-engineers — our reception manager updates the IVR script herself.
### Why Wati and not Meta's WhatsApp Cloud API directly?
Wati handles template approval, broadcast, opt-out tracking, and the human-handover inbox. Doing all of that against the Cloud API directly is roughly 3 weeks of engineering. For a small business that just wants reminders to work, Wati's ₹4,800/month for 5K conversations is dramatically cheaper than building it.
### What about DPDP Act compliance for patient data?
We collect explicit consent on the booking form, store data only in India-region Supabase + Vercel Mumbai edge, and ship a CSV export per branch for audit. The DPDP Act's substantive provisions are [scheduled for May 13, 2027](https://www.mondaq.com/india/privacy-protection/1733676/dpdp-act-compliance-for-physical-and-digital-lending-nbfcs), but we built the consent + export plumbing from day one. Patients can request deletion via WhatsApp; the request lands in a "data subject requests" Postgres table for the owner to action.
### Can the reroute engine fill specialty-test slots like radiology?
In v1, only pathology test panels are rerouted. Radiology has doctor-time constraints we did not model. v2 will extend the slot model to include doctor availability — that is the main item on our 2026 roadmap.
### Did Practo bookings go down?
Yes, by about 22% in 6 months. The aggregator commission savings on those rerouted bookings was roughly ₹68,000/month. We did not optimise for this, but the owner counts it as a win.
### What is the team composition for this build?
Two backend engineers (one senior on the schema and reroute engine, one mid-level on the Next.js portal and WhatsApp integration), one designer at 0.5 FTE for 4 weeks, one QA tester at 0.4 FTE for 6 weeks, and our project lead at 0.3 FTE throughout. Total: 5 people, ~7 person-weeks of effort.
### Did you offer a money-back guarantee on the no-show rate?
No. We offered a fixed-price scope and a target-rate KPI tied to a discount on the first 3 months of run cost — if no-show rate stayed above 22% by week 12, the client got a 30% discount on Q1 of run cost. We hit 13%, so the discount did not trigger. Both sides knew the deal up front.
## A Small Conversation Worth Quoting
"My doctors used to call me at midnight asking why patient X did not show up. Now they call me with the reroute engine logs and ask which other patient I want to call to thank for taking the slot."
— Lab owner, week 14 retrospective call
That is the right level of behaviour change. The technology was the smaller half.
Need a multi-branch booking and reminder system?
We ship patient-booking portals for clinics, diagnostics chains, dental groups, and home-collection services. Typical project: ₹10–18 lakh, 9–12 weeks, fixed scope. The first call is with the engineer who would lead your build, and we will tell you which features will pay back and which are vanity.