Back to the build library
Healthcare & clinics
Advanced

Lab Result Follow-Up Workflow

Move a patient from result-ready to result-discussed on a tracked clock, so nothing sits in a queue waiting for someone to notice it.

Industry
Healthcare & clinics
Build effort
Advanced
Typical time to launch
3–5 weeks
On this page
The problemWhat gets builtHow it runsAutomations in detailWhat changesBefore you start
Want this built for you?

Bring your stack to a call and we will scope it against how your business actually runs.

Book a call
01

The problem

A lab result arrives in an inbox. Somebody has to see it, a provider has to review it, a decision has to be made, the patient has to be told, and in many cases something has to happen next: a repeat test, a referral, a medication change, a conversation. Every one of those handoffs is a place where a result can stall, and the stalls are invisible. Nobody sends an alert saying "this result has been sitting unreviewed for nine days." The queue just grows, and the oldest items sink to the bottom where they are least likely to be seen.

Then the patient calls. They had blood drawn eleven days ago and heard nothing, so they call the front desk, who cannot discuss results, so they take a message for the nurse, who checks the inbox and finds the result was reviewed six days ago but nobody told the patient. That call costs staff time at three stations and costs the practice credibility, because from the patient's side the practice simply forgot about them. Practices with a manual process typically cannot answer the basic question of how many results are currently waiting on something, which means they cannot see the backlog they are carrying.

The operational failure is a missing clock and a missing owner. The clinical review itself is not automatable and should not be. A provider reads the result and decides what it means. What is automatable is everything around that: making sure the result is assigned the moment it lands, that the assignment ages visibly, that the patient is told something is ready without the result itself traveling through an insecure channel, that a result requiring a conversation gets a booked conversation rather than a voicemail, and that the loop is only closed when the patient has actually been reached. This build is a tracking and chasing system wrapped around a clinical decision it never makes.

02

What gets built

Pipeline and stages

One pipeline, Result Follow-Up, one opportunity per result set rather than per test, so a panel is one record.

  • Order Placed: test ordered, specimen not yet collected. Opens the expected-return clock.
  • Specimen Collected: drawn or sampled; result now genuinely pending.
  • Result Received: result landed, unassigned.
  • Awaiting Clinical Review: assigned to a reviewing provider.
  • Reviewed - Routine: reviewed, within expected range, no action beyond notification.
  • Reviewed - Needs Discussion: reviewed, requires a conversation before any notification goes out.
  • Reviewed - Needs Action: reviewed, requires a repeat test, referral, or treatment change.
  • Notification Sent: patient told that a result is available, by the appropriate channel.
  • Patient Contact Attempted: for results requiring a call; attempts logged.
  • Result Discussed: the patient has actually been spoken to or has viewed the result, and that is recorded.
  • Action Scheduled: the next step is booked.
  • Closed - Loop Complete: discussed and any action scheduled or completed.
  • Specimen Problem: lost, insufficient, or rejected; needs a redraw.
  • Unreachable - Escalated: the attempt ladder is exhausted and the case has gone to a named clinician.

Calendars

  • Results Review Block: a daily internal block per reviewing provider, so review is scheduled work rather than whatever is left at the end of the day.
  • Results Discussion Call: 15-minute slots a nurse or provider uses for results that need a conversation. Patients can be sent a booking link for these, which converts a game of phone tag into a kept appointment.
  • Follow-Up Visit: the clinical calendar the action branch books into.
  • Repeat Collection: short slots for a redraw or repeat test, so the action branch can book the test rather than just mentioning it.

Custom fields

