Skip to content

MessageHeader Profile

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

Nexus EMR profile for MessageHeader -- the routing envelope on an eReferral message bundle, consumed by the gateway's operation-routing middleware. Infrastructure rather than clinical content: no effective date, and R4 gives it no identifier element. First pass: the event vocabulary is not settled.

FSH

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

// NexusEmrCoreMessageHeader.fsh
// FIRST PASS. The routing envelope on an eReferral message bundle.
//
// Why this profile exists. The gateway's operation-routing middleware reads MessageHeader to
// dispatch inbound eReferral traffic, so it is a live consumption surface with no IG statement
// behind it. This is INFRASTRUCTURE, not clinical content: a MessageHeader carries no patient
// data of its own, and its whole job is to say what kind of message this is and who sent it so
// the receiver can route it.
//
// That distinction drives two calls in this file that differ from every clinical profile here:
//   • NoEffectiveDate -- a routing envelope has no clinical date. See below.
//   • No identifier block -- R4 MessageHeader has NO `identifier` element at all (the message's
//     own id is `Bundle.identifier` on the enclosing message bundle), so the absence of an
//     identifier block here is deliberate rather than an omission.
//
// FIRST-PASS SCOPE. Skeleton and the routing contract the middleware depends on. Deliberately
// NOT settled: the event vocabulary (each partner uses its own event codes and no local set is
// agreed), and whether outbound messages are constructed by this stack at all or only received.

Alias: $NexusEmrCoreMessageHeader = ../StructureDefinition/nexus-emr-core-messageheader

// The routing precondition. Without an event the receiver cannot dispatch, and without a source
// it cannot reply or attribute -- the two elements the middleware actually needs. R4 already
// requires both; this states them as the routing contract so a reader knows they are relied on.
Invariant:     messageheader-routable
Description:   "A MessageHeader must carry an event and a source -- the receiver cannot dispatch without the event, and cannot reply or attribute without the source"
Severity:      #error
Expression:    "event.exists() and source.exists()"

// Profile Definition
Profile:        NexusEmrCoreMessageHeader
Parent:         MessageHeader
Id:             nexus-emr-core-messageheader
Title:          "MessageHeader Profile"
Description:    "Nexus EMR profile for MessageHeader -- the routing envelope on an eReferral message bundle, consumed by the gateway's operation-routing middleware. Infrastructure rather than clinical content: no effective date, and R4 gives it no identifier element. First pass: the event vocabulary is not settled."
* ^url = $NexusEmrCoreMessageHeader // Canonical URL from alias
* ^status = #draft
* ^experimental = true
* obeys messageheader-routable

* . ^short      = "Nexus EMR MessageHeader Core"
* . ^definition = """
  The header of a FHIR message bundle: what event it represents, who sent it, who it is for, and
  what it is about. Used to route inbound eReferral traffic.

  Carries no clinical content of its own -- the content is in the bundle's other entries, pointed
  at by `focus`.
  """

// THE effective date for this profile (see the EffectiveDate / NoEffectiveDate RuleSets): NONE,
// deliberately. A routing envelope has no clinical date. The message's send time is transport
// metadata (`Bundle.timestamp` on the enclosing bundle), not a fact about a patient, and letting
// a consumer date a chart surface by it would put transport timing into clinical recency windows.
// Declaring `none` rather than nothing is the point: an undeclared profile falls back to a
// generic guess and invents a date.
* insert NoEffectiveDate

// SMART on FHIR exposure -- READ-ONLY. A MessageHeader is a routing envelope that is read off a
// message bundle to dispatch it; nothing in this estate files one as an independently addressable
// resource, so no create, update or delete is offered and no scope grants one. Expect to meet one
// inside a bundle rather than to retrieve it by id.
* insert SmartExposedReadOnly(messageheader)

* 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 NexusEmrCoreMessageHeader resource for human interpretation."

// NOTE: no identifier block. R4 MessageHeader has no `identifier` element -- the message
// identifier lives on the enclosing Bundle. Correlate messages by `Bundle.identifier` and
// `response.identifier`, not by anything on this resource.

// -----------------------------------------
// Event -- what kind of message this is (the routing key)
// -----------------------------------------
* event[x] 1..1 MS
* event[x] ^short = "The event this message represents (the routing key)"
* event[x] ^comment = """
⚠ FIRST PASS -- no binding. R4 offers a choice of Coding or uri, and each eReferral partner brings
its own event vocabulary; binding a local set now would make conformant partner traffic
non-conformant. Consumers MUST handle both the Coding and the uri form -- reading only `eventCoding`
silently fails to route a partner using `eventUri`.

**What this estate itself sends is a `Coding` under eHealth Ontario's event system**, declared at
../fhir/NamingSystem-OnMessageEventCodeNS.md so that meeting it is recognition
rather than a lookup that fails. That declaration deliberately does NOT bind this element: it says
what our own outbound traffic carries, and a partner remains free to send a code, or a system, that
this specification has never named.
"""

// -----------------------------------------
// Source / destination -- who sent it, who it is for
// -----------------------------------------
* source 1..1 MS
* source ^short = "The system that sent the message"
* source.endpoint 1..1 MS
* source.endpoint ^short = "The sender's endpoint (where replies go)"
* source.name 0..1 MS
* source.name ^short = "Human-readable name of the sending system"

* destination 0..* MS
* destination ^short = "The intended receiver(s)"
* destination.endpoint 1..1 MS
* destination.endpoint ^short = "The receiver's endpoint"

* sender 0..1 MS
* sender ^short = "The organization or person responsible for the message content"
* sender only Reference(NexusEmrCorePractitioner or NexusEmrCorePractitionerRole or NexusEmrCoreOrganization)
* sender ^comment = "Distinct from `source`, which is the technical sending system. `sender` is who is accountable for the content -- for inbound partner traffic this is usually an Organization."

* responsible 0..1 MS
* responsible ^short = "The party accountable for the message"
* responsible only Reference(NexusEmrCorePractitioner or NexusEmrCorePractitionerRole or NexusEmrCoreOrganization)

// -----------------------------------------
// Focus -- what the message is about
// -----------------------------------------
* focus 0..* MS
* focus ^short = "The resource(s) this message concerns"
* focus ^comment = "Points at the other entries in the message bundle -- typically a ServiceRequest or Task for an eReferral, plus the Patient. These are intra-bundle references and resolve within the bundle, NOT against the server, which is the usual reason a naive resolver reports them missing."

// -----------------------------------------
// Response -- present only when this message answers another
// -----------------------------------------
* response 0..1 MS
* response ^short = "Present when this message is a response to an earlier one"
* response.identifier 1..1 MS
* response.identifier ^short = "The id of the message being answered (R4 type `id`, NOT an Identifier)"
* response.identifier ^comment = "⚠ Type trap. Despite the element name, this is R4 primitive **`id`**, not an `Identifier` datatype -- so it is a bare string constrained to `[A-Za-z0-9\\-\\.]{1,64}`. A `urn:uuid:...` value is **invalid** (colons are not permitted), and a system+value object will not parse. It holds the plain id of the message this one responds to. Cross-boundary correlation with a partner's own message identifier therefore cannot ride here; use `Bundle.identifier` on the enclosing message bundle for that."
* response.code 1..1 MS
* response.code ^short = "ok | transient-error | fatal-error"
* response.code ^comment = "A partner reporting `fatal-error` has REJECTED the referral. A consumer that ignores this element will show a submitted referral as in-flight indefinitely."