Softechinfra
Business

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.

ManviManvi
November 7, 202514 min read
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 was losing 28% of booked appointments to no-shows. They had 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 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

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.

Why This Matters Now

Indian diagnostics chains were the silent victim of the smartphone era. Off-the-shelf LIMS like LabVantage charge yearly licence fees 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 affordable to build because Next.js + Vercel + Postgres covers most of the heavy lifting and the WhatsApp Business API is now stable for high-volume conversational use cases.

The other reason: the Indian SMS reminder literature 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 a fixed budget 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.

    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)

    LayerChoiceAlternativeWhy
    FrontendNext.js 14 App RouterPlain React + ViteServer components meant patients on 4G in Ujjain saw the slot grid in 1.4s vs 3.8s with client-rendered React.
    DatabasePostgreSQL 16 (Supabase)MongoDB AtlasBooking is fundamentally relational — patient, branch, slot, test panel, payment. We would not pick Mongo for this even at gunpoint.
    Cache / Pub-SubRedis (Upstash)In-memoryReroute engine needed pub-sub across regions. Upstash's pay-per-request model fits an SMB volume curve.
    WhatsAppWati on top of WhatsApp Business APIAiSensy or GallaboxWati'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.
    IVRExotelTwilioExotel'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 any reception manager can update.
    HostingVercel Pro (frontend) + Supabase Pro (backend)Self-hosted HetznerThe 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

    Build cost breakdown Engineering (2 devs × 9 weeks) UX/Design (Khushi, 4 weeks) PM + QA (Manvi, 6 weeks) Exotel IVR setup + initial credits WhatsApp template + Wati setup

    Run cost (steady state, month 4): Vercel Pro, Supabase Pro, Upstash Redis, Wati Premium (5K conversations), Exotel (2,400 IVR minutes), WhatsApp Business conversation charges (~12,000 user-initiated), plus Meta marketing-template charges, for 95,000 monthly tests booked through the portal.

    The Outcome (Numbers That Mattered to the Owner)

    28% → 13%
    No-Show Rate
    −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 been a big extra cost 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 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 lower than Twilio's at our test volume, and that adds up for a 9-clinic chain doing ~2,400 IVR minutes/month. 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 plan 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, 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. Those rerouted bookings also saved the aggregator commission. 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: 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.

    Book a Build Call

    Tags:
    HealthcareWhatsApp Business APIBooking SystemDiagnosticsCase StudyNext.jsExotel
    Share this post:
    Manvi

    Manvi

    QA Tester at Softechinfra with expertise in CRM testing and quality assurance.