Workflow 1: Enrollment & Cadence Initialization
Trigger: Program Enrollment form submitted.
- Create the Recall & Series Adherence card in Enrolled: Cadence Set. Apply program-active.
- Write program_code, program_enrollment_date, cadence_interval_days, cadence_tolerance_early_days, cadence_tolerance_late_days, visits_in_series_total, visit_sequence_number = 1, program_owner_clinician, authorization_required.
- Write the contact preferences: preferred_recall_channel, best_time_to_reach, ok_to_leave_voicemail, ok_to_contact_alternate, alternate_contact_name, plus sms_consent, email_consent, mail_consent and their timestamps.
- Compute the clock: next_due_date = last_completed_visit_date + cadence_interval_days; due_window_open_date = next_due_date − cadence_tolerance_early_days; due_window_close_date = next_due_date + cadence_tolerance_late_days.
- Assign assigned_recall_owner and readiness_owner by program, not by round robin: cadence programs reward a single familiar voice.
- Branch on authorization_required. If true, create a Visit Readiness card immediately in Readiness Due so the first authorization is confirmed before the first booked visit rather than after.
- Send the enrollment confirmation. SMS: "You're all set, {{first_name}}. Your visits with {{clinician}} are every {{interval_weeks}} weeks and we'll always book the next one before you leave. Reply STOP to opt out." Email carries the same plus the practice's number and the preference-update link.
- Hand immediately to Workflow 2 so the first next-visit booking happens at this visit, not later.
Exit conditions: card created and clock populated. This workflow runs once per enrollment.
Workflow 2: Book the Next Visit at Checkout
Trigger: a program visit is marked complete, or an enrollment is created.
This is the most important workflow in the build. Every other workflow below is remediation for this one failing.
- Update the clock: write last_completed_visit_date = today, increment visit_sequence_number, recompute next_due_date, due_window_open_date, due_window_close_date.
- Branch on series position. If visit_sequence_number > visits_in_series_total, move to Program Complete, apply series-complete, notify program_owner_clinician, and exit: do not generate another recall.
- Create an immediate, blocking task on the checkout screen: "Book next {{program_code}} visit: window {{due_window_open_date}} to {{due_window_close_date}}". The task cannot be cleared without one of two outcomes: an appointment, or a recorded reason.
- Wait 15 minutes. Branch on whether an appointment now exists in the window.
- Booked: write next_visit_booked_datetime, set booked_at_prior_visit = true, move card to Next Visit Booked, send confirmation SMS: "Booked, {{first_name}}, {{appointment_day}} at {{appointment_time}} with {{clinician}}, {{location}}. Reply R to change it.": and exit.
- Not booked: set booked_at_prior_visit = false, apply no-book-at-checkout, and send a same-day self-booking link within two hours of the visit, while the appointment is still fresh: "{{first_name}}, we didn't get your next visit on the calendar today. Here are open times in your window: {{booking_link}}"
- Wait 48 hours. If still unbooked, create a call task for assigned_recall_owner: not a text. A patient who left without booking has already demonstrated that the text path is not working for them.
- Wait 5 days. If still unbooked, hand the card to Workflow 3 at its first step regardless of how far away the due date is.
Exit conditions: appointment exists in the window, program complete, or card withdrawn. Stop condition worth naming: if the clinician has flagged that the next interval needs to be re-set at a review visit, this workflow books the review visit instead and does not compute a cadence it was not given.
Workflow 3: Pre-Due Warm-Up (unbooked cards only)
Trigger: nightly engine finds a card where days_until_due = 21 and no next_visit_booked_datetime.
Global conditions: exits instantly on any booking. Quiet hours enforced, 8:00am–8:00pm local. Channel selection follows preferred_recall_channel; if that is "call," every SMS step below becomes a call task instead.
- Day −21, 10:00am. SMS: "Hi {{first_name}}, it's about time for your next visit with {{clinician}}. Pick a time that works: {{booking_link}}"
- Day −14, 9:30am. Email: subject "Time to schedule your next visit". Body names the clinician, the location, two concrete open slots inside the window, and the self-booking link. No program, medication or condition named anywhere in the subject line or the body. An email subject line is visible on a lock screen.
- Day −7, 2:00pm. SMS: "{{first_name}}, {{clinician}} has {{slot_1}} and {{slot_2}} open. Reply 1 or 2 and I'll hold it for you." A numeric reply books directly and exits.
- Day −3, 11:00am. Call task for assigned_recall_owner, 15-minute slot on the Recall Call Block.
- Day 0 (due_window_open_date). Hand to Workflow 4.
Exit conditions: booking created, sms-stop, card withdrawn, or clinician pauses the program.
Workflow 4: Due-Window Recall
Trigger: nightly engine moves a card into Due Window Open: Unbooked.
This is the workflow with the highest clinical value, so it is the one that escalates to humans fastest.
- Set adherence_status = due, apply due-now, move card to Recall Outreach Active, add the card to today's published worklist, notify assigned_recall_owner.
- Day 1, 9:00am. SMS: "{{first_name}}, you're due for your visit with {{clinician}} this week. {{booking_link}}, or reply and I'll find you a time."
- Day 1, afternoon. Call attempt 1 from the Recall Call Block. Branch on ok_to_leave_voicemail: if true, leave the approved script: "This is {{staff_name}} from {{practice_name}} calling for {{first_name}}. Please give us a call back at {{number}} to get your next visit scheduled. Thank you." Nothing more; the voicemail names no program and no reason. If false, no message is left and only the missed-call record goes on the card.
- Increment recall_attempt_count, write recall_last_attempt_date and recall_last_channel on every attempt, every time. Attempt history is what makes the next attempt smarter and the reporting honest.
- Day 3. Email with two slots. Day 4. Call attempt 2, deliberately at a different time of day than attempt 1, guided by best_time_to_reach.
- Day 6. SMS: "{{first_name}}, I don't want you to fall behind on your visits. Is there a day or time that would actually work for you? Just reply and tell me." An open-ended ask here outperforms another link, because the patient's obstacle is usually not the booking link.
- Day 8. Call attempt 3. Branch on ok_to_contact_alternate: if true and the patient has not been reached in three attempts, call alternate_contact_name with the minimum-necessary script. Ask them to have the patient call the practice, disclose nothing about why.
- Day 10. Send the Barrier Survey as a single question with fixed logistical options. Any option captured writes barrier_reason and routes to the matching fix: transport to the location-change branch, cost to a human call about financial assistance, work schedule to the extended-hours slot list, childcare to the slots with the shortest wait.
- Day 12. Notify program_owner_clinician that the patient is inside the window and unreached. A clinician-signed message is a different thing than a front-desk message, and in cadence programs it is frequently the thing that works.
- At due_window_close_date: hand to Workflow 6.
Exit conditions: appointment booked (move to Rebooked, clear due-now, reset recall_attempt_count), patient declines on the Withdrawal form, sms-stop (SMS steps stop, call and mail steps continue. An SMS opt-out is not an opt-out from care), or clinician pauses the program.
Workflow 5: Visit Readiness Check
Trigger: nightly engine finds a booked program visit 14 days out.
- Create a Visit Readiness card in Readiness Due, assigned to readiness_owner.
- Branch on authorization_required. If true: check authorization_status and authorization_expires_date. If the authorization expires before the appointment date, apply auth-expiring, create an urgent renewal task, and set a hard check at 7 days out.
- Check order_status. If not placed, task the clinical team. Check supply_confirmed. If not confirmed, task whoever owns ordering and apply supply-hold.
- All three clear: move to Ready. No patient-facing message: silence is correct when everything is fine.
- Any item unresolved at 7 days out: move the readiness card to Blocked, set readiness_blocked_reason, move the adherence card to Readiness Hold, and notify both the readiness owner and the clinician.
- Any item unresolved at 3 days out: create a call task to speak to the patient by voice and offer a date that the practice can actually honor. The message is a call, not a text, and it is made by a person. A patient who travels for a visit that cannot happen is a patient the program loses.
- On resolution, return the adherence card to Next Visit Booked and let the reminder ladder resume.
Exit conditions: readiness Ready, visit completed, or visit rescheduled (which raises a fresh readiness card).
Workflow 6: Overdue Escalation and Lapse Detection
Trigger: nightly engine, on every active card past due_window_close_date.
- Recompute days_overdue. Set adherence_status = overdue. Move card to Overdue.
- Set lapse_tier by days_overdue: 1–29 → none; 30–59 → 30 and apply overdue-30; 60–89 → 60 and apply overdue-60; 90–179 → 90 and apply overdue-90; 180+ → 180.
- At tier 30: one SMS: "{{first_name}}, you're past due for your visit with {{clinician}}. Can we get you back on the schedule? {{booking_link}}": plus one call attempt and a notification to the clinician. Increment missed_visit_count once, here, not on every nightly pass.
- At tier 60: stop the SMS cadence entirely. The channel has demonstrated it does not reach this patient. Switch to a scheduled human call and a mailed letter if mail_consent allows, the letter signed by the clinician and carrying no clinical detail on the envelope or the first visible line.
- At tier 90: move card to Lapsed: Re-engagement and hand to Workflow 7. Apply lapsed and prior-lapse: the second tag persists forever, because a once-lapsed patient gets a more attentive cadence for the rest of their enrollment.
- Clinical branch, defined by the practice, not by the automation: for programs where a long gap changes what the next visit must be, the nightly engine flags the card for clinician review at a threshold the clinician sets, and self-booking is disabled on that card until a clinician clears it. Rebooking a patient into a visit that is no longer the right visit is worse than not rebooking them.
Exit conditions: visit completed (hand to Workflow 8), withdrawal recorded, or card closed by the clinician.
Workflow 7: Lapse Re-engagement
Trigger: card enters Lapsed: Re-engagement.
A slower campaign on purpose. The automated channels have already failed; the job now is one good human contact, not more volume.
- Week 1. Scheduled call from assigned_recall_owner on the Recall Call Block. A real 15-minute slot, not a spare moment. Script leads with the question, not the pitch: "We noticed we haven't seen you. What got in the way?"
- Week 1, if unreached. Mailed letter, clinician-signed, if mail_consent allows.
- Week 3. Email: subject "We'd like to get you back on the schedule". Body offers three concrete options (a different location, an early or late slot, a telehealth check-in first) because the lapse was usually caused by one of those three being unavailable.
- Week 5. Call attempt 2, at a different time of day. If ok_to_contact_alternate is true, attempt the alternate contact with the minimum-necessary script.
- Week 8. Final SMS if sms_consent is still true: "{{first_name}}, the door's open whenever you're ready. {{booking_link}}"
- Week 10. Write reengagement_outcome and move the card to Withdrawn / Closed with withdraw_reason = unreachable. Notify the clinician, who decides whether anything further is clinically required. A decision that stays with the clinician and never with a workflow.
- On any successful contact: capture barrier_reason, book the visit, move to Reactivated, and set the card's future cadence to the attentive variant: warm-up starts at 28 days out instead of 21, and the checkout task is marked mandatory.
Exit conditions: visit booked and completed, withdrawal recorded, or week 10 reached.
Workflow 8: Visit Completion and Cycle Restart
Trigger: program visit marked complete.
- Write last_completed_visit_date. Increment visit_sequence_number.
- Branch on timing. Completed inside the window: increment on_time_visit_count. Completed after due_window_close_date: increment late_visit_count, update longest_gap_days, and if the previous visit was also late, apply two-late-in-a-row and notify the clinician.
- Clear due-now, overdue-* and lapsed tags. Reset recall_attempt_count to 0. Set adherence_status = on cadence.
- Recompute the full clock forward from the new last_completed_visit_date.
- Hand to Workflow 2 so the next visit is booked before the patient leaves. The loop closes here, and this handoff is why the build compounds instead of decaying.
Exit conditions: none. This workflow is the hinge and runs on every completed visit for the life of the program.
Workflow 9: Reminder and Confirmation Ladder
Trigger: scheduled relative to next_visit_booked_datetime.
Global conditions: appointment still booked; readiness not Blocked (a blocked visit sends the readiness message instead); quiet hours enforced; every message carries date, time, location and clinician name only.
- T-7 days. SMS: "{{first_name}}, confirming your visit with {{clinician}} on {{appointment_day}} at {{appointment_time}}. Reply C to confirm or R to reschedule." R sends slots inside the due window only: never a slot that would push the patient past due_window_close_date, which is the difference between a reschedule and a lapse.
- T-2 days. Email with full logistics, directions, and any prep instruction the program requires stated in neutral operational terms: "please don't eat after midnight" rather than anything naming the test.
- T-1 day, 4:00pm. SMS reminder.
- T-2 hours. SMS arrival note with parking or check-in instruction.
- No confirmation by T-2 days: add the patient to the front desk's confirmation call list. A booked-but-unconfirmed cadence visit is the highest-value call the front desk can make that day.
Exit conditions: visit completed, canceled, or sms-stop.
Workflow 10: Same-Day Cancellation Backfill
Trigger: a program visit is canceled with less than 48 hours' notice.
- Return the canceling patient's card to Due Window Open: Unbooked and immediately offer three alternative slots inside the remaining window.
- Query cards where adherence_status is due or overdue, sms_consent = true, and no booking exists: ordered by days_overdue descending, so the most overdue patient gets first offer.
- Send to the top five only: "{{first_name}}, a slot just opened with {{clinician}} on {{day}} at {{time}}. First to reply YES gets it."
- First YES books and the others receive "That one's taken, but I can still get you in this week: {{booking_link}}". Over-offering and then disappointing people is worse than leaving the slot empty, which is why the list is capped at five.
- No reply in 90 minutes: release the slot to public self-booking.
Exit conditions: slot filled, or 90 minutes elapsed.
Workflow 11: Daily Worklist and Owner Accountability
Trigger: nightly, 30 minutes before the first Recall Call Block.
- Publish the day's worklist to assigned_recall_owner: cards in Due Window Open: Unbooked, then Overdue by days_overdue descending, then readiness cards in Blocked.
- Cap the list at what the protected blocks can actually absorb. An uncapped list is abandoned by Thursday.
- Notify the practice manager with three numbers only: cards due today, cards overdue past 30 days, and yesterday's book-at-checkout rate.
- Escalate any card with recall_attempt_count ≥ 5 and no contact to the manager for a decision: different person, different channel, or clinician involvement.
- Escalate any readiness card in Blocked for more than 48 hours to the manager, because a blocked readiness card is an appointment about to be wasted.
Exit conditions: none: runs every clinic day.
Workflow 12: Consent, Opt-Out, Quiet Hours and PHI Guard
Trigger: runs ahead of every outbound message in every workflow above.
- STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT: apply sms-stop, set sms_consent = false, remove from every SMS step, send the single required confirmation. Critical branch: an SMS opt-out does not close the adherence card and does not stop the call, mail or clinician paths. It changes the channel and nothing else. Set preferred_recall_channel = call and continue.
- START or UNSTOP: restore sms_consent with a fresh timestamp and source.
- HELP: reply with practice name, phone number and opt-out instruction.
- Before any send: suppress if the relevant channel consent is not true, and fall back to the next consented channel.
- Before any send: enforce 8:00am–8:00pm local, queueing anything that comes due inside quiet hours to 8:05am.
- Before any send: run the disclosure check. The template must come from the approved library and must not contain program_code, a program or brand name, a medication, a lab or test name, a diagnosis, a body system, a result, or a dose. Permitted payload is date, time, location, clinician name and a link, and this applies to email subject lines and voicemail scripts as strictly as to SMS bodies, because both are read and heard by people other than the patient.
- Before any voicemail: check ok_to_leave_voicemail. If false, no message. Before any alternate-contact call: check ok_to_contact_alternate and use the minimum-necessary script, which asks the patient to call the practice and discloses nothing else.
- Suppress all program messaging for any contact flagged deceased, transferred out, or clinician-paused. A recall text to a deceased patient's family is the worst message this build could ever send; the suppression check is not optional and is verified in testing.
Exit conditions: none. This guard never turns off.