Deliberately minimal on clinical content. The platform tracks the state of the follow-up process; the result itself lives in the EHR or lab portal.

  • order_id, order_placed_at, order_category (a non-diagnostic grouping such as routine screening, monitoring, or diagnostic workup), expected_return_days, ordering_provider
  • specimen_collected_at, collection_site, specimen_status (pending, received, insufficient, rejected, lost)
  • result_received_at, result_location (a pointer (portal, EHR reference) not the values), review_assigned_to, review_assigned_at, reviewed_at, reviewed_by
  • result_disposition (routine, needs discussion, needs action, urgent. Set by the reviewer, and the only field that carries any clinical weight), notification_method_required (portal, call, in-person)
  • notification_sent_at, notification_channel, patient_viewed_at, discussed_at, discussed_by, discussion_method (phone, video, in person, portal message)
  • action_required, action_type (repeat test, referral, medication change, visit), action_scheduled_at, action_completed_at
  • contact_attempts, last_attempt_at, last_attempt_outcome, escalated_at, escalated_to
  • consent_sms, consent_email, portal_enrolled, preferred_contact_channel, consent_revoked_at

Tags

  • State: result-pending, review-overdue, notification-pending, awaiting-patient-contact, loop-closed
  • Disposition routing: disposition-routine, disposition-discussion, disposition-action, disposition-urgent: deliberately non-descriptive, so a tag visible in a list view says nothing about the patient's health.
  • Exceptions: specimen-problem, result-overdue-from-lab, contact-ladder-exhausted, escalated-to-clinician
  • Action: action-pending, action-scheduled, action-overdue
  • Consent: sms-opt-out, email-opt-out, portal-only, all-outreach-paused

Forms

  • Result Review Disposition: an internal form the reviewing provider completes. Sets result_disposition, notification_method_required, action_required, and action_type. It is the single switch that routes everything downstream, and it exists so the routing decision is explicit and attributable rather than inferred.
  • Results Discussion Booking: a short form plus booking link sent to patients whose result needs a conversation. It says a clinician would like to speak with them about recent results and offers times. It says nothing about what the result is.
  • Contact Attempt Log: an internal form staff complete after each outreach attempt, writing last_attempt_outcome and incrementing contact_attempts. This is what makes the attempt ladder auditable rather than anecdotal.
  • Communication Preferences and Consent: the standing form for granting or withdrawing SMS and email consent and for recording a portal-only preference.

BO's role and guardrails

This is the build where BO's limits matter most. BO's job is to get the patient to the right human or the right secure channel, and nothing else.

  • BO can tell a patient that a result has been received and reviewed and that it is available in their portal, or that a clinician would like to speak with them, and can offer booking times.
  • BO never discloses, reads, summarizes, characterizes, or hints at a result. Not the values, not the range, not whether it is normal, not whether it is good news. "Is it normal?" is answered with "I'm not able to go over results. Let me get you to someone who can," and a handoff.
  • BO never speculates about what a result might mean, never reassures, and never says a result "looks fine" even when the disposition is routine. Reassurance is a clinical statement.
  • BO never fires on a record tagged disposition-urgent. Urgent results are a human-to-human phone call, immediately, and the automation's only job there is to alert staff loudly and then stay out of the way.
  • BO escalates on any message expressing anxiety, distress, or urgency about a result, and on anything describing a symptom. Escalation is to a live clinician, not a queue.
  • BO stops on all-outreach-paused and honors any opt-out phrasing, including indirect phrasing.

Numbers and channels

  • A dedicated local number for result notifications, A2P 10DLC registered under the practice's own legal entity, used only for neutral "something is ready" notifications. Never used to carry result content.
  • The patient portal as the only channel through which a patient reads a result. The portal is behind authentication; the SMS and email channels exist only to point at it.
  • Email from an authenticated practice subdomain, with subject lines that say a message is waiting and nothing more.
  • Inbound voice routing so a patient calling about results reaches a nurse line rather than the general front desk, with the call outcome written back to the opportunity.
  • Outbound calling from the clinical line for discussion and urgent results, with every attempt logged to the record.
  • An internal alert channel with escalation tiers: a routine review-aging digest, and a separate immediate alert path for urgent dispositions and specimen problems.
03

