1. Enrollment, Consent Gate and Start Confirmation
Trigger: Adherence Program Enrollment submitted.
Actions in order:
- Create the card in Adherence Program at Enrolled. Write program_code, program_type_code, course_start_date, course_length_days, refill_due_date, check_in_cadence, prescribing_provider, pharmacy_on_file. Apply adherence-active and the matching cadence tag.
- Compute program_end_date from start plus course_length_days.
- Consent gate. If no channel has consent recorded as yes, call workflow 8, set preferred_channel to phone, create a coordinator task to arrange the voice fallback, and stop here. No messages are sent on an unconsented channel, ever.
- Wait until course_start_date, then send the start prompt on preferred_channel. SMS: “Hi {{contact.first_name}}, it's {{practice_name}}. There's a quick one-minute check waiting in your secure portal: {{portal_link}}. For anything urgent call {{nurse_line}} or your local emergency number. Reply STOP to stop texts.”
- Wait 24 hours. If not confirmed, resend once on the same channel with a shorter nudge.
- Wait 48 hours. If still not confirmed, set adherence_self_report to not started, create a task to verify with pharmacy_on_file whether it was ever collected, and open an L1 escalation.
- Wait 48 hours. If still nothing, notify prescribing_provider: a prescribed course has no evidence of ever having been started. This is the single highest-yield alert in the build.
- On confirmation: set the real start date if the patient reports a different one, move to Course Started then Early Course, call workflow 2.
Exit: course confirmed started, or card moved to Discontinued with reason unreachable or patient choice.
2. Early Course Check-In Cadence
Trigger: stage becomes Early Course.
Conditions: consented channel only; held to quiet hours in timezone; suppressed entirely while escalation-open is present.
Actions in order:
- Wait 3 days from course_start_date. Send check-in one. SMS: “Hi {{contact.first_name}}, your day-3 check-in from {{practice_name}} is ready. Three quick questions in your secure portal: {{portal_link}}. Urgent? Call {{nurse_line}}.” Email subject line: “Your check-in from {{practice_name}} is ready”. Set last_check_in_sent.
- On response, write adherence_self_report, tolerability_flag and the call-request flag, set last_check_in_response_date, reset missed_check_in_count to zero, and branch: taking as directed and nothing reported and no call requested, continue; anything else, call workflow 5.
- Wait to day 7. Send check-in two, same structure, copy varied so it does not read as a loop: “Hi {{contact.first_name}}, week one check-in. Same three questions, one minute: {{portal_link}}.”
- Wait to day 14. Send check-in three. Add one extra portal question on whether the patient expects any barrier to continuing: asked in the portal, never in the message.
- After day 14 with no open escalation, move the card to Steady Course and call workflow 3.
- Any check-in sent and not answered within 48 hours calls workflow 4.
Exit: day 15 reached, escalation opened, or terminal stage set.
3. Steady Course Cadence and Completion
Trigger: stage becomes Steady Course.
Actions in order:
- Schedule recurring check-ins at the interval set by check_in_cadence: Dense every 14 days, Standard every 30 days, Light every 60 days. Each one is the same three-question form behind a portal link.
- Before each send, check for escalation-open, an answered check-in in the last seven days, or a booked appointment in the next three days. Any of those suppresses the send. A patient who is already talking to a clinician should not also be getting a robot.
- Rotate three copy variants so the cadence does not become wallpaper. Variant two: “Hi {{contact.first_name}}, time for your regular check-in with {{practice_name}}: {{portal_link}}. Takes a minute.”
- Wait until refill_due_date minus 7 days, then move to Refill Window and call workflow 6.
- Wait until program_end_date. Send the completion check-in: “Hi {{contact.first_name}}, you're at the end of your course. One last short check-in from {{practice_name}}: {{portal_link}}.”
- On completion response, capture whether the course was finished as prescribed, move to Course Complete, swap adherence-active for adherence-complete, and create the clinically specified next step. A follow-up booking, a repeat enrollment, or nothing. Notify prescribing_provider with the course summary.
- If the completion check-in goes unanswered through the workflow 4 ladder, move to Course Complete with the outcome recorded as unknown. An unknown outcome is a real finding and belongs in the numbers, not hidden as a success.
Exit: Course Complete, Discontinued, or Lost to Follow-Up.
4. Missed Check-In Ladder
Trigger: a check-in has been sent and no response recorded after 48 hours.
Actions in order:
- Increment missed_check_in_count, apply checkin-missed-1. Resend on the same channel: “Hi {{contact.first_name}}, we haven't heard back. Your check-in is still open: {{portal_link}}.”
- Wait 48 hours. If still no response, apply checkin-missed-2 and switch channel: if the first attempt was SMS, send email with subject “Your {{practice_name}} check-in is still waiting”, and the reverse. Only channels with recorded consent are used.
- Wait 72 hours. If still no response, apply checkin-missed-3, open an L1 escalation and create a coordinator call task due the same business day. The script tells the caller to confirm identity before saying anything beyond the practice name.
- Wait 3 business days. If the call task is unsuccessful across two attempts at different times of day, apply patient-unreachable, stop all automated messaging on the card, notify prescribing_provider, and move to Lost to Follow-Up.
- Any response at any rung clears the missed tags, resets missed_check_in_count, and returns the card to its cadence.
- Cross-check: before declaring unreachable, the workflow checks refill_confirmed_date. A patient who is refilling on time but not answering check-ins is a different problem from a patient who has disappeared, and gets a different note to the prescriber.
Exit: response received, or Lost to Follow-Up.
5. Clinical Escalation Ladder
Trigger: a check-in answer of missed some doses, stopped taking, something is bothering me, or please call me; or a Tolerability Report submission; or BO detecting urgent language in any inbound message.
Conditions: the practice maps each answer to a level at configuration time. The mapping is a clinical decision, not a default, and it is reviewed with the prescribers.
Actions in order:
- Apply escalation-open and the level tag. Write escalation_level and escalation_opened_at. Suspend every automated message on this card immediately.
- L1: coordinator. Typical mapping: missed some doses, no tolerability concern. Create a coordinator task due within 4 business hours. The coordinator calls, records what they learn, and either closes the escalation or raises it. Patient-facing acknowledgement: “Thanks {{contact.first_name}}, someone from {{practice_name}} will call you today. If anything feels urgent in the meantime, call {{nurse_line}} or your local emergency number.”
- L2: nurse or pharmacist. Typical mapping: stopped taking, tolerability flagged as troubling, or an explicit call request. Notify the nurse queue now, offer a same-day Nurse Check-In Call or Pharmacist Consult booking link, and create a task due within 2 business hours. If no slot exists today, the task escalates to the clinical lead for manual placement rather than offering tomorrow.
- L3: prescriber. Typical mapping: urgent language detected, a safety-relevant report, or an L2 unresolved within its window. Notify prescribing_provider directly and immediately on their configured alert path, move the card to Escalated: Clinical, and create a same-day task with no round-robin: L3 always lands on a named person.
- Urgent-language path. If the trigger was urgent language, the patient receives one message and only one: “{{contact.first_name}}, if you are having a medical emergency call your local emergency number now. Otherwise call {{nurse_line}} straight away. We have alerted the team.” BO stops. No automation attempts triage, reassurance, or advice.
- Escalation timeout. Any open escalation past its due window re-notifies the owner, then the clinical lead, then appears on the daily digest. Escalations cannot silently age.
- Closure. A clinician records the outcome: continue unchanged, continue with a change, pause, or stop. Write escalation_closed_at, clear escalation-open, and either resume the cadence (adjusting check_in_cadence to Dense for 14 days after any L2 or L3) or move the card to Discontinued with discontinuation_reason.
Exit: clinician-recorded closure. Only a person closes an escalation.
6. Refill Window and Gap Watch
Trigger: refill_due_date minus 7 days, or stage becomes Refill Window.
Actions in order:
- Apply refill-window-open. Send the prompt: “Hi {{contact.first_name}}, it's about time for your next supply. Confirm or get help in your secure portal: {{portal_link}}.” The message never names what the supply is.
- Wait 4 days. If refill_confirmed_date is empty, send a second prompt that offers help rather than repeating the ask: “Hi {{contact.first_name}}, need a hand sorting your next supply? Reply HELP and we'll call you.”
- On refill_due_date. If still unconfirmed, start counting refill_gap_days and create a pharmacy verification task: staff check with pharmacy_on_file whether it was dispensed, because a patient who collected but never confirmed is not a gap.
- Gap day 7. Apply refill-overdue-7, open an L1 escalation, and set adherence_self_report to a provisional missed-doses state pending the call.
- Gap day 14. Apply refill-overdue-14, raise to L2, and notify prescribing_provider with the gap length. A two weeks-long gap changes the clinical picture and the prescriber should be the one deciding what it means.
- Access branch. If the patient reports a cost or access barrier on the portal form, route to the coordinator with the practice's own access-support process. The automation never discusses price, coverage or alternatives. It routes to a human who can.
- On confirmation at any point: set refill_confirmed_date, clear the overdue tags, roll refill_due_date forward, return the card to Steady Course.
Exit: refill confirmed, dispensing verified with the pharmacy, or card moved to Discontinued.
7. Daily Adherence Digest and Queue Health
Trigger: scheduled, each weekday before the first clinic block.
Actions in order:
- Recalculate refill_gap_days and course day-counts on every active card.
- Build the internal digest: all open escalations by level with age and owner, every escalation past its due window, all checkin-missed-3 cards, all refill-overdue-7 and refill-overdue-14, all cards with no start confirmation past 72 hours, all patient-unreachable from the last seven days, and all cards reaching program_end_date this week.
- Send it to the clinical lead and the coordinator queue. The digest contains patient identifiers and belongs on an internal channel with access controls: never a personal inbox, never a group chat.
- Create one task per unowned item. Reassign anything owned by someone out of office.
- Weekly, add the roll-up: completion rate, start-confirmation rate, check-in response rate by channel, escalation volume by level and by trigger answer, and median time from escalation open to clinician contact.
Exit: never; this is what keeps the ladder honest.
8. Consent, Quiet Hours and Content Guardrail
Trigger: enrollment with no consent on file, Communication Consent submission, or any inbound opt-out keyword.
Actions in order:
- Capture. Record patient_consent_sms and patient_consent_email as separate values, plus consent_captured_at, consent_language_version and consent_source. Store the exact wording the patient saw, versioned, not a paraphrase of it.
- Separation. Consent for adherence check-ins is recorded separately from marketing consent. A patient can be opted out of newsletters and still receive their check-ins, and opting out of check-ins never silently removes them from the program. It switches them to the voice fallback and tells the prescriber.
- On opt-out. Set that channel's flag to no, apply the revoked tag, remove the card from every active ladder on that channel, create a coordinator task to continue by phone, and send exactly one confirmation: “You're unsubscribed from texts from {{practice_name}}. We'll call you instead about anything that needs your attention.” Then nothing further on that channel.
- Quiet hours and frequency. Hold every automated send to the practice's configured window in the patient's timezone. Cap sends per patient per rolling 24 hours across all adherence workflows, and across other programs the patient is enrolled in, so a patient on two courses with an open refill window does not get four messages in an afternoon. Over-cap sends queue to the next window.
- Content guardrail. Before any send, verify the body and the subject line contain no merge field sourced from a clinical table: no medication name, no dose, no schedule, no indication, no symptom, no program_type_code, no readable diagnosis. Permitted merge fields are an explicit allow-list. First name, practice name, portal link, nurse line, booking link, generic date. A send that fails is blocked and raised as an internal alert, never silently rewritten.
- Escalation suppression. While escalation-open is present, all automated sends on the card are blocked regardless of schedule. Clinical conversations are not interrupted by cadence.
Exit: runs permanently as the guard layer under every other workflow.