Patient questionnaires
Schedule, Track, and Score Patient Questionnaires on FHIR
Run recurring patient questionnaire programs (PHQ-9, KCCQ-12, post-surgical milestones, study protocols) as standard FHIR workflows. Scheduling, timezone handling, reminder delivery, completion tracking, and threshold alerts come from the server, with the data already in a format your EHR or registry can consume. The patient app stays a thin frontend; the backend, audit trail, and access control are shared with the rest of your FHIR product.
What you can build
- PHQ-9, KCCQ-12, or custom protocols at scale
Define the schedule once as a reusable PlanDefinition. Apply it to every enrolled patient via `$apply`. Change the cadence without a code deployment.
- Standard instruments without re-typing them
Import PHQ-9 (LOINC 44249-1) and other standard instruments directly into FHIR Questionnaires through the Web UI. Codes and answer options are preserved, not lost in translation.
- Tasks that materialize on the right day
Fire Arrow creates Task resources up to a configurable horizon, transitions them to `ready` at the patient's local time (with DST handling), and exposes them through standard FHIR search.
- Reminders that fit your delivery channel
Subscribe to ready Tasks over a REST hook, email, WebSocket, or Azure Storage Queue. Push notifications, SMS, in-app messages, call-center systems all consume the same event stream.
- Trend dashboards on the clinical timeline
QuestionnaireResponses appear next to the rest of the patient's clinical data. Care teams see longitudinal change in one place instead of a separate reporting tool.
- Threshold-based alerts in your workflow
A subscription on `QuestionnaireResponse` runs your scoring and escalation logic. PHQ-9 score above 20 triggers a critical-band alert; above 15 a moderate-severe alert. Where the response goes next is your call.
- Data ready for the next system
Everything (the instrument, the protocol, the schedule, the responses) is standard FHIR R4 from the start. Hand it to an EHR, a registry, an analytics platform, or a study database without an export converter.
How it works
- 1 Define the questionnaire
Start from a published instrument like PHQ-9 or KCCQ-12 by importing it from LOINC, or build a custom one. Either way the result is a standard FHIR Questionnaire with the original codes intact, referenced everywhere downstream by a single stable identifier.
- 2 Publish it so other resources can lock onto it
Mark the questionnaire as active and assign it a stable identifier. The protocol in the next step points at this exact published version, so a later edit to a draft cannot quietly change what patients are answering.
- 3 Describe the protocol once, reuse it for every patient
Define the recurring schedule as a FHIR PlanDefinition: which questionnaire, how often, and for how long (for example, PHQ-9 every two weeks for six months). The cadence lives in data, so moving from biweekly to weekly is a configuration change, not a code release.
- 4 Enroll a patient on the protocol
Apply the protocol to a specific patient with their start date and timezone. The server generates that patient's own CarePlan and anchors every future occurrence to their local time, so a 9 AM reminder still fires at 9 AM after a daylight-saving transition.
- 5 Turn on scheduling and due-event notifications
A single call activates the schedule and registers your patient app to be notified whenever an occurrence becomes due. From here the server handles timezone, daylight-saving, and recurrence on its own. No cron jobs, no background workers on your side.
- 6 Patient app renders the questionnaire when it is due
When the next occurrence is due, the server pushes a notification. Your patient app fetches the linked questionnaire, presents it to the patient, and posts the answers back. The to-do is closed and the clinical record is updated in the same step. Care teams see the response on the patient's timeline immediately.
- 7 Score and alert in a separate flow
A second subscription delivers every submitted response to your scoring logic. Compute the score (PHQ-9 critical band, KCCQ-12 trend, or anything custom), compare it to your thresholds, and route the result through whatever escalation path you already have: clinician task, in-app message, dashboard flag. Reminders and clinical alerts stay independent, so a change to one does not disturb the other.
What you get out of the box
The protocol is data, not code
In a conventional implementation, monitoring logic lives in application code: a cron schedule, a configuration file, hardcoded intervals in a backend service. A protocol change (different cadence, different instrument, different duration) requires a developer, a code change, and a redeployment.
With `PlanDefinition`, the protocol is a versioned FHIR resource. A clinician or administrator can update the cadence, switch the instrument, or change the duration without touching code. The same protocol can be applied to a population through programmatic `$apply` calls.
Every occurrence is its own searchable record
Each scheduled occurrence is captured as a small FHIR Task that links back to the patient and to the protocol it came from, and carries the state of the work: due, in progress, completed, or missed. Because Tasks are standard FHIR data, a dashboard like "patients with an overdue PHQ-9 in the last seven days" is one FHIR query, not a custom reporting pipeline.
Tasks live under the same authorisation rules as the rest of the FHIR data, so nothing has to be reimplemented per app. A patient signed in to the patient app sees only their own Tasks. A clinician sees only the Tasks belonging to their organisation. A service account that sends reminders gets only the operations it needs.
Reminders and clinical alerts stay independent
"Patient has a questionnaire due" and "patient just submitted a concerning score" are different events with different audiences. Treating them as two notification streams keeps the reminder pipeline and the clinical-alert pipeline from being tangled into one piece of code.
One stream tells the patient app when an occurrence is due, which drives reminders. A second stream tells your scoring service when a response comes back, which drives alerts. Swapping the push-notification provider, adding a new scoring rule, or changing the escalation path can each happen on its own without disturbing the other side.
Operational controls, with sensible defaults
Two settings tune the scheduling engine: how far ahead occurrences are prepared (thirty days by default) and how often the engine checks. A cap on the number of open occurrences per activity keeps a multi-year programme from accumulating thousands of pending records at once.
Notifications cannot pile up unnoticed. Every subscription has a required end date and a configurable maximum lifetime, so a forgotten one expires on its own. Delivery failures are tracked, and the server can auto-disable a subscription after too many consecutive failures or after a configurable stretch with no successful delivery. Renewing or tearing one down is a single call.
Timezone is the one setting worth getting right at enrollment. With it, recurring reminders fire at the patient's local wall-clock time and survive daylight-saving transitions. Without it, the server still creates the work, but it cannot put it in front of the patient at the right hour.
Example deployments
- Depression monitoring with PHQ-9
PHQ-9 every two weeks for six months. The instrument is imported once from LOINC, the protocol defines the cadence, and a scoring rule routes critical-band scores (≥20) and moderate-severe scores (≥15) into the care team's existing escalation workflow.
- Cardiology symptom surveillance
KCCQ-12 monthly during heart-failure follow-up. Same shape as the PHQ-9 case with a different instrument and a different cadence; worsening scores can trigger an appointment, a clinician review, or an in-app message, whichever the team already uses.
- Post-surgical recovery tracking
Check-ins at the two-week, six-week, and twelve-week milestones after a procedure. Each occurrence is anchored to the patient's procedure date rather than to a fixed recurring interval, so the schedule still works when a procedure slips by a few days.
- Clinical study symptom surveys
Recurring questionnaires across a study population on a single protocol. The same data can be sliced into different views (operational staff, investigators, sponsors) by configuring access rules, with no duplicate pipeline per audience.
FAQ
Can patients answer Questionnaires from a mobile app?
Yes. The app is a normal FHIR client with the patient's bearer token. It reads ready Tasks for the patient, renders the linked Questionnaire, and POSTs a QuestionnaireResponse on submission. PatientCompartment ensures it only ever sees the patient's own Tasks.
How are scoring rules expressed?
FHIR supports standard scoring extensions on Questionnaire items (such as `ordinalValue`). Scores can be calculated client-side, server-side via a custom operation, or in a downstream consumer of a QuestionnaireResponse subscription. For standardized scoring, the FHIR Clinical Quality Language (CQL) infrastructure is available.
Can I change the schedule for an enrolled patient?
Yes. Update the CarePlan and future Tasks within the scheduling horizon are revised. Already-completed Tasks stay as they are. Updates to the underlying PlanDefinition can be re-applied programmatically across the enrolled population.
How does Fire Arrow handle timezones and DST?
Timezone is resolved from the CarePlan extension (set during `$apply`) or from the Patient resource. DST transitions are handled centrally so that recurring patient communication fires at the correct local wall-clock time without per-application bug fixes.
Does it work for non-clinical workflows like study questionnaires?
Yes. The mechanics are the same: Questionnaire defines the form, PlanDefinition defines the schedule, CarePlan applies it to a participant, Tasks materialize on the timeline, and QuestionnaireResponses capture the answers. Identity filters and property filters can serve different views to operational staff, investigators, and sponsors.
What is the operational footprint?
Fire Arrow Server runs as a Docker container with PostgreSQL alongside. The materialization job, subscription delivery, and the rest of the workflow run inside the same backend. Multi-node deployments use distributed JDBC-based locking so concurrent materialization stays consistent.