How it runs, step by step

  1. A provider places an order at a visit. The order flows from the EHR into the platform and creates an opportunity in Result Follow-Up at Order Placed, carrying order_id, ordering_provider, order_category, and expected_return_days. That last field starts a clock the practice can actually see.
  2. The patient goes to the draw station. Collection writes specimen_collected_at and moves the stage to Specimen Collected. If the patient was supposed to be collected and was not within the expected window, a separate nudge chases the collection rather than the result. A surprising share of "missing results" are specimens that were never taken.
  3. Within an hour of collection the patient gets a neutral expectation-setting message: "Hi [first name], your sample has been sent to the lab. We'll let you know as soon as your results are ready to view, usually within [expected_return_days] days. No news isn't good news or bad news; we'll always contact you either way." That last line removes most of the anxious calls on day five.
  4. The result arrives from the lab, either through an interface or by a staff member logging receipt. result_received_at is stamped, result_location is set as a pointer to where the actual values live, and the stage moves to Result Received. No values are written into the platform.
  5. The assignment workflow fires immediately. It assigns review_assigned_to by the ordering provider, falling back to the provider on the review rota if the ordering provider is out. review_assigned_at is stamped and the stage moves to Awaiting Clinical Review. The reviewer gets a notification carrying a record link only.
  6. The reviewer opens the result in the EHR during their Results Review Block, reads it, and completes the internal Result Review Disposition form. They set result_disposition to needs discussion, notification_method_required to call, action_required to yes, and action_type to follow-up visit. reviewed_at and reviewed_by are stamped automatically.
  7. The disposition routes the record. Stage moves to Reviewed - Needs Discussion, tag disposition-discussion is applied, and, critically, the routine portal-notification branch is suppressed. A result needing a conversation does not get an automated "your results are ready" message, because a patient who opens an abnormal result alone on a Friday evening with no one to call is a harm the workflow is responsible for.
  8. Instead the patient receives a neutral contact request: "Hi [first name], Dr. [name] would like to speak with you about your recent results. You can book a time that suits you here: [booking link], or call us at [number]." It says nothing about what the result is, and nothing implying urgency or alarm.
  9. In parallel a task is created for the nurse to call, so the patient has two routes to the conversation and does not depend on noticing a text. Each attempt is logged through the Contact Attempt Log, incrementing contact_attempts and writing last_attempt_outcome.
  10. The patient books a Results Discussion Call slot for the next morning. Stage moves to Patient Contact Attempted. The booking confirmation carries the time and the clinician's name, nothing more.
  11. The call happens. The clinician discusses the result, and afterwards records discussed_at, discussed_by, and discussion_method. Stage moves to Result Discussed. This is the moment the loop actually closes, not when a notification was sent, which is a different and much weaker fact.
  12. Because action_required is yes, the action branch starts. It offers the Follow-Up Visit calendar and keeps offering until action_scheduled_at is populated, with reminders on a defined cadence and a staff task if the window closes unfilled. Stage moves to Action Scheduled.
  13. Had the disposition been routine instead, the record would have taken the short path: a portal notification, notification_sent_at stamped, and the loop closed on patient_viewed_at rather than on a conversation. A routine result that is never viewed still gets chased. It just gets chased more gently.
  14. Once the action is scheduled, the stage moves to Closed - Loop Complete and the record leaves the active board. Every timestamp stays: when the order was placed, when the specimen was collected, when the result arrived, who reviewed it and when, what they decided, when the patient was told, by what method, who told them, and what was scheduled. That chain is the thing a practice almost never has, and the thing that is most valuable when a question is asked months later.
04

Automations in detail

Nine workflows. Two rules apply to every one of them: no workflow ever puts result content into a message, and no workflow fires patient-facing messaging on a record tagged disposition-urgent. Build those as global suppression conditions rather than remembering to add them per workflow.

1. Order Placed and Collection Chase

Trigger: order event received from the EHR, creating an opportunity at Order Placed.

Conditions and branches: branch on whether collection happens in-house today or at an external site later. In-house same-day collection skips straight to workflow 2 on the collection event.

