Case Study · Emergency Dispatch Platform

AmbuQuik — dispatch software where minutes are the metric.

An on-demand ambulance booking and emergency transport platform: a patient app, a driver app and a dispatch console, sitting on a real-time backend that finds the nearest available ambulance in expanding rings — and escalates automatically when an SOS goes unanswered.

AmbuQuik — on-demand ambulance booking and dispatch platform built by OET
Domain
Emergency medical transport
Surfaces
Patient app, driver app, console
Core build
Flutter · GraphQL · Redis
Status
Built & deployment-ready
The Brief

Booking a cab and booking an ambulance are not the same problem.

The surface resemblance is obvious — a map, a pickup point, a nearby vehicle. But the failure modes are completely different. If a cab request finds no driver, the rider waits and tries again. If an ambulance request finds no driver, that is the emergency.

So the system had to be built around what happens when things don't go smoothly: no driver in range, a driver who doesn't respond, an SOS raised mid-trip, a dispatcher who hasn't acknowledged an alert. Each of those needed an explicit, time-bounded answer in code — not a retry button.

The second requirement was a human in the loop. Emergencies arrive by phone call as often as by app, so the console had to let a dispatcher place a booking on a caller's behalf and watch it through to completion.

The design principle: every path where the system could stall silently gets a deadline and an automatic next action. Nothing waits forever for a human to notice.

The Dispatch Engine

Nearest first, then wider — on a clock.

Matching runs as a continuous service rather than a one-shot query. It searches geospatially, offers to one driver at a time, and widens the net when nobody takes it.

Search the inner ring

A Redis geospatial index returns available ambulances closest to the pickup point, ranked by real distance.

Offer, with a timer

The request goes to one driver with a 30-second window. No answer is treated as a decline — the offer moves on rather than stalling.

Widen the radius

If the ring is exhausted, the search grows outward in 3 km steps up to a 15 km ceiling, picking up ambulances further out.

Fail loudly, not silently

An overall 5-minute ceiling ends the search and surfaces it, so a request can never sit unmatched and unnoticed.

Why It's Built This Way

Event-driven, so it reacts in milliseconds instead of polling.

Driver locations stream continuously into a Redis geospatial index, which answers “who is near this point right now” without touching the primary database. New orders and driver rejections publish onto Redis pub/sub channels, so the matcher reacts to events the moment they happen rather than waiting for its next sweep.

Because it runs on shared Redis state rather than in-process memory, the matcher scales horizontally — more instances can run behind the same queue without two of them offering the same ambulance to two different patients.

Trip updates reach the apps over GraphQL subscriptions on WebSockets, so the patient's map moves as the ambulance moves, with no polling and no stale positions.

SOS Escalation

An alarm that escalates itself.

An SOS raised from inside a trip enters a state machine with hard deadlines at every stage. If a stage's clock expires, the alert escalates on its own — it never sits quietly in a queue waiting to be seen.

2 min

To acknowledge

From the moment the alert is raised, someone must pick it up. Past two minutes it escalates to the next tier.

5 min

To dispatch

Acknowledged isn't handled. If no action has been dispatched within five minutes, it escalates again.

15 min

To resolve

The alert stays open, and keeps escalating, until it is explicitly resolved — with every step recorded.

Accountability

Every alert leaves a paper trail.

Each SOS carries an activity log — who acknowledged it, when, what was dispatched, and how it closed. A supervisor reviewing an incident afterwards can reconstruct the full timeline instead of reasoning from memory.

The escalation service sweeps every 30 seconds, so a breached deadline is caught within half a minute of expiring rather than at the next shift change. Admin actions across the platform are recorded in a separate audit log.

Three Surfaces

Patient, driver, dispatcher — three apps, one backend.

Each surface talks to its own GraphQL schema. A patient's API simply doesn't contain the fields a dispatcher can reach, so role separation is enforced at the schema boundary rather than patched in with per-field checks.

Flutter · Patient

AmbuQuik

Book an ambulance in a few taps, then watch it approach in real time.

  • Phone + OTP sign-in
  • Waypoint picking with fare preview
  • Vehicle tiers and paid add-ons like a stretcher
  • Live tracking with in-trip chat
  • In-trip SOS
  • Scheduled bookings for planned hospital visits
  • Saved addresses, wallet, coupons, trip history
Flutter · Driver

AmbuQuik Driver

Go online, take the job, and keep the map moving without touching the phone.

  • Online/offline duty toggle
  • Incoming requests with a live countdown
  • Background location streaming, screen kept awake
  • Hand-off to the driver's preferred nav app
  • Document and vehicle verification at sign-up
  • Earnings, payout accounts and trip history
React · Operations

Dispatch console

The control room — and the reason a phone call can become a booking.

  • Manual booking on a caller's behalf
  • Live order board and SOS alert queue
  • Driver, patient and fleet management
  • Service tiers, zones and surge pricing
  • Payouts, complaints and marketing campaigns
  • Role-based operator permissions and audit log
The Commercial Layer

Fares, wallets and payouts, built for how operators actually run.

Zone-aware pricing. Fares combine a base charge, per-kilometre and per-minute rates and a minimum fare, configurable per vehicle tier and overridable per geographic zone with a surge multiplier — because a city centre and an outlying district don't cost the same to serve.

Prepaid dispatch. Ambulance services are configured to take payment before an ambulance is assigned, so the order lifecycle has an explicit awaiting-payment state ahead of the search — a deliberate difference from the ride-hailing default of pay-on-arrival.

Money that moves both ways. Patient wallets with top-up, coupons and gift cards on one side; driver earnings, payout accounts and payout sessions on the other, with fleet operators holding their own balances. Payments run through PhonePe with server-side checksum verification.

Two-way ratings. Patients rate drivers and drivers rate patients against review parameters the operator configures, rather than a fixed star scale nobody can tune.

The Stack

What it's built on

Mobile Apps

  • Flutter · Dart
  • flutter_bloc + hydrated state
  • get_it / injectable DI
  • Typed GraphQL codegen
  • Geolocation & background location
  • Firebase messaging, analytics, Crashlytics

Backend

  • Node.js · TypeScript
  • Apollo Server 4 (GraphQL)
  • WebSocket subscriptions
  • Prisma ORM over SQL
  • Redis — pub/sub, GEO, cache
  • BullMQ background queues

Console & Infra

  • React + Vite + TypeScript
  • Tailwind CSS · TanStack Query
  • Google Maps · Recharts
  • Docker Compose (API, SQL, Redis)
  • AWS S3 · PhonePe payments
  • Multi-provider SMS/OTP
Flutter TypeScript GraphQL Prisma Redis BullMQ React Docker

Need real-time dispatch, tracking or field operations?

Ambulances, service technicians, logistics fleets — the hard parts are the same: who's nearest, who's free, and what happens when nobody answers.

Start a conversation → See more work