Skip to content

Communication Profile

Canonical../StructureDefinition/nexus-emr-core-communication
Statusdraft (experimental) · 1.26.0
BaseCommunication (constraint)
SourceFSH · JSON

Nexus EMR profile for Communication, covering two distinct populations: WELL-internal staff chat about a patient, and eReferral correspondence passed through from or to external partners. First pass -- the pass-through boundary and message threading are not yet settled.

FSH

The authored source of truth: resources/StructureDefinition-nexus-emr-core-communication.fsh (edit on GitHub). One FSH file may define several related artifacts.

// NexusEmrCoreCommunication.fsh
// FIRST PASS. Two genuinely different things share this resource type today, and the profile
// has to acknowledge both without pretending they are one model.
//
//   1. WELL-INTERNAL CHAT. The in-app chat panel runs entirely on Communication -- staff
//      messaging each other about a patient. Internal operational read/write.
//   2. eREFERRAL PASS-THROUGH. Communication instances arriving from and going to external
//      eReferral partners (Ocean and others), where the shape is the partner's, not ours.
//
// THE TWO NEED DIFFERENT THINGS FROM THIS GUIDE, which is why they are modelled apart rather than
// reconciled. Pass-through (2) needs to be DOCUMENTED: the shape is the partner's, and a consumer
// needs to know it arrives unaltered and what may be relied on. Internal chat (1) needs a governed
// CONTRACT, because it is a surface this platform owns and writes, and nobody else's specification
// is going to constrain it.
//
// WHAT THIS RESOURCE IS NOT, ruled 2026-08-19: **it is not the conversation on a Task.** An earlier
// design paired every Task with Communication resources for its turns, and both shapes ended up
// partly documented at once, which is worse than either. A task's turns are `Task.note` repeats,
// typed to the Task Note profile -- see `StructureDefinition-nexus-emr-task-note.fsh`, and the
// `note` element on the ClinicalTask profile for the reasoning and for the additive graduation
// valve if per-turn identity is ever needed. The two populations above are messages that exist in
// their own right; a task turn is not one of them, and a writer reaching for Communication to
// record "what was said on this task" is reaching for the wrong resource.
//
// FIRST-PASS SCOPE. Skeleton, the `category` discriminator that separates the two populations,
// and the constraints that keep internal chat readable. Deliberately NOT settled: the
// pass-through boundary (which elements external partners may set freely), message threading
// (`inResponseTo` vs `partOf` -- the chat panel currently threads by neither), and whether
// internal chat should move off Communication entirely onto a non-clinical transport.

Alias: $NexusEmrCoreCommunication = ../StructureDefinition/nexus-emr-core-communication

// The category discriminator is what lets a reader tell internal chat from partner traffic.
// Without it the two populations are indistinguishable and a chat panel query can surface
// eReferral correspondence (or vice versa) into a surface never designed for it.
Invariant:     communication-has-category
Description:   "SHOULD carry a category identifying which population this Communication belongs to (internal chat vs external eReferral traffic) -- the two are otherwise indistinguishable to a reader"
Severity:      #warning
Expression:    "category.exists()"

// Profile Definition
Profile:        NexusEmrCoreCommunication
Parent:         Communication
Id:             nexus-emr-core-communication
Title:          "Communication Profile"
Description:    "Nexus EMR profile for Communication, covering two distinct populations: WELL-internal staff chat about a patient, and eReferral correspondence passed through from or to external partners. First pass -- the pass-through boundary and message threading are not yet settled."
* ^url = $NexusEmrCoreCommunication // Canonical URL from alias
* ^status = #draft
* insert NoResourceNoteExtension
* ^experimental = true
* obeys communication-has-category

* . ^short      = "Nexus EMR Communication Core"
* . ^definition = """
  A message about a patient. Either WELL-internal staff chat, or eReferral correspondence
  exchanged with an external partner. `category` distinguishes them and readers should scope
  on it -- the two populations have different authorities over their content.
  """

