Back to BlogIndustry Guides

A Prescription Refill Request Workflow (Web/PWA)

Rupak Amin

Founder & Lead Engineer, RAITHub

9 min read

RAITHub ships and tests production software. See QA as a Service or talk to us.

A prescription refill workflow routes a patient's request to a clinician, who decides; the software never prescribes. Build it as a state machine (requested, in review, approved, declined, dispensed), put requests in a clinician review queue, audit every step, and test that no request is auto-approved and none goes quiet. This is engineering guidance only: the prescribing decision is clinical, and your compliance partner sets the data rules.

This post is about building the request-and-review software, not clinical or prescribing advice. Whether a refill is appropriate is a clinical decision made by a qualified clinician. What patient and medication data you may store, and how it must be protected, are governed by health-data and pharmacy law that differs by jurisdiction; this is general information, so confirm the rules with your compliance partner and adviser. If you would rather have it built for you, see how RAITHub would build this below.

What does the software actually do, and not do?

It captures a request, routes it to a clinician, records the clinician's decision, and tracks the outcome. It does not decide whether the refill is safe or appropriate: that is prescribing, a clinical act. Building this line in from the start keeps the system honest and safe: every approval has a clinician behind it, and the software's job is to make that review fast, complete and auditable, never to shortcut it.

The software doesThe software does not
Capture the patient's refill request with the details a clinician needsDecide whether the refill is clinically appropriate
Route it to the right clinician's review queueAuto-approve based on rules alone
Record the decision, reason, clinician and timeMake or alter a prescription
Track and surface requests that are waiting too longReplace clinical judgement or pharmacy checks

What does the data model look like?

A refill request has a current state, a link to the patient and the medication reference, and a history. The decision is recorded against a clinician, never inferred.

