AI · 5 min read
Medical marketing meets a full booking queue
A tested Python queue model shows how booking capacity changes a medical marketing funnel, and where its synthetic assumptions stop.
Suppose a campaign doubles the number of people asking for an appointment. The booking desk still has one person answering requests. Has the campaign worked?
Counting incoming requests says yes. Counting completed bookings gives a less comfortable answer. Some callers may leave before anyone reaches them, and the callers who stay may wait longer. A model that multiplies clicks by a fixed conversion rate cannot see this bottleneck.
Here is a small Python simulation that can. It contains no patient records, trained model or estimate of a real clinic's performance. Its job is to make one assumption visible: a booking request consumes staff time. Before adding an AI model to a marketing funnel, it is useful to know what happens when demand reaches that constraint.
Six requests, one desk
Use deliberately simple inputs: six requests arrive at minutes 0 through 5. Each takes five minutes to process. A caller will wait at most eight minutes before abandoning the queue. There is one desk, and requests are handled in arrival order.
The first request starts immediately and finishes at minute 5. The second waits four minutes. The third waits eight. The fourth would wait twelve minutes, so it leaves. It must not reserve five minutes of staff time on its way out.
The executable model keeps a heap of the times at which staff become free. The earliest time is always at the front:
for arrival in arrivals: start = max(arrival, free_at[0]) wait = start - arrival if wait > patience: abandoned += 1 continue heapq.heapreplace(free_at, start + service_minutes) waits.append(wait)Only accepted requests move a staff member's availability forward. Equality is intentional: an eight-minute wait is accepted. A different tie-breaking policy is possible, but it should be explicit and tested.
This is an offline scheduling calculation. It decides abandonment from the eventual waiting time rather than scheduling a separate abandonment event. Under these assumptions—known service duration, first-come service, equal patience and no interruptions—that is sufficient to count accepted requests. It is not a general-purpose event simulator. For a richer model, SimPy's bank reneging example shows requests competing with timeouts for a shared resource.
Change capacity and demand separately
Download booking.py and test_booking.py into the same directory. Run python3 -B booking.py; there are no third-party dependencies. These are the model's computed results, not observations of a clinic:
| Scenario | Requests | Completed bookings | Abandoned | Mean wait among accepted requests |
|---|---|---|---|---|
| One desk; arrivals at minutes 0–5 | 6 | 3 | 3 | 4 minutes |
| Two desks; the same arrivals | 6 | 6 | 0 | 3 minutes |
| One desk; arrivals at minutes 0–11 | 12 | 4 | 8 | 5 minutes |
Adding a desk clears the first fixture. Doubling requests without adding capacity produces one more booking and five more abandoned requests. The accepted share drops from 50% to about 33%. A campaign dashboard could show rising demand while the booking experience gets worse.
That result follows from the inputs. It does not prove that hiring someone pays for itself, or that a particular reminder system improves attendance. Neither wages nor appointment attendance appear in the model. A completed booking is also not completed treatment; the two events need separate records in any real evaluation.
Notice the denominator of the wait metric. It excludes abandoned callers. Reporting only accepted callers' wait can make an overloaded service look better than it feels. The table keeps abandonment alongside wait so the exclusion is visible.
What the experiment checks
The seven tests run with python3 -B -m unittest -v test_booking.py on Python 3.11.5. They check both fixtures, the exact patience boundary, empty input and invalid parameters. One case verifies that an abandoned request does not occupy the desk. Another checks that served plus abandoned equals incoming requests across 31 fixture sizes.
There is no closing time. Accepted work finishes even if it extends beyond the last arrival, and last_finish reports that extension. There are no priority patients, callbacks, breaks or variable handling times. Those omissions are manageable because they are visible. Adding them changes the question the model answers and calls for new checks.
For a next experiment, vary the five-minute service assumption before changing the arrival process. If a modest change in handling time reverses the proposed staffing decision, measuring that duration matters more than fitting a sophisticated demand model. Once randomness is introduced, compare repeated runs and retain the seeds; a single attractive run is not a forecast.
Where AI could enter
A demand forecast could provide arrival scenarios. A text classifier could estimate which kinds of booking requests need longer handling. Both require evaluation on data separate from their training data, as the healthcare model evaluation note demonstrates. Neither makes the queue model's assumptions disappear.
A reminder intervention is a different question. Giving the simulation a higher attendance probability after reminders merely encodes the benefit you hope to establish. Evidence for that probability must come from outside the simulation. Keep assumed intervention effects separate from measured ones in the input file and in the results you show stakeholders.
Using real healthcare information also changes the data-governance work. In the US, HHS distinguishes marketing from certain treatment and care-coordination communications, with authorization requirements and exceptions. A general encryption checklist cannot decide which category a proposed campaign belongs to. Consult the HHS marketing guidance when defining that project; this synthetic model makes no compliance claim.
The useful deliverable here is a smaller, answerable question: with these arrivals and this handling time, how many requests can the booking desk finish? Solve that before attributing the whole patient journey to a conversion rate.
Found a mistake or tried a different approach?
Send Alex a note ↗