Actions in order:

  1. Write order_id, order_placed_at, order_category, expected_return_days, ordering_provider. Add tag result-pending.
  2. External-site branch, wait 3 days. If specimen_collected_at is empty, SMS: "Hi [first name], [provider name] ordered a test for you at your last visit. Here's where to get it done: [instructions link]. Questions? Call [number]."
  3. Wait 4 days. If still uncollected, one email reminder on the same neutral pattern.
  4. Wait 7 days. If still uncollected, create a staff task to call and notify the ordering provider, since an uncollected order is a clinical loose end rather than an administrative one.
  5. Wait 14 days. If still uncollected, move to a closed-with-reason state and notify the ordering provider so they can decide whether it still matters.

Stop and exit: specimen_collected_at populated, order canceled, or the provider closes it. Never name the test in any of these messages.

2. Specimen Collected and Expectation Setting

Trigger: specimen_collected_at populated; stage moves to Specimen Collected.

Conditions and branches: branch on consent_sms and portal_enrolled. Patients not enrolled in the portal get a portal invitation in the same message, because enrollment is what makes the rest of the build work.

Actions in order:

  1. Wait 1 hour. SMS: "Hi [first name], your sample is on its way to the lab. We'll let you know as soon as results are ready to view, usually [expected_return_days] days. No news isn't good or bad news; we'll contact you either way."
  2. Not-enrolled branch, add: "Results go to your patient portal. If you haven't set it up yet, it takes a minute: [enrollment link]."
  3. Set a timer for expected_return_days plus the practice's grace period.
  4. If that timer expires with result_received_at still empty, add tag result-overdue-from-lab and create an internal task to chase the lab. No patient message. The patient has already been told "usually", and chasing the lab is the practice's job.
  5. If specimen_status comes back insufficient, rejected, or lost: move stage to Specimen Problem, tag specimen-problem, notify the ordering provider, and run the redraw branch. A booking link to Repeat Collection with the copy "Hi [first name], the lab wasn't able to complete your test and we need another sample. Sorry about that. Book a quick redraw here: [booking link]."

Stop and exit: result received, or the record reaches Specimen Problem and the redraw branch takes over.

3. Result Intake and Review Assignment

Trigger: result received event, or a staff member logging receipt.

Conditions and branches:

  • Assign to ordering_provider if they are available. If they are flagged out, assign to the provider on the review rota and note the substitution on the record.
  • If the lab transmits a critical flag, branch immediately to workflow 8 and bypass everything else.

Actions in order:

  1. Stamp result_received_at and write result_location as a pointer. Do not write values into the platform.
  2. Set review_assigned_to and stamp review_assigned_at. Move stage to Awaiting Clinical Review.
  3. Notify the assigned reviewer on the internal channel. The notification body says only "A result is ready for your review" plus a record link. No patient name, no test name.
  4. Create a review task on the reviewer's Results Review Block, due within the practice's review target.
  5. Wait until the review target elapses. If reviewed_at is empty, re-notify the reviewer and add tag review-overdue.
  6. Wait a further interval equal to the review target. If still unreviewed, escalate to the clinical lead and include the record in the daily aging digest.
  7. Wait a further 24 hours. If still unreviewed, escalate again to the practice's designated final escalation and mark the record for the morning huddle. The ladder never stops nudging and never silently gives up.

Stop and exit: reviewed_at populated.

4. Disposition Routing

Trigger: Result Review Disposition form submitted, writing result_disposition.

Conditions and branches: this is the switchboard, and the four branches must be genuinely separate paths rather than variations on one message.

  • Routine: stage to Reviewed - Routine, tag disposition-routine, release to workflow 5.
  • Needs discussion: stage to Reviewed - Needs Discussion, tag disposition-discussion, suppress workflow 5 entirely, release to workflow 6.
  • Needs action: stage to Reviewed - Needs Action, tag disposition-action. Route to workflow 6 first if a conversation is required before the action, otherwise to workflow 7.
  • Urgent: tag disposition-urgent, suppress every patient-facing workflow, release to workflow 8 only.

Actions in order:

  1. Stamp reviewed_at and reviewed_by. Write notification_method_required, action_required, action_type.
  2. Remove review-overdue if present. Apply the disposition tag.
  3. Move the stage and hand off to the single appropriate downstream workflow.
  4. Write an audit entry recording the disposition decision with its timestamp and author.

