Skip to content

Subscription Profile

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

Nexus EMR profile for Subscription (R4 criteria-based) -- server-side push notification registration managed by the subscription manager service. Infrastructure and a data-egress security surface: no effective date, and R4 gives it no identifier element. First pass; note the R5 redesign migration path.

FSH

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

// NexusEmrCoreSubscription.fsh
// FIRST PASS. Server-side push notification registration.
//
// Why this profile exists. The subscription manager service creates and manages Subscription
// resources, so it is a live write surface with no IG statement behind it. Like MessageHeader
// this is INFRASTRUCTURE -- it carries no patient data and describes no clinical fact. Unlike
// MessageHeader it is also a SECURITY surface, and that is what makes it worth profiling even though
// it carries nothing clinical: a Subscription is a standing instruction to send record content to an
// endpoint, so its criteria and channel together define an ongoing data egress path.
//
// Two calls that differ from the clinical profiles here, for the same reasons as MessageHeader:
//   • NoEffectiveDate -- a subscription has no clinical date. `end` is an expiry, not an
//     effective date.
//   • No identifier block -- R4 Subscription has NO `identifier` element (R5 adds one), so its
//     absence here is deliberate.
//
// ⚠ R4 vs R5 WARNING. Subscription was redesigned in R5 (topic-based, with `SubscriptionTopic`
// replacing the R4 `criteria` search string). This profile is R4 and describes the R4 shape.
// Anything built on `criteria` here is on a migration path, and that is a known cost -- do not
// design new long-lived integrations against it without reading the R5 backport guidance first.
//
// FIRST-PASS SCOPE. Skeleton, and the criteria/channel contract with its security notes.
// Deliberately NOT settled: which resource types may be subscribed to (this should be a closed
// list and currently is not), whether payload content is permitted at all versus id-only
// notifications, and the R5 migration.

Alias: $NexusEmrCoreSubscription = ../StructureDefinition/nexus-emr-core-subscription

// A subscription with no channel endpoint is inert; one with no criteria matches nothing
// meaningful. R4 requires both, and this states them as the contract because a half-configured
// subscription fails silently rather than erroring -- it simply never fires, which is
// indistinguishable from "nothing happened".
Invariant:     subscription-deliverable
Description:   "A Subscription must carry criteria and a channel type -- a subscription missing either never fires, and silent non-delivery is indistinguishable from no matching events"
Severity:      #error
Expression:    "criteria.exists() and channel.type.exists()"

// Profile Definition
Profile:        NexusEmrCoreSubscription
Parent:         Subscription
Id:             nexus-emr-core-subscription
Title:          "Subscription Profile"
Description:    "Nexus EMR profile for Subscription (R4 criteria-based) -- server-side push notification registration managed by the subscription manager service. Infrastructure and a data-egress security surface: no effective date, and R4 gives it no identifier element. First pass; note the R5 redesign migration path."
* ^url = $NexusEmrCoreSubscription // Canonical URL from alias
* ^status = #draft
* ^experimental = true
* obeys subscription-deliverable

* . ^short      = "Nexus EMR Subscription Core (R4 criteria-based)"
* . ^definition = """
  A standing request to be notified when resources matching a search expression change, and the
  channel those notifications are delivered on.

  Carries no clinical content. Defines an ongoing egress path for record content, so its criteria
  and channel are a security concern as much as a routing one.
  """

// THE effective date for this profile (see the EffectiveDate / NoEffectiveDate RuleSets): NONE,
// deliberately. A subscription describes no clinical event. `end` is an expiry timestamp, not an
// effective date, and dating a chart surface by it would be meaningless. Declaring `none` rather
// than nothing prevents a consumer inventing a date from `end`.
* insert NoEffectiveDate

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

// NOTE: no identifier block. R4 Subscription has no `identifier` element (added in R5).
// Correlate subscriptions by resource id, or by a tag, until the R5 migration.

* status 1..1 MS
* status ^short = "requested | active | error | off"
* status ^comment = "`error` and `off` both mean notifications are NOT being delivered, and the server sets them itself (e.g. after repeated delivery failure). A manager service that created a subscription and assumes it is still live will miss events silently -- this element must be polled or watched, not set-and-forgotten."

* end 0..1 MS
* end ^short = "When the subscription expires"
* end ^comment = "An expiry, NOT an effective date -- see the NoEffectiveDate declaration above. SHOULD be set: an open-ended subscription is a permanent egress path that outlives the reason it was created."

* reason 1..1 MS
* reason ^short = "Why this subscription exists"
* reason ^comment = "Free text, required by R4, and worth taking seriously rather than filling with a placeholder: it is the only human-readable record of why an egress path was opened, and the first thing an audit asks."

// -----------------------------------------
// Criteria -- what is being watched (a security boundary)
// -----------------------------------------
* criteria 1..1 MS
* criteria ^short = "The search expression selecting resources to notify on"
* criteria ^definition = """
An R4 search expression (e.g. `Observation?patient=123`) selecting the resources whose changes
trigger a notification.

⚠ This is a security boundary, not just a filter. A broad criteria string (a resource type with no
patient or category scoping) subscribes to the whole population of that type.
"""
* criteria ^comment = "⚠ NOT ENFORCED HERE, and not declared by this guide either. `criteria` remains `1..1` with no type or parameter invariant, so a criteria posted directly to a server is checked by whatever that server chooses to check. A criteria naming a parameter the server does not support may match nothing and fire never -- silently, and indistinguishably from a period in which nothing happened. That is the failure mode to design against when accepting a subscription from an untrusted writer: validate the string against the search surface the receiving server actually declares, rather than assuming it will be rejected. This API does not offer the Subscription type to applications; see the Subscriptions page in the guide for the criteria vocabulary a future surface would use and the limits that would apply to it."

// -----------------------------------------
// Channel -- where notifications go
// -----------------------------------------
* channel 1..1 MS
* channel ^short = "How notifications are delivered"
* channel.type 1..1 MS
* channel.type ^short = "rest-hook | websocket | email | sms | message"
* channel.endpoint 0..1 MS
* channel.endpoint ^short = "Where notifications are sent"
* channel.endpoint ^comment = "⚠ Required in practice for every channel type except websocket. This is the egress destination -- review it, not just the criteria, when auditing what leaves the system."
* channel.payload 0..1 MS
* channel.payload ^short = "MIME type of the notification body; absent means id-only notification"
* channel.payload ^comment = "⚠ Load-bearing security semantics that read as a formatting detail. When ABSENT, the notification carries no resource content and the receiver must fetch (and be authorized to fetch) the resource itself. When PRESENT, record content is pushed to `channel.endpoint` directly. Prefer absent -- id-only notification keeps authorization at the read, where it can be enforced."
* channel.header 0..* MS
* channel.header ^short = "Headers sent with the notification (e.g. authorization)"
* channel.header ^comment = "⚠ Secrets belong nowhere near a queryable resource. If a bearer token is placed here it is readable by anyone who can read the Subscription; scope read access accordingly, or use a channel that does not require an inline credential."