// THE effective date for this profile: when the message was sent, falling back to when it was
// received. A message with neither is undatable and cannot join any date-driven chart surface.
* insert EffectiveDate([[sent | received]])

* insert Salient(status, status, code)
* insert Salient(category, category, coded)
* insert Salient(date, [[sent | received]], dateTime)

// CLOSED TO SMART APPS PENDING AN EXPOSURE DECISION, and it was briefly open for read AND write.
//
// FHIR-28 left this type closed deliberately when SMART exposure was first declared in the IG.
// 1.26.0 opened it on the reasoning that the shipped application reads and writes it -- which is
// true, and is not the same question: the first-party application takes its permissions from role
// claims through its own security context, never from a SMART scope. "The endpoint serves it" and
// "a third-party app may reach it" are two decisions, and this mechanism exists precisely to keep
// them apart.
//
// WHAT CHANGED SINCE FHIR-28 IS THE ARGUMENT FOR CLOSING IT AGAIN RATHER THAN AGAINST. This type
// now carries a patient's CORRECTION REQUEST and their STATEMENT OF DISAGREEMENT -- the statutory
// record of a dispute between an individual and their custodian. A wildcard read reaches both, and
// a wildcard write would let an application post into that space.
//
// The read half is genuinely arguable: a statement of disagreement is required to follow the record
// it disputes when that record is disclosed, so an application assembling a disclosure has a real
// claim to it. The write half is not arguable on the same grounds and was granted with it, because
// the grant is per type and these are one type. That is the decision to take, and it should be
// taken knowing which content is behind it.
//
// What would decide it: an application that names whether it needs the clinical message thread, the
// privacy correspondence, or both -- and a ruling on whether the two can be separated at all, given
// that a SMART scope names a resource TYPE and `Consent` already demonstrates what it costs when
// three families share one.

* meta.source 0..1 MS
* meta.source ^short = "Identifies EMR instance & pipeline version the resource came from"
* meta.source ^definition = "A URI that identifies the EMR pipeline and version from which this resource originated.  This tells you which EMR instance (recommend using the instance identifier), and the version of the pipeline code/transformations."

* text 0..1 MS // Narrative element with human-readable summary
* text ^short = "Narrative"
* text ^definition = "A human-readable narrative summary of the NexusEmrCoreCommunication resource for human interpretation."

// NOT Must Support, and the reason is that nothing mints one. `identifier` here is
// RECOMMENDED but not required, under no mandated system -- and since the instance-scoped
// scheme was retired (FHIR-71) no producer writes a business identifier onto this type at
// all. Must Support asserts that an implementer can be expected to populate an element;
// claiming it for one the specification neither mandates nor produces states a guarantee
// that is not being made. The element stays available and documented -- a writer with a
// source-system identifier should still carry it.
* identifier 0..*
* identifier.system 1..1
* identifier.value 1..1
* identifier ^short = "Business identifier(s) for the communication"
* identifier ^definition = """
Unique identifiers for the communication.

A business identifier is recommended but not required (identifier is 0..*). For eReferral traffic this is where the partner's own message identifier belongs -- it is the only governed way to correlate a reply with what it replies to across the boundary.

⚠ HERITAGE EMR SITUATION -- UNRESOLVED. Heritage EMRs have internal messaging and inbox features, but they are NOT modelled as FHIR Communication: they are local message tables with their own threading, read/unread state, and per-user routing that has no FHIR equivalent. Nothing currently maps them into this resource. Two consequences: (1) a heritage-sourced message has no identifier this IG can name, so any ingest would have to mint one, and (2) migrating that history at all is an open decision -- the alternative is to leave heritage messages in the source system and have this resource cover only natively-authored traffic. Until that is decided, treat every Communication here as either WELL-internal-native or external eReferral, never heritage-migrated.

If no recognized system exists, implementers MAY invent a URI for the system.

A source system's own identifier SHOULD be carried alongside this one, flagged `use = secondary`. See [Carrying the raw code](../guide/patterns/raw-codes.md).
"""