Stop and exit: immediately after routing. This workflow sends nothing and waits for nothing; it exists to make the routing decision explicit and attributable.

5. Routine Result Notification

Trigger: tag disposition-routine applied.

Conditions and branches: branch on portal_enrolled. Enrolled patients get the portal notification. Not enrolled: send an enrollment prompt, and if they do not enroll within the window, create a task to mail or call rather than leaving them uninformed. Branch on consent_sms and portal-only for channel selection.

Actions in order:

  1. Release the result to the portal per practice policy. Stamp notification_sent_at and notification_channel. Move stage to Notification Sent. Add tag notification-pending.
  2. SMS: "Hi [first name], your recent results have been reviewed by [provider name] and are now in your patient portal: [portal link]. If you have questions after reading them, call [number]." No characterization of the result, not even "routine". The patient reads the result, not the workflow's summary of it.
  3. Email in parallel. Subject: "A new message is in your patient portal". Body points at the portal and nothing else.
  4. Wait 3 days. If patient_viewed_at is empty, one reminder: "Hi [first name], just a reminder that your results are waiting in your portal: [portal link]."
  5. Wait 4 days. If still unviewed, create a staff task to call, increment contact_attempts, and move stage to Patient Contact Attempted.
  6. On patient_viewed_at being stamped: remove notification-pending, add loop-closed, move stage to Result Discussed with discussion_method set to portal, then to Closed - Loop Complete if action_required is false.

Stop and exit: result viewed, staff log a conversation, or the ladder exhausts into workflow 9. Maximum two automated reminders.

6. Discussion-Required Contact Ladder

Trigger: tag disposition-discussion applied, or notification_method_required set to call.

Conditions and branches: branch on whether the practice permits a patient-booked discussion slot for this category, or requires an outbound staff call. Where booking is permitted, run both paths in parallel (a booking link and a call task) because either one closing the loop is a win.

Actions in order:

  1. Add tag awaiting-patient-contact. Create a call task for the nurse, due same business day, carrying a record link only.
  2. Within 1 hour, if booking is permitted, SMS: "Hi [first name], [provider name] would like to talk through your recent results with you. Book a time here: [booking link], or call us at [number]." No urgency framing, no detail, no "please call as soon as possible". That phrasing reads as alarming and it is not information, it is anxiety.
  3. Log attempt one through the Contact Attempt Log. Increment contact_attempts, stamp last_attempt_at and last_attempt_outcome.
  4. Wait 1 day. If discussed_at is empty, attempt two. A phone call task, plus an email on the neutral pattern. Voicemail copy, if left: "This is [name] from [practice name] calling for [first name]. Please call us back at [number] at your convenience." Nothing about results in a voicemail, because voicemails are heard by households.
  5. Wait 2 days. Attempt three: call task plus one SMS reminder with the booking link.
  6. Wait 2 days. Attempt four. Call task assigned to a senior staff member, and notify the reviewing provider that contact has not been made.
  7. At attempt five or at 10 days from the disposition, whichever comes first: move stage to Unreachable - Escalated, tag contact-ladder-exhausted and escalated-to-clinician, stamp escalated_at and escalated_to, and hand to workflow 9.
  8. On contact being made: staff record discussed_at, discussed_by, discussion_method. Stage moves to Result Discussed. Release to workflow 7 if action_required is true.

Stop and exit: discussed_at populated, or escalation. Hard cap on automated patient messages at three across the whole ladder, regardless of attempt count. The rest of the ladder is human calls.

7. Action Scheduling and Overdue Chase

Trigger: action_required true with discussed_at populated.

Conditions and branches: branch on action_type. Repeat test offers Repeat Collection; visit offers Follow-Up Visit; referral creates a referral task rather than a patient booking link, since the practice has to initiate it; medication change creates a task for the provider and suppresses patient booking entirely.

