Step 3: Model the medication plan
What we are building
A small synthetic plan expressed as FHIR R4 MedicationRequest resources, ready for the patient app to read.
What you should already have
- The sandbox project and patient access policy from step 2.
- A synthetic Patient resource created through an authorized sandbox or clinical setup flow.
- Permission to create the sample plan as a project administrator or another authorized test operator.
The implementation
Choose the FHIR resources
Ovok does not expose a separate medication-plan API. Use the FHIR resource that matches each fact:
| Need | Resource | Use in this tutorial |
|---|---|---|
| A prescribed or intended plan with dose instructions | MedicationRequest | The app reads active requests for the signed-in Patient. |
| A reusable coded or detailed medication definition | Medication | Optional. A request may instead use medicationCodeableConcept. |
| A statement that a patient is or was taking medication over a period | MedicationStatement | Not used for each individual reminder occurrence. |
| One occurrence reported as completed or not done | MedicationAdministration | Used in step 8 for patient-reported taken/skipped events. |
The CapabilityStatement at GET /fhir/R4/metadata advertises create, read, update, delete, history, and search interactions for these medication resources in the current sandbox. Always check the target environment before deploying.
Create synthetic requests outside the patient app
Create the following requests in the sandbox using an authorized setup tool. Do not put an administrator token in the mobile app. This is non-clinical sample data; replace nothing here with a real medication or prescribing instruction.
{
"resourceType": "MedicationRequest",
"status": "active",
"intent": "order",
"medicationCodeableConcept": { "text": "Demo medicine A" },
"subject": { "reference": "Patient/REPLACE_WITH_SYNTHETIC_PATIENT_ID" },
"authoredOn": "2026-10-07T09:00:00Z",
"dosageInstruction": [
{
"text": "Demo instructions only.",
"timing": {
"repeat": {
"frequency": 1,
"period": 1,
"periodUnit": "d",
"timeOfDay": ["08:00:00"],
"boundsPeriod": { "start": "2026-10-07", "end": "2026-11-06" }
}
}
}
]
}
Create two more active MedicationRequest resources for the same synthetic Patient: one with frequency: 2 and timeOfDay: ["08:00:00", "20:00:00"], and one with frequency: 1, timeOfDay: ["18:30:00"], and its own neutral dosageInstruction.text. Give each resource a different medicationCodeableConcept.text and a different server-assigned resource id. These examples demonstrate one daily dose, two daily doses, and an instruction such as “Demo instructions only; meal context supplied by the test plan.” They are not recommendations.
The sample authoredOn, boundsPeriod.start, and boundsPeriod.end values are illustrative. Before creating the sample requests, update the start/end dates to a current test window so they remain active during your walkthrough.
MedicationRequest.medication[x] and subject are required. The dosageInstruction carries the display text and timing. timeOfDay is a FHIR time, so it has no UTC offset. The tutorial interprets this simple example as the patient's local wall-clock time; step 4 defines that product decision and its limitations.
Read the plan with the FHIR client
Once step 5 has created the client and authenticated patient profile, the app reads only active requests belonging to that Patient:
const plan = await loadMedicationPlan(client, patient.id);
loadMedicationPlan is implemented in step 5 using the client's paginated search method. Keep the subject filter in the request and let the AccessPolicy enforce the same boundary server-side.
Important Ovok decisions
MedicationRequest.statusdescribes the request's lifecycle. Onlyactiverequests are included in this simple Today view; cancelled, stopped, completed, or entered-in-error requests must not be shown as current.- A
Medicationresource is optional when the request has a usablemedicationCodeableConcept. Do not create duplicate Medication resources simply to display a text label. - A
MedicationStatementis not a dose-by-dose adherence log. The tutorial uses oneMedicationAdministrationper reported occurrence and makes clear it is patient-reported. Dosage.timingcan express more than this tutorial's dailytimeOfDaysubset. The app must not guess a reminder time fromwhen,frequency, or free text. Unsupported timing needs an explicit product/clinical interpretation before scheduling.
Expected result
The sandbox contains three synthetic MedicationRequest resources linked to one test Patient, and the authenticated patient search returns only that patient's active requests.
Common errors and troubleshooting
- No requests are returned: verify the
subjectreference is exactlyPatient/{id}, the request isactive, and the user is signed in as that patient. - The plan displays a misleading schedule: inspect the source
Dosage.timing. The tutorial supports explicit localtimeOfDayentries; it does not parse free-text instructions into times. - A write returns 403: create the sample plan through an authorized setup identity. The patient app should not receive a privilege that lets it prescribe or edit the plan.
Previous / Next
Previous: step 2: create and configure the Ovok project · Continue to step 4: set up localisation and time handling.