messaging.pdf
Messaging & Communications Decision Guide
Companion to the Messaging & Communications docs.
Companion to the Messaging & Communications docs.
Section 1: Use Case & Participants
These questions establish the “who and why” before scoping any features. The answers should shape how you frame the rest of the conversation.
1.1 Who is messaging whom?
• Patient provider ↔
• Provider provider ↔
• Care team internally (no patient in the thread)
• System/automated patient (one-way notifications) →
• Some combination of the above
Why it matters: different participant types affect access control, thread structure, and whether a patient subject is always required on a thread.
1.2 What is the primary purpose of messaging in their product?
• Clinical care coordination (e.g. async visits, follow-ups) • Patient engagement / support • Internal operational communication • Outbound notifications only (reminders, results)
Why it matters: determines how much of the feature set is actually needed and whether async encounters/billing will come up.
1.3 Is this a new build or replacing something they have today?
• If replacing: what are they replacing, and what are the gaps that drove this?
• If new: is messaging central to the product or additive?
Why it matters: existing systems often carry assumptions about data model or UX that will surface as requirements. Knowing this early prevents surprises.
Section 2: Feature Scoping
Go through each together. For each: Yes / No / Nice-to-have / Not sure.
| # | Feature | Yes | No | Nice-to-have | Notsure |
|---|---|---|---|---|---|
| 1 | Live / real-time updates (new messages appear without refresh) | ||||
| 2 | Message response tracking and routing (assigning threads to providers or queues) | ||||
| 3 | Read receipts and unread counts | ||||
| 4 | File and image attachments | ||||
| 5 | Message editing and drafts | ||||
| 6 | Automated messaging (reminders, out-of-office, SLA escalations) | ||||
| 7 | External channel delivery (SMS, email — beyond in-app) | ||||
| 8 | Async encounters / billing |
Section 3: Feature Deep Dives
Cover each feature flagged Yes or Nice-to-have. The goal is to land on a clear recommended approach by the end of each section.
3.1 Thread Structure (always covered)
Questions:
• Are threads always tied to a specific patient, or do you need internal/admin threads with no patient context? • Are threads 1:1 only, or do you need group conversations with more than two participants? • Do threads need to be tagged or categorized (e.g. by specialty, product line, urgency)? If so, do tags need to be combinable (e.g. a thread tagged both “cardiology” and “urgent”)?
What the answers drive:
| Situation | Approach |
|---|---|
| Threads always tied to a patient | Set subject on every thread header— enables filtering by patient across the inbox |
| Internal/admin threads needed | subject is optional — don’t assume it’s always present in queries or UI |
| 1:1 only | Simpler participant model; simplifies read receipt implementation |
| Group threads needed | Recipient list on thread header must include all participants including the thread creator |
| Tags/categories needed, single dimension | Use a single category entry on both the thread header and child messages |
| Tags/categories needed, multiple combinable dimensions | Use multiple category entries — more maintainable but queries may need to match several categories |
3.2 Live Updates
Questions:
• Do new messages need to appear in real time without a page refresh? • Is this needed for the patient-facing app, the provider-facing app, or both?
What the answers drive:
| Situation | Approach |
|---|---|
| Real-time updates needed | WebSocket subscriptions on the thread — requires this to be scoped and enabled for the project |
| Polling is acceptable | Simpler implementation; periodic re-fetch of thread messages |
| Notification only (e.g. badge, push) without live message rendering | Can use subscription to trigger a notification without requiring in-place message updates |
3.3 Routing & Assignment
Questions:
• Do messages get assigned to specific providers, to a pool/queue by role, or both? • Can threads be reassigned or escalated after initial assignment? • After reassignment, does the previous owner need to retain visibility of the thread? • Do you need a structured audit trail of who owned a thread and why it was rerouted?
What the answers drive:
| Situation | Approach |
|---|---|
| Individual assignment only | Set Task.owner to a specific provider; remove performerType |
| Pool/queue routing | Set Task.performerType by role type; clear Task.owner; providers claim from pool |
| Both | Use performerType for initial pool routing; set owner and clear performerType when claimed |
| Reassignment needed, previous owner loses access | Update Task.owner in place; update Communication.recipient to match |
| Reassignment needed, previous owner retains visibility | Create a new Task for the new owner; mark original as cancelled with a note pointing to the new Task |
| Audit trail needed (free text) | Append to Task.note on each reroute with author and timestamp |
| Audit trail needed (structured/queryable) | Create a Provenance resource on each reroute with a coded reason-queryable via Provenance?target=Task/{id} |
3.4 Read Receipts & Unread State
Questions:
• Are threads ever group conversations (more than 2 participants)? • Do you need to query unread counts via API — e.g. a badge count or a list of all unread threads for a user? • Do unread counts need to be tracked per-recipient in a group thread?
What the answers drive:
| Situation | Approach |
|---|---|
| 1:1 threads only | Option A: mark message status = completed when read; query unread via status filter. Simplest model. |
| Group threads, no API unread query needed | Option B: store per-participant read state as an extension on the thread header. Shows unread dot in UI; not searchable via API. |
| Group threads, API unread query needed | Option C: create a read-receipt Task per recipient per message; complete it when read. Supports badge counts and unread queries. Most complex. |
3.5 Attachments
Questions:
• What file types need to be supported? • Do attachments need to be searchable or referenceable as clinical documents — e.g. queryable by patient or document type — or are they just files to download within a message?
What the answers drive:
| Situation | Approach |
|---|---|
| Files are just downloads within a message | Store as contentAttachment on the message payload — simple, no separate resource needed |
| Attachments need to be clinical documents (searchable, referenceable by patient/type) | Create a DocumentReference and attach via contentReference on the payload — file becomes a first-class searchable clinical document. |
| Attachments are references to existing clinical resources (e.g. a lab result) | Use contentReference pointing at the relevant FHIR resource (e.g. DiagnosticReport) |
3.6 Editing & Drafts
Questions:
• Can sent messages be corrected or retracted after sending? • If so, does the original message content need to be discoverable via search (e.g. “show all edited messages”), or is it sufficient for the history to exist but not be directly queryable? • Do users need to save a draft and return to it across sessions or devices, or is per-device/browser draft storage acceptable?
What the answers drive:
| Situation | Approach |
|---|---|
| Sent messages cannot be edited | No special handling needed |
| Sent messages can be corrected; edits need to be searchable | Retract-and-correct: mark original status = entered-in-error, create new message with corrected content linked via inResponseTo and tagged with a correction category. |
| Sent messages can be corrected; searchability of edits not required | In-place payload update via patchResource. Full version history still exists but is not searchable. |
| Drafts only needed per browser/device | Store in browser localStorage keyed by threadID — no server resources needed, but lost if user clears storage or switches devices |
| Drafts need to persist across devices/sessions | Store as Communication with status=preparation; update as user types (debounced); promote to in-progress with a sent timestamp when sent. |
3.7 Automations
Questions:
• Are there events that should trigger automated messages (e.g. new lab result, appointment reminder)? • Are there SLA or response-time requirements (e.g. all messages responded to within 4 hours)? • Do providers have out-of-office or availability states that should affect routing? • Do you need to report on SLA compliance or response time metrics?
What the answers drive:
| Situation | Approach |
|---|---|
| Event-triggered automation | Bot triggered by a Subscription on the relevant resource type |
| Recurring automation | Cron-scheduled Bot that scans for stale threads and creates reminder Tasks |
| Out-of-office/availability-based rerouting | Bot triggered on new message checks provider Schedule; reroutes Task to pool if provider has no available slots |
| SLA reporting needed | Task data should be exported to an analytics platform. |
3.8 External Channels
Questions:
• Which channels — SMS, email, other? • Is this outbound-only (notify the patient) or does the patient reply back into the thread? • If inbound replies are needed, how is the patient identified from an incoming message? • Does the external provider have its own conversation ID that can be stored for thread matching?
What the answers drive:
| Situation | Approach |
|---|---|
| Outbound notifications only | App creates the Communication, then calls executeBot directly to send via the external channel. |
| Inbound replies needed | Inbound webhook Bot receives the payload, identifies the patient, matches or creates a thread. |
| External conversation ID available | Upsert thread header by that identifier on first message; use it as conditional partOf reference on all inbound messages. |
| No external conversation ID | Match thread by patient phone/email and open thread status; ambiguous cases need a defined fallback strategy. |
| Round-trip | Store channel on thread header as medium; app creates the Communication then calls executeBot to dispatch outbound. |
| Webhook retry / duplicate messages likely | Use createResourceIfNoneExist with the provider’s message ID as an identifier to prevent duplicate inbound Communications. |
3.9 Async Encounters / Billing
Questions:
• Does the customer intend to bill for messaging-based care? • How do you define a “session”? — one thread per session, grouped by day, or patient-initiated? • Can a single messaging session involve multiple patients? • Do you need to capture diagnosis codes, service type, or other clinical details per encounter?
What the answers drive:
| Situation | Approach |
|---|---|
| No billing intent | No Encounter resource needed; thread stands alone |
| Billing needed, one patient per session | Create an Encounter per session; link to the thread header via Communication.encounter |
| One thread = one session | Straightforward 1:1 mapping of thread to Encounter |
| Rolling interaction model | One Encounter spans multiple threads or a time window; multiple thread headers link to the same Encounter |
| Multiple patients can be in one session | Create a parent session Encounter + one child Encounter per patient. |
| Clinical documentation needed for billing | Populate Encounter.participant, Encounter.reasonCode, and Encounter.serviceType per patient Encounter. |