OZO FHIR implementation guide
0.2.2 - ci-build
OZO FHIR implementation guide - Local Development build (v0.2.2) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions
requester fieldinput/fsh/profiles/ozo-communicationrequest.fshinput/fsh/profiles/ozo-communication.fshinput/pagecontent/interaction-messaging.mdinput/pagecontent/overview.mdinput/fsh/instances/communicationrequest-team-example.fsh (NEW)input/fsh/instances/communication-team-reply-example.fsh (NEW)This document analyzes the addressing problem in the FHIR proposal for team-level messaging and proposes a solution to enable team-to-team communication using CareTeam while maintaining individual auditability.
Related: See the Organization Addressing Proposal for the original business requirements and high-level solution overview.
basedOninResponseTorequester fieldrequester can be different from sendersender allows: Device, Organization, Patient, Practitioner, PractitionerRole, RelatedPerson, HealthcareServicesender to: Practitioner, RelatedPerson (excludes Organization!)recipient allows: Device, Organization, Patient, Practitioner, PractitionerRole, RelatedPerson, HealthcareService, Group, CareTeam, Endpointrecipient to: Practitioner, RelatedPerson, CareTeam (excludes Organization!)Step 1: Pharmacy sends request
CommunicationRequest:
requester = Practitioner/A1 ← Individual who initiated (auditability)
sender = Organization/Pharmacy-A ← Reply-to address
recipient = Organization/Clinic-B ← Addressed to organization
payload = "Can you review patient's medication?"
Step 2: Clinic replies
Communication:
sender = Practitioner/B1 ← Individual responds (auditability)
recipient = Organization/Pharmacy-A ← Read from CommunicationRequest.sender
basedOn = CommunicationRequest/...
payload = "Yes, I'll review it today"
Why this works:
requester tracks the individual who initiated (auditability)sender in CommunicationRequest provides the Organization reply-to addresssender in Communication tracks the individual who responds (auditability)Step 1: Pharmacy sends request
CommunicationRequest:
requester = Practitioner/A1 ← Must be Practitioner (OZO constraint)
sender = Practitioner/A1 ← Must be Practitioner (OZO constraint)
recipient = CareTeam/B ← Using CareTeam as proxy
Step 2: Clinic needs to reply
Communication:
sender = Practitioner/B1
recipient = ??? ← No Organization in original request to reply to!
The issue:
requester fieldNote: This is the recommended solution. See Detailed Analysis below for complete specification.
requester: **Keep as Practitioner |
RelatedPerson** (to track individual who initiated the conversation) |
sender: Allow CareTeam (in addition to Practitioner, RelatedPerson)recipient: **Keep as Practitioner |
RelatedPerson | CareTeam** (no change) |
sender: Allow CareTeam or **Practitioner |
RelatedPerson** (CareTeam for team-level authorization, individual for auditability) |
recipient: **Keep as Practitioner |
RelatedPerson | CareTeam** (no change) |
CommunicationRequest.requester tracks who initiated the conversationStep 1: Pharmacy A initiates conversation with first message
CommunicationRequest:
requester = Practitioner/A1 (individual who initiated - auditability)
sender = CareTeam/Pharmacy-A (team is thread owner - reply-to address)
recipient = CareTeam/Clinic-B (addressed to team)
payload = "Can you review patient's medication?" (initial message)
subject = Patient/123
Step 2: Practitioner B1 from Clinic B replies
Communication:
sender = Practitioner/B1 (individual responds for auditability)
recipient = CareTeam/Pharmacy-A (read from CommunicationRequest.sender)
basedOn = CommunicationRequest/... (links to thread)
payload = "Yes, I'll review and respond by tomorrow"
Step 3: Another practitioner A2 from Pharmacy A follows up
Communication:
sender = Practitioner/A2 (different person from same team)
recipient = CareTeam/Clinic-B (continue to same team)
basedOn = CommunicationRequest/... (links to thread)
inResponseTo = Communication/step2 (reply to previous message)
payload = "Thank you for the quick response"
| Resource | Field | Allowed Types | Note |
|---|---|---|---|
| CommunicationRequest | requester | Practitioner, RelatedPerson | No change - tracks individual who initiated |
| CommunicationRequest | sender | Practitioner, RelatedPerson, CareTeam | CareTeam added - team-level reply-to address |
| CommunicationRequest | recipient | Practitioner, RelatedPerson, CareTeam | No change |
| Communication | sender | Practitioner, RelatedPerson | No change - must be individual for auditability |
| Communication | recipient | Practitioner, RelatedPerson, CareTeam | No change |
Core Principle: Allow CareTeam as sender in both CommunicationRequest and Communication, while maintaining individual auditability through the requester field and tracking the individual team member separately.
The original OZO constraints created an impossible situation:
Relax constraints to allow CareTeam:
CommunicationRequest.requester: Practitioner | RelatedPerson (NO CHANGE - must be individual)
CommunicationRequest.sender: Practitioner | RelatedPerson | CareTeam
CommunicationRequest.recipient: Practitioner | RelatedPerson | CareTeam (NO CHANGE)
Purpose:
requester identifies the individual who initiated (auditability)sender can be CareTeam (reply-to address for the conversation, team-level authorization)recipient remains CareTeam-capable (shared inbox for receiving team)Keep constraints as-is (individual sender required):
Communication.sender: Practitioner | RelatedPerson (NO CHANGE - must be individual)
Communication.recipient: Practitioner | RelatedPerson | CareTeam (NO CHANGE)
Purpose:
Step 1: Thread Initiation
Instance: thread-pharmacy-to-clinic
InstanceOf: CommunicationRequest
* status = #active
* subject = Reference(Patient/123)
* requester = Reference(Practitioner/A1) // ← Individual who initiated
* sender = Reference(CareTeam/Pharmacy-A) // ← Reply-to address (team authorization)
* recipient = Reference(CareTeam/Clinic-B)
* payload[0].contentString = "Can you review the patient's medication list?"
Step 2: Reply from Clinic (Practitioner B1)
Instance: msg-1-pharmacy-question
InstanceOf: Communication
* status = #completed
* basedOn = Reference(CommunicationRequest/thread-pharmacy-to-clinic)
* sender = Reference(Practitioner/A1) // ← Individual auditability
* recipient = Reference(CareTeam/Clinic-B) // ← From CommunicationRequest.recipient
* payload[0].contentString = "Can you review the patient's medication list?"
Application Logic for Reply-To Discovery:
basedOn referenceCommunicationRequest.sender is CareTeam → use that as reply recipientStep 3: Reply (Practitioner B1)
Instance: msg-2-clinic-response
InstanceOf: Communication
* status = #completed
* basedOn = Reference(CommunicationRequest/thread-pharmacy-to-clinic)
* inResponseTo = Reference(Communication/msg-1-pharmacy-question)
* sender = Reference(Practitioner/B1) // ← Individual auditability
* recipient = Reference(CareTeam/Pharmacy-A) // ← From CommunicationRequest.sender
* payload[0].contentString = "Yes, I'll review it today and follow up by EOD"
Step 4: Follow-up (Different Practitioner A2)
Instance: msg-3-pharmacy-followup
InstanceOf: Communication
* status = #completed
* basedOn = Reference(CommunicationRequest/thread-pharmacy-to-clinic)
* inResponseTo = Reference(Communication/msg-2-clinic-response)
* sender = Reference(Practitioner/A2) // ← Different person, same team
* recipient = Reference(CareTeam/Clinic-B) // ← Continue to same team
* payload[0].contentString = "Thank you for the quick response!"
GET /Communication?recipient=CareTeam/Pharmacy-A&_include=Communication:based-on
Returns all Communications where recipient is the CareTeam, plus their parent CommunicationRequests.
GET /Communication?sender=Practitioner/A1
Returns all Communications sent by this individual.
GET /Communication?based-on=CommunicationRequest/thread-pharmacy-to-clinic
&_sort=sent
Returns chronologically sorted thread.
input/fsh/profiles/ozo-communicationrequest.fshCurrent constraints to relax:
// CURRENT:
* sender only Reference(OZOPractitioner or OZORelatedPerson)
* recipient only Reference(OZOPractitioner or OZORelatedPerson or OZOCareTeam)
// CHANGE TO:
* sender only Reference(OZOPractitioner or OZORelatedPerson or OZOCareTeam)
* recipient only Reference(OZOPractitioner or OZORelatedPerson or OZOCareTeam)
// KEEP AS IS (no change):
* requester only Reference(OZOPractitioner or OZORelatedPerson)
Rationale:
requester stays restricted to individuals (auditability - tracks who initiated)sender now allows CareTeam (provides reply-to address and team-level authorization)recipient remains unchanged (CareTeam already supported)input/fsh/profiles/ozo-communication.fshCurrent constraints:
// NO CHANGES NEEDED:
* recipient only Reference(OZOPractitioner or OZORelatedPerson or OZOCareTeam)
* sender only Reference(OZOPractitioner or OZORelatedPerson)
Rationale:
sender stays restricted to individuals (auditability - tracks who sent each message)recipient already allows CareTeam (no change needed)input/pagecontent/interaction-messaging.mdAdd new section:
CommunicationRequest.senderUpdate existing examples:
CommunicationRequest with:
requester = Practitioner (individual who initiated)sender = CareTeam (reply-to address with team-level authorization)recipient = CareTeam (addressed to team)Communication replies with:
sender = Practitioner (individual auditability)recipient = CareTeam (from CommunicationRequest.sender)input/pagecontent/overview.mdUpdate CommunicationRequest description:
The `CommunicationRequest` can use CareTeam as sender for team-level messaging:
- `requester`: Individual who initiated (Practitioner or RelatedPerson) - for auditability
- `sender`: Can be CareTeam to provide reply-to address and team-level authorization
- `recipient`: Can be CareTeam to enable shared inbox pattern for teams
Update Communication description:
The `Communication` sender must remain an individual for auditability:
- `sender`: Individual who sent the message (Practitioner or RelatedPerson) - for auditability
- `recipient`: Can be CareTeam to address messages to all members of a team
- Individual team member tracked separately for display and delete permissions
input/fsh/instances/communicationrequest-team-example.fsh (NEW)Instance: CommunicationRequest-Pharmacy-to-Clinic
InstanceOf: OZOCommunicationRequest
Title: "Example: Pharmacy to Clinic Team-Level Communication Request"
Description: "Shows a CommunicationRequest from a pharmacy team to a clinic team using CareTeam addressing"
* status = #active
* subject = Reference(Patient/example-patient)
* requester = Reference(Practitioner/pharmacy-practitioner-a1)
* sender = Reference(CareTeam/pharmacy-a)
* recipient = Reference(CareTeam/clinic-b)
* payload[0].contentString = "Can you review this patient's medication list for potential interactions?"
input/fsh/instances/communication-team-reply-example.fsh (NEW)Instance: Communication-Clinic-Reply
InstanceOf: OZOCommunication
Title: "Example: Clinic Reply to Team-Level Message"
Description: "Shows a Communication reply from clinic practitioner to pharmacy team"
* status = #completed
* basedOn = Reference(CommunicationRequest/CommunicationRequest-Pharmacy-to-Clinic)
* sender = Reference(Practitioner/clinic-practitioner-b1)
* recipient = Reference(CareTeam/pharmacy-a)
* payload[0].contentString = "I've reviewed the medications and will send my recommendations today"
| Aspect | Original Proposal | Recommended Solution (CareTeam as Sender) |
|---|---|---|
| Addressing mechanism | Practitioner as sender with extension | CareTeam as sender in CommunicationRequest |
| Team authorization | Unclear | CareTeam gives team-level authorization |
| Individual auditability | Via extension | Communication.sender = Practitioner |
| Reply-to discovery | Complex | Simple (read from CommunicationRequest.sender) |
| Custom extensions | Yes (for tracking team) | Not required for basic flow |
| Profile changes needed | Yes | Yes (allow CareTeam in sender) |
This solution provides team-level authorization while maintaining individual auditability through proper use of requester and sender fields.