* status 1..1 MS
* status ^short = "preparation | in-progress | not-done | completed | entered-in-error | ..."
* status ^comment = "Internal chat writes `completed` on send. For eReferral traffic the partner's lifecycle applies and `status` is pass-through -- do not infer local workflow state from it."

// -----------------------------------------
// Category -- the population discriminator (see communication-has-category)
// -----------------------------------------
* category 0..* MS
* category ^short = "Which population: internal staff chat, or external eReferral correspondence"
* category ^definition = """
Which population this Communication belongs to. Readers should scope on it -- internal chat and
external eReferral correspondence have different authorities over their content, and a chat surface
that does not scope will surface partner correspondence into a panel never designed for it.

⚠ HERITAGE EMR SITUATION -- UNRESOLVED. If heritage-EMR message history is ever migrated (see
`identifier`), it becomes a THIRD population alongside internal chat and eReferral, and this element
is where that distinction would have to live. No code for it exists, because the migration decision
has not been made. Anyone binding a value set here must resolve that first, or the binding will need
widening the moment migration is agreed.
"""
* category ^comment = "⚠ FIRST PASS -- no value set is bound yet, deliberately. Binding one requires deciding the pass-through boundary first (a partner-supplied category must remain conformant), and that decision is not made. Until then this is text-or-code and readers MUST tolerate text-only. A local NexusEmrCommunicationCategoryVS is the intended follow-up."

* subject 0..1 MS
* subject ^short = "The patient the communication is about"
* subject only Reference(NexusEmrCorePatient)
* subject ^comment = "0..1 per R4. Internal chat about a patient SHOULD carry it -- a chat message with no subject cannot be scoped to a chart, and cannot be filtered out of one either, which is the access-control concern."

* sender 0..1 MS
* sender ^short = "Who sent the message"
* sender only Reference(NexusEmrCorePractitioner or NexusEmrCorePractitionerRole or NexusEmrCorePatient or NexusEmrCoreRelatedPerson or NexusEmrCoreOrganization or NexusEmrCoreAppDevice)
* sender ^comment = "Practitioner is the primary provider identity in this IG. Organization covers a partner-system sender on inbound eReferral traffic; Device covers system-generated messages. RelatedPerson covers a SUBSTITUTE DECISION-MAKER acting for the patient -- a distinct fact from the patient acting themselves, and one that must not be flattened onto Patient: where a right over the record is exercised by proxy, the record has to show that authority was delegated, because that is what is checked when the exercise is challenged."

* recipient 0..* MS
* recipient ^short = "Who the message was addressed to"
* recipient only Reference(NexusEmrCorePractitioner or NexusEmrCorePractitionerRole or NexusEmrCorePatient or NexusEmrCoreRelatedPerson or NexusEmrCoreOrganization or NexusEmrCoreCareTeam)

* sent 0..1 MS
* sent ^short = "When the message was sent"
* received 0..1 MS
* received ^short = "When the message was received"

// -----------------------------------------
// Payload -- the message content
// -----------------------------------------
* payload 0..* MS
* payload ^short = "The message content"
* payload ^comment = "Internal chat writes `contentString`. eReferral traffic may carry `contentAttachment` or `contentReference` -- a reader that assumes contentString will silently render an empty message. Handle all three."

// -----------------------------------------
// Threading -- NOT YET MODELLED, and that is the finding
// -----------------------------------------
* inResponseTo 0..* MS
* inResponseTo ^short = "The message this one replies to"
* inResponseTo only Reference(NexusEmrCoreCommunication)
* inResponseTo ^comment = "⚠ FIRST PASS. The chat panel currently threads by neither `inResponseTo` nor `partOf` -- conversation order is reconstructed from timestamps, which is why concurrent messages can interleave wrongly. Declaring the element here is the first step; deciding whether threads are `inResponseTo` chains or a `partOf` grouping is open."