CREATE TABLE refill_requests (
  id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  patient_id    bigint NOT NULL REFERENCES patients(id),
  medication_ref text NOT NULL,                 -- a reference to the record, access controlled
  state         text NOT NULL DEFAULT 'requested',
                -- requested | in_review | approved | declined | dispensed | cancelled
  state_since   timestamptz NOT NULL DEFAULT now(),  -- for the overdue check
  requested_at  timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE refill_decisions (
  id          bigserial PRIMARY KEY,
  request_id  uuid NOT NULL REFERENCES refill_requests(id),
  outcome     text NOT NULL CHECK (outcome IN ('approved','declined','needs_appointment')),
  reason      text,
  by_clinician bigint NOT NULL REFERENCES clinicians(id),  -- a human, always
  created_at  timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX refill_requests_by_state ON refill_requests (state, state_since);

Store a reference to the medication record and control access to the detail separately, keeping stored patient data to what the review needs. What you may hold is for your compliance partner to define.

How does the clinician review queue work?

A request enters in_review and appears in a queue of the right clinician or team. The queue shows what the clinician needs to decide, and the decision is an explicit action that writes a refill_decisions row and moves the request to approved, declined or needs_appointment. The crucial rule: the system never writes an approval without a clinician action behind it. If you later add rules (for example, flagging requests that are clearly within an existing valid prescription), they triage the queue, they do not decide, a clinician still confirms. That boundary is what keeps the software on the right side of "not prescribing."

How do you make sure no request is forgotten?

With the same overdue check used for referrals and SLAs: each state has a target time, and a scheduled, idempotent job surfaces requests that have waited too long. A patient waiting on a refill is time-sensitive, so an overdue request should be escalated, not left in a queue. The pattern is identical to the maintenance SLA engine in a maintenance ticketing SaaS with SLA escalation, applied to clinical turnaround times your clinicians set.

-- Requests waiting too long for review (the overdue worklist).
SELECT id, medication_ref, state, state_since
FROM refill_requests
WHERE state IN ('requested', 'in_review')
  AND state_since < now() - make_interval(hours => $1);  -- target review time

Why audit every step, and how?

Because a medication decision must be traceable to a clinician, and patient data access is reviewable. Record every state change and decision as an append-only event: the from-state, the to-state, the clinician or patient who acted, the reason and the time. Never overwrite. This gives a defensible record of who approved or declined a refill and when, which is essential in clinical software. The audit pattern is in designing an append-only audit log, and patient-level access is controlled with the roles in designing SaaS authorization.

How do you test a refill workflow?

The two tests that matter most: nothing is approved without a clinician, and nothing is forgotten.

import { describe, it, expect } from 'vitest'
import { decide, overdue, stateOf } from './refills'

describe('refill workflow', () => {
  it('cannot be approved without a clinician decision', async () => {
    const req = await seedRequest({ state: 'in_review' })
    // No automatic path to 'approved' exists; only decide() with a clinician can.
    await decide(req.id, { outcome: 'approved', byClinician: 12, reason: 'within valid script' })
    const d = await lastDecision(req.id)
    expect(d.byClinician).toBe(12)
    expect(await stateOf(req.id)).toBe('approved')
  })

  it('surfaces a request waiting too long for review', async () => {
    const req = await seedRequest({ state: 'requested' })
    await ageStateSince(req.id, { hours: 48 })
    const list = await overdue({ targetHours: 24 })
    expect(list.map((r) => r.id)).toContain(req.id)
  })
})

Add a test that each decision writes exactly one audit event, and one that a patient cannot read another patient's requests.

Buy, build or hire?

OptionChoose this whenThe catch
A patient-portal or EHR moduleYour practice uses one platform that already includes refill requestsYou fit its workflow and data model; your own review queue and channels may not map
Phone and a paper listVery low volumeNo audit trail, no overdue tracking, no patient self-service
Custom web app or PWAYou want patient self-service, your own review workflow, audit and reach on low-end phonesYou own the workflow and the record; prescribing stays clinical and data rules come from your compliance partner

How long does it take to build yourself, and what is the risk?

For an experienced developer building the request workflow, review queue, overdue check and audit trail as a web app or PWA, our estimate is 4 to 6 weeks at a fixed scope. The main risks are two. First, the safety boundary: the software must never approve a refill without a clinician, so review that path hard. Second, the data layer: health and medication data carry strict obligations that differ by jurisdiction, and no app is compliant on its own. This is general information, so build it with your compliance partner setting the rules and clinicians owning every decision.

Why RAITHub for this

  • Workflows, roles and reach. RAITHub built PadhAI with channel parity across a PWA, WhatsApp and Telegram, and enforces per-tenant isolation and roles on Sundor Skin (530+ tests) and PropDesk (1,024 tests).
  • Healthcare adjacency, stated honestly. RAITHub has built a healthcare scheduling app for a client; it has not shipped a regulated health product, and there is no published health case study, so weigh the evidence accordingly.
  • Safe by design. The approval path is built so only a clinician can approve, and every step is audit-logged.

When you don't need us

  • Your portal's refill module fits. If its workflow and data model match your practice, use it.
  • Volume is tiny. A documented manual process may be enough.
  • You need a regulated, validated system. RAITHub builds no GxP-validated systems; use a vendor cleared for that.

How RAITHub would build this

  • Request workflow: a state machine where only a clinician decision can approve, every transition audit-logged.
  • Review queue: requests routed to the right clinician, with optional rule-based triage that flags but never decides.
  • Overdue check: an idempotent scheduled job that escalates requests waiting too long.
  • Reach and data: a PWA that works on low-end phones, with patient data minimised and protected to your partner's rules.

Timeline: a first version fits the 4 to 6 week fixed-scope range; EHR or pharmacy integration pushes it toward the 6 to 12 week backend range. See SaaS development and the HealthTech industry page. A related build is a referral management system for clinics.

You receive: the workflow, queue and audit code, tests gated in CI, handover docs, and full IP in your name under NDA. Clinical decisions stay with clinicians; data rules come from your compliance partner.

Frequently asked questions

Does a prescription refill app decide whether to refill?

No. The software captures the request, routes it to a clinician and records their decision. Deciding whether a refill is appropriate is prescribing, a clinical act, so every approval has a clinician behind it. The software never auto-approves on rules alone.

How is the refill request modelled?

As a state machine: requested, in review, approved, declined, needs appointment, dispensed, cancelled. Each request links to the patient and a medication reference, and the decision is recorded against a clinician in its own row, never inferred from the state.

How do I make sure a refill request is not forgotten?

With a scheduled, idempotent job that surfaces requests waiting longer than the target review time clinicians set. A patient waiting on a refill is time-sensitive, so an overdue request is escalated rather than left sitting in a queue.

Why does a refill workflow need an audit trail?

Because a medication decision must be traceable to a clinician, and patient data access is reviewable. Record every state change and decision as an append-only event with the from-state, to-state, the person who acted, the reason and the time, and never overwrite.

Can patients request refills from a basic phone?

Yes, if you build it as a PWA that works on low-end devices and poor connections. RAITHub built PadhAI for exactly that kind of reach, with the same experience across a web app and messaging channels, which the same approach can bring to a refill request flow.

Does RAITHub handle the compliance side?

RAITHub builds the workflow, access control and audit trail. What patient and medication data you may store, and how it must be protected, are governed by health-data and pharmacy law that differs by jurisdiction, and no app is compliant on its own. This is general information; confirm the rules with your compliance partner and adviser.

prescription refillhealthtechworkflowreview queueaudit trailpwa

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.