Actions in order:

  1. Add tag action-pending. Move stage toward Action Scheduled.
  2. Within 2 hours, send the matching booking link: "Hi [first name], as [provider name] discussed, the next step is [neutral description such as 'a repeat test' or 'a follow-up visit']. Book here: [booking link]." The neutral description comes from action_type, which is a process category rather than a clinical finding.
  3. Wait 3 days. If action_scheduled_at is empty, one SMS reminder.
  4. Wait 4 days. If still unscheduled, one email plus a staff call task.
  5. Wait 7 days. If still unscheduled, add tag action-overdue, notify the ordering provider, and include in the weekly overdue-action review.
  6. On booking: stamp action_scheduled_at, swap action-pending for action-scheduled, move stage to Action Scheduled.
  7. On the action being completed, stamp action_completed_at, add loop-closed, move stage to Closed - Loop Complete. If the action was a repeat test, this closes one record and workflow 1 opens the next. The loop is genuinely a loop.

Stop and exit: action scheduled or completed, or the provider closes it as no longer required.

8. Urgent Disposition Alert

Trigger: result_disposition set to urgent, or a critical flag received with the result.

Conditions and branches: none. This workflow has one path deliberately, because branching logic in an urgent path is a failure mode. It escalates by time until a human acknowledges.

Actions in order:

  1. Apply tag disposition-urgent and set a global suppression on all patient-facing automation for this record. Nothing automated reaches the patient from this point on.
  2. Immediately alert the reviewing provider and the clinical lead simultaneously on the priority channel, and by phone if the platform supports it. The alert says a result requires immediate attention and carries a record link.
  3. Create a high-priority task, "Contact patient directly regarding urgent result", assigned to the reviewing provider with the clinical lead as a watcher.
  4. Wait 15 minutes. If the task is unacknowledged, re-alert both plus the next person on the escalation list.
  5. Wait 15 minutes. If still unacknowledged, escalate to the practice's final on-call contact.
  6. Continue escalating at 15-minute intervals until acknowledged. This workflow does not time out, and that is intentional.
  7. On acknowledgment, the clinician calls the patient directly and records discussed_at, discussed_by, discussion_method, and the attempt log. Every attempt is recorded even if contact fails.
  8. If contact fails after the clinician's own attempts, the record goes to workflow 9 with the urgent flag intact, which changes the escalation there.

Stop and exit: a clinician acknowledges and the patient contact is recorded. No automated patient message is ever sent from this workflow. Its whole job is to make a human pick up a phone quickly.

9. Unreachable Escalation and Loop Audit

Trigger: stage moves to Unreachable - Escalated, or a record has been open past the practice's maximum age.

Conditions and branches: branch on the disposition carried in. Urgent and action-required records escalate to a named clinician immediately. Routine records go to a weekly review instead, because the risk profile is different and treating them identically buries the ones that matter.

Actions in order:

  1. Stamp escalated_at and escalated_to. Tag escalated-to-clinician.
  2. Notify the named clinician with the full attempt history: how many attempts, on what channels, with what outcomes, over what period. This is what the Contact Attempt Log was collecting.
  3. Create a task to decide the next step, which may include a letter, contacting an emergency contact where permitted, or a clinical judgment that no further action is warranted. The decision and its author are recorded on the record.
  4. Suppress all automated patient messaging on this record. From escalation onward, every contact is human and logged.
  5. Run a weekly digest to the clinical lead: every record open past the maximum age, grouped by what it is waiting on, with the oldest first. This digest is the backstop for the whole build. It is the thing that catches what the other eight workflows missed.
  6. Run a monthly loop-closure report: results received, results reviewed inside target, loops closed, loops escalated, and average days to closure by disposition. Review it as a standing agenda item rather than on request.

Stop and exit: the clinician records a decision and closes the record. A record never closes itself out of this workflow by timing out.

Before launch, test four things specifically: that a non-routine disposition cannot trigger the routine notification branch; that disposition-urgent suppresses every patient-facing send; that an opt-out mid-ladder routes to a staff task rather than ending contact; and that the attempt history exports in a readable form. Also read one of your own notification templates out loud as if it were a voicemail heard by a family member. If it discloses anything, rewrite it.

05

What changes

The backlog becomes visible. Before this build, the honest answer to "how many results are waiting on us right now?" is usually a guess. Afterwards it is a board, split by what each result is waiting on: review, notification, patient contact, or a scheduled action. You cannot work a queue you cannot see, and most practices discover the backlog is larger and older than they assumed.

Review stops being the thing that happens last. An assignment that ages visibly, with a named owner and an escalation at a defined threshold, behaves completely differently from an inbox that nobody is individually accountable for.

The loop closes on contact rather than on sending. This is the single most important shift in the build. "We sent a notification" and "the patient knows" are different facts, and a process that treats them as the same will lose patients quietly. By making discussed_at or patient_viewed_at the closing condition, the workflow keeps chasing until something actually happened.

Patient-initiated calls drop sharply, because the expectation-setting message on day one and the notification on the day of review remove the two moments that generate them. Staff stop fielding "did my results come back?" and the nurse line gets its time back.

And the record becomes defensible. Every result carries who reviewed it, when, what they decided, when and how the patient was contacted, how many attempts it took, and what was scheduled. That is a materially better position than a recollection and a sticky note.

Metrics to watch

  • Hours from result_received_at to reviewed_at, as a median and as a 90th percentile. The tail is where the risk lives, so watch it more closely than the average.
  • Count and age of records at Awaiting Clinical Review, by reviewer. This surfaces capacity problems as well as individual backlogs.
  • Hours from reviewed_at to notification_sent_at: the gap that causes the day-eleven phone call.
  • Days from result_received_at to loop closed, segmented by result_disposition. Discussion-required results will always be slower; what matters is whether they are slower than your own target.
  • Loop closure rate: the share of results reaching Closed - Loop Complete rather than aging out. The most important number on this list.
  • Average contact_attempts for discussion-required results, and the share reaching Unreachable - Escalated.
  • Portal view rate for routine notifications, which tells you whether portal enrollment is real or theoretical.
  • Action scheduling rate and days to schedule among records with action_required true, plus the count sitting at action-overdue.
  • Results overdue from the lab: orders past expected_return_days with nothing received. This catches lost specimens and interface failures that otherwise look exactly like a slow lab.
  • Inbound patient calls asking about results, as a count per week. It should fall, and it is the cleanest proxy for whether patients feel informed.
06

Before you start

Operational prerequisites

This build touches clinical responsibility more directly than any other in the clinical set, so the decisions below need to be made by clinicians and documented before anything is configured.

  • A written disposition policy. What counts as routine, what needs discussion, what needs action, and what is urgent. The automation routes on result_disposition and does nothing else clinical; the definitions behind that field are entirely the practice's.
  • A named review rota with coverage for absence. Assignment to a provider who is on leave is the most common way a result sits for three weeks.
  • Defined review targets and escalation thresholds. How many hours before an unreviewed result escalates, and to whom. Set these yourself; do not inherit a default.
  • A decision about urgent results that the automation must respect. The strong recommendation is that urgent dispositions trigger an immediate staff alert and nothing patient-facing at all, with the clinician calling directly. Decide this explicitly and configure the suppression, because it is the branch where automation can do real harm.
  • A policy on automatic result release to the portal. Many EHRs now release results to patients on their own timetable, which may put a result in front of a patient before it has been reviewed. You need to know what your EHR does by default and build around that reality, because your notification sequence is no longer the only thing the patient sees.
  • An attempt ladder and an endpoint. How many attempts, over how many days, across which channels, before a result is escalated to a named clinician, and what happens then, including whether a letter is sent. The ladder must have a defined end, and the end must be a person, not a stage.
  • A named clinician who owns the unreachable queue. This queue is small and it is the highest-risk thing in the build.

Integrations

  • The EHR or lab information system, through a native interface where one exists or Zapier or Make otherwise, delivering order placement, specimen collection, and result receipt events. The platform should receive the fact of a result and a pointer to it, not the values. Keep the clinical content in the clinical system.
  • The patient portal, which is the only channel through which a patient reads a result.
  • Google Calendar or Outlook for review blocks and discussion call availability.
  • Slack or email for internal alerts, configured with two distinct tiers so an urgent alert does not arrive looking like a routine digest.
  • Telephony for outbound discussion calls, with call outcomes logged against the opportunity.

Numbers and messaging setup

  • Provision a dedicated local number and complete A2P 10DLC brand and campaign registration under the practice's own legal entity before launch, registered to reflect care-coordination messaging rather than marketing.
  • Configure opt-out handling for STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, and QUIT, plus natural-language variants, with immediate and global suppression including sequences already mid-wait.
  • Decide and document the fallback for a patient with no usable electronic channel: a phone call, a letter, or both. In this build the fallback is not optional. A result that needs communicating still needs communicating, and an opted-out patient must route to a staff task rather than disappearing.
  • Set quiet hours in the patient's time zone and confirm them against your state's rules. Then make one deliberate exception: urgent dispositions bypass automated messaging entirely and go to a staff alert, so quiet hours never delay an urgent result. They just mean a human places the call rather than a workflow sending a text.
  • Never send result notifications in a batch at a fixed hour if that hour is late. A notification arriving at 9pm with no one to call generates a night of worry for no operational gain.

Protected health information and consent

This is the highest-sensitivity build in the clinical set. The rules below are not refinements; they are the design.

  • No result content in SMS, ever. No values, no ranges, no "normal" or "abnormal", no test name, no interpretation, no reassurance. An SMS says that something is available and where to go. A test name alone can disclose a condition, so the test name stays out too.
  • No clinical content in email subject lines, and none in email bodies either. Treat email as a notification channel pointing at the portal, not as a delivery channel. "A new message is waiting in your patient portal" is the whole subject line.
  • Deliver results only through an authenticated channel. The portal, a phone call to a verified patient, or an in-person conversation. Verify identity before discussing a result by phone, including on an outbound call. The person who answers a mobile is not always the patient.
  • Apply the minimum necessary standard to internal notifications too. A Slack alert saying a result needs review should carry a record link and nothing else. Internal convenience is not a reason to put clinical detail into a channel that is backed up, searchable, and visible to people who do not need it.
  • Never let automation disclose an abnormal result. Suppress the routine notification branch for anything other than a routine disposition, and route it to a human conversation. This is the single most important configuration decision in the build.
  • Handle authorized representatives explicitly. Minors, guardianship arrangements, and patients who have designated someone to receive information on their behalf all need to be reflected in who may be contacted and what may be said. Build this as a field on the record rather than relying on staff memory, and know that the rules differ by state and by the patient's age.
  • Capture consent per channel with timestamps, and honor revocation immediately and globally. Record a portal-only preference as a first-class choice, since some patients want results in the portal and no texts at all. Revocation must stop queued sends, not just future triggers.
  • Keep a complete audit trail and protect it. Who was assigned, when, who reviewed and when, what disposition was set, every notification with channel and timestamp, every contact attempt and its outcome, who discussed the result and how, and what was scheduled. Confirm the retention period, confirm the trail cannot be edited or deleted by ordinary users, and test that you can actually export it. An audit trail you cannot produce is not an audit trail.
  • Restrict access by role. Front desk staff generally need to see that a result is pending and who owns it, not the disposition. Set field-level permissions accordingly, and note that this is why the disposition tags are deliberately opaque rather than descriptive.
  • Put a signed business associate agreement in place before the first live record, covering the platform, the messaging carrier, the email sender, the portal if it is a separate vendor, the telephony provider, and any automation bridge such as Zapier or Make. Check specifically whether the automation bridge logs payload contents, because a middleware log holding result data is an exposure that is easy to create and easy to miss.

This section describes operational practice, not legal advice. Result communication requirements, timelines, release rules, and the handling of minors and representatives vary by state and by the practice's own clinical governance, and the rules around automatic result release in particular have been changing. Confirm every specific with the practice's own compliance counsel and clinical leadership before going live, and re-confirm whenever your EHR changes its release behavior. To scope this build for your practice, book a call at bookings.appbo.io.

Field notes

Build it with us.

Every build on this page is something we configure, test and hand over with the SOPs your team needs to run it.

Book a call