Skip to content

Clinical Documents

An encounter note, a letter and a machine-generated chart summary are the same FHIR resource with different fields set. There is no note resource, no letter resource and no summary resource: there is one Composition profile, and what separates the three is field values rather than different mechanisms. Everything on this page is a consequence of that.

Generated Summaries and Templates go deeper on two of the three streams. This page is the shape they share.

Three documents, one resource

Encounter note 34109-9 Note Letter 51852-2 Letter Generated summary 34133-9 Summary type Composition type · what CLASS of document category · the refinement subject · the patient author · person, or Device section[] · the body, in order code · what this section clinically is text · the narrative, always extension · render kind, structured value

Same skeleton for all three. Only type separates them, and a consumer that ignores it renders a machine-generated summary as though a clinician wrote it.

Composition.author will not do that job. It answers who wrote it, and never what it is: a scribe-drafted note and a generated summary both carry a Device author. Composition.type answers it, and it is the only element that always can -- it is 1..1, so every document carries one.

A source system may write a refinement code into the class slot

Converted OSCAR data arrives with LOINC 11488-4 "Consult note" on every casemgmt_note, whatever the note actually is. That is a refinement code sitting in the class slot. A converter that knows better writes 34109-9 "Note" on type and carries 11488-4 on category.

Composition, or DocumentReference?

The test is not "is it clinical". Both are. It is: did we author it, and is the content text we control?

A READER Consumer Composition section.text = XHTML the words are in the resource read DocumentReference attachment.url | .data a pointer, or inline bytes A TIFF, a PDF, a fax often opaque, sometimes text second fetch

A note held as text is readable, greppable and diffable in place. A document held as bytes is not readable at all until somebody dereferences it.

Composition DocumentReference
What it is a note we authored a document that arrived as a file
Content XHTML in section.text bytes behind content.attachment.url
Examples progress note, consult note, telephone note, letter, generated summary scan, fax, external PDF letter, consent form
Lifecycle preliminary / final / amended current / superseded
Readable without a second fetch? yes no

A scanned consult letter is a DocumentReference even though a human wrote it, because what we hold is a TIFF. A dictated-then-transcribed progress note is a Composition even though a machine produced the text, because we hold the text.

A deliberate divergence from US Core

US Core models clinical notes as DocumentReference. FHIR's document architecture and its C-CDA lineage are the deeper prior art for authored, sectioned, attested narrative, and DocumentReference.docStatus borrows the CompositionStatus value set, so Composition is that lifecycle's native home. An implementer arriving from US Core should expect to find progress notes here.

The smallest possible note

Six elements are required on every document.

Required
status preliminary / final / amended
type the CLASS of document
subject the patient. Always exactly one
date when the document was last logically changed
author a person, a role, or a Device
title what a chart list shows

A document with no title is unlistable in a UI, which is why title is required rather than nice to have.

Optional, set when it applies
category the refinement under the class
encounter the visit this documents
event.period when the care happened
attester who signed
identifier a business key
section the body, in order

identifier is 0..1. Do not use its presence to tell document kinds apart

The obligation sits on the element rather than on the cardinality. A document synced from another system should carry that system's identifier, because it has to be reconcilable across the boundary. A generated summary must carry its business key, or it duplicates on every run. A document born here with nothing to reconcile against may omit it.

section is optional too, and a document with no readable body is caught by a warning-severity invariant rather than an error -- composition-has-narrative, on the invariants page. A note with no body renders as a blank chart entry rather than as a visible failure, which is the outcome the warning exists to make noisy.

Classify with type, refine with category

The rule is one sentence: classify with Composition.type, refine with Composition.category. They are two levels of "what is this", they are different questions, and they bind different vocabularies.

type is 1..1 and carries exactly one of three LOINC codes. That is the element a consumer switches on, and the element to search on.

Document type -- the class category -- the refinement
Progress note 34109-9 Note 11506-3 Progress note
Consult note 34109-9 Note 11488-4 Consult note
Letter 51852-2 Letter 51852-2 Letter
Day-sheet summary 34133-9 Summary of episode note nexus-daysheet-summary
Interval summary 34133-9 Summary of episode note nexus-interval-summary
Encounter recap 34133-9 Summary of episode note nexus-encounter-recap

The refinement set also names 18842-5 Discharge summary, 34117-2 History and physical and 34748-4 Telephone encounter note, all under the 34109-9 Note class.

A letter carries the same code on both, and that is the ordinary case rather than a special one: where a document has no kind finer than its class, category repeats type. The refinement set is a strict superset of the class set precisely so that repeat validates -- a nesting the build enforces rather than the prose merely claiming it.

Why type is extensible and category is preferred

The asymmetry is the point, and it is the easiest thing on this page to get backwards. The stronger binding sits on the smaller vocabulary.

type is bound extensible to Composition Classification. The class axis is small and the value set enumerates it: note, summary, letter. That is the whole space, so a code outside it is nearly always a refinement written into the class slot -- exactly the mistake worth a warning. Nothing is rejected, so a document that genuinely is none of the three stays conformant.

category is bound preferred to Composition Kind. The refinement axis is LOINC's whole document space and no set here enumerates it. Extensible would warn a producer for holding a more precise code than the guide happened to list -- punishing precision, which is the opposite of what a refinement slot is for. Preferred says prefer these, and be more precise where you can.

Neither binding is silent in the validator

TypedFhir grades required at ERROR and both extensible and preferred at WARNING; example is not checked. The check fires only for a code outside the bound set, so a warning here means "this code is not one the guide listed", never "this instance is wrong".

One value set per element

Three elements ask three different questions about what something is. Nothing in a code says which question it answers, so each element gets its own vocabulary.

ONE QUESTION EACH, ONE SET EACH type what CLASS of document classification · extensible category which KIND under that class kind · preferred section.code what this SECTION clinically is section · extensible type = 34109-9 the same code, in the slot that means document INDISTINGUISHABLE AS DATA 34109-9 · Note a DOCUMENT code · belongs on type 8716-3 · Vital signs a SECTION code · belongs on section.code Same system, same format. Nothing in the code says which. section.code = 34109-9 well formed, and undetectable after the fact

A set bound to one element contains only codes that are legal on that element. One set per element is what turns a rule written in prose into a rule a validator can catch.

type asks what class of document this is, and binds the classification set. category asks which kind under that class, and binds the kind set. section.code asks what one section of the body clinically is, and binds the section set. The kind set contains the three class codes on purpose, so a class code is legal in either document slot. Nothing else is legal on more than one of the three.

The error no reader can catch. LOINC hands out document codes and section codes from one system, in one format, with nothing on the code itself to say which axis it belongs to. 34109-9 "Note" types a document. 8716-3 "Vital signs" types a section. Put the first one in a section slot and it is well formed, it parses, it round-trips, and it is quietly wrong for the life of the record. The only thing that separates the two axes is the element the code sits in, which is why that element carries a binding rather than a sentence of prose.

The split reaches the call signature

The class set draws on one code system, so a bare code is unambiguous and TypedFhir takes one: composition.type = "34133-9". The refinement set unions LOINC with the guide's own nexus-composition-type, so a bare code there could mean either system and the client refuses it -- category takes a full Concept. One element, one vocabulary, all the way down.

The general rule is worth carrying to any element. If a value set is bound to two elements that answer different questions, it will eventually permit the very collapse the prose warns against. Bind one set per element, or accept that the rule is advice.

section.code is bound, and extensible on purpose

New in 1.22.0. section.code binds Composition Section: the C-CDA on FHIR and IPS section vocabulary restricted to what primary care actually produces -- problem list, vitals, allergies, medications, assessment and plan, and ten more.

Preferred would document a vocabulary and warn on nothing, which would not catch the error the binding exists for. Extensible warns on a code outside the set, and a document code in a section slot is exactly that.

The cost is accepted, out loud

A legitimate section kind the set does not yet name also warns. That is the trade: growing the set is one line, and the alternative is catching this class of error by eye forever.

This matters beyond tidiness. C-CDA on FHIR and IPS both make section.code the element a consumer navigates by. A note whose only statement of what a section is lives in one of this guide's extensions cannot be read by anyone who does not implement this guide.

The body is a stack of blocks

The note editor writes an ordered stack of independent pieces. One block is one section, and base R4 already carries three quarters of it.

Composition.section[] · ORDER IS THE SEQUENCE Subjective id=b1 · title · text = «patient reports...» block-type = text Vitals id=b2 · code = 8716-3 · text = «BP 130/85...» block-type + block-data Plan id=b3 · text.status = empty · emptyReason = notstarted raised, left blank, KEPT Assessment id=b4 · code = 51847-2 · text = «...» block-type = text

Nothing records a position, because section is an ordered element in R4. A block raised and left blank is kept, because a gap between two written pieces is part of the shape of what the author wrote.

A block needs and R4 already has
identity section.id
a heading section.title
its narrative section.text
its position the order of section itself
which kind wrote it nowhere -- hence one extension
that kind's own structured value nowhere -- hence the other

Two extensions, and nothing else. That restraint is the design: everything base R4 can carry, base R4 carries.

Blank blocks are kept, and the codes were chosen carefully

An empty-status narrative plus emptyReason notstarted satisfies three base FHIR invariants at once, and notstarted is the honest code -- unavailable and nilknown are claims about the patient, and a blank block asserts neither.

Narrative status is additional, not generated. Non-obvious and correct. generated tells a downstream renderer that the text was derived from structured data and may be thrown away and rebuilt. Here the provider's prose is the content and exists nowhere else in the resource, so a renderer that believed generated would discard it and render nothing.

Three questions per section, three elements

One section a block, a letter body, a claims list What is this section clinically? section.code — a problem list is 11450-4 Who produced it? section.author — a Practitioner, or a Device How do I render it? composition-block-type

They are independent, and collapsing any two of them is the mistake this model most wants a producer to avoid. Two of the three are base R4 and portable; only the render axis is ours.

A problem-list section is 11450-4 whether an editor widget, a letter template or a harness graph produced it. A claims section renders identically however it was assembled. That is the whole reason the three document kinds reduce to one skeleton.

Block type is the render axis, not the authoring tool

composition-block-type says how to render a section, not which widget wrote it. The distinction is load-bearing.

Read as "which widget wrote this", a letter has no kind, because a template filled it, and a generated summary has no kind, because a graph wrote it. Two thirds of the sections in this estate become unrepresentable, and a consumer is pushed to infer rendering from author, which answers a different question.

Read as "how do I render this", an editor widget, a letter template and a harness graph are all producers of section shapes, and a consumer renders what they produce through one dispatch. A cited-claims section is the same render kind whether a graph or a person assembled it.

Three kinds are written today, one per stream committed to writing sections.

Kind Who writes it What the render instruction means
text the note editor a run of narrative the author typed. No structured value beside it
merge-field-body the letter composer this body was filled rather than typed, so a consumer can offer to re-render it against current data, or show which runs came from the record
cited-claims the summary graphs every claim is backed by a citation. Surface the backing; do not render the claims as bare prose

Every kind is equal, and text has no standing

It shipped first, that is all. A consumer that special-cases text and treats the rest as exceptions has the dispatch backwards.

An unknown block kind must survive you

The block-kind binding is extensible as of 1.22.0, on Note Block Types. It was required through 1.21.x, which made the guide a release gate: ship a vitals block, and every note carrying one is nonconformant until the guide cuts a release. Extensible turns a break into a documentation lag.

A binding cannot enforce what matters most here, so it is stated as a consumer obligation instead.

Render an unrecognised kind as text. Do not store text in its place

Render it as text -- you have no widget for it, the narrative is authoritative, and nothing clinical is lost. Then write the code back unchanged. Display and storage are different decisions. Rewriting an unrecognised kind to text on save is data loss, and leaves the record asserting that a specialised block was prose.

There is precedent both ways, and nobody closes this set. SDC's questionnaire-itemControl answers the same question about which control renders an item: extensible, with form fillers explicitly told to ignore controls they do not understand rather than rewrite them. openEHR, the one ecosystem that has solved composition-of-typed-authoring-units at scale, uses open-ended versioned archetype ids.

A section carrying no kind at all predates the extension and reads as text. That is tolerance for data filed before block authoring existed -- the four-section SOAP notes this EMR wrote for years among them -- not a default new data may lean on.

The contract between narrative and structured data

A section carries its content twice, on purpose. This is the most load-bearing rule in the profile and the easiest one to optimise away by accident.

One section section.text · the narrative entry + block-data · structured This EMR’s own UI renders structured, ignores narrative Everyone else another EMR, a partner app, an export, a model NOTHING IS SHOWN TWICE

The rule: a renderer shows the structured data and ignores the narrative. Nothing appears twice.

The consequence, and this is where producers get it wrong: both sides must be complete.

  • Anything the UI needs must be structured. A field that exists only in prose is invisible to the surface that renders it.
  • Anything clinically meaningful must also be in the narrative. Including negatives: "No active problems" is a finding, not filler, and a narrative that omits it reads as a section nobody filled in. Booleans and scalars count too -- a value that lives only inside a structured payload leaves every consumer who is not this UI reading an incomplete document.

Only genuinely absent facts are left out of both. Absent means the fact does not exist, not that it was inconvenient to render.

The two halves are easy to hear as a contradiction, so say them together

"The narrative is authoritative" and "the renderer shows the structured data" are one contract, not two rules competing. The narrative is authoritative for everyone who is not this EMR's own UI; the structured data is the display path for that UI only. Neither sentence is safe on its own.

The invariant to build on: a consumer that ignores every extension in this guide must still get a correct, readable document. A section that breaks it -- clinical content in the structured value and nothing in section.text -- is malformed however well it round-trips.

What a block's structured value rides in

The value rides in an Attachment on composition-block-data, not in a JSON string. Three candidate shapes were weighed, in this order of preference.

Shape Verdict
1 SDC -- a Questionnaire plus a QuestionnaireResponse Reach for this first. Where a render kind is really a form -- vitals, a diabetes review, a flowsheet -- this is literally the problem SDC exists to solve, and it gets you section.entry promotion for free
2 valueAttachment, contentType: application/json What shipped. Self-describing envelope; what R4 already provides for an opaque payload
3 a complex extension with typed sub-extensions per kind Viable only if the shapes turn out small and stable. Not taken
x JSON in a valueString Rejected. Self-describing to nobody: a reader cannot tell what it holds without recognising the render kind first, and no published international IG carries editor state that way

Unreadable is not absent

A value that cannot be parsed reads as no structured value rather than as an error -- one malformed section must not put a whole clinical document out of reach. But a writer that cannot parse a stored value must round-trip it untouched rather than drop it. Conflating unreadable with absent on a write path deletes the stored value.

Block data, or section.entry?

A vitals block one section block-data editor state rewritten on every keystroke section.entry promoted Observations what a chart query actually wants Different questions. Expected to coexist on one section. Open: whether the left box should exist at all.

Minting real resources per keystroke would put editor state into the clinical record and make section.entry unqueryable for what it is for. That is the argument for keeping them apart today, and the two are expected to coexist on one section.

Whether the structured value belongs in an extension at all is genuinely undecided. What would settle it is the first render kind that promotes its own facts to section.entry on save; a vitals block is the obvious candidate. No consumer action follows from it being open: a writer that uses the extension today is correct either way, because a later move to entry would add a place to read the facts rather than change where the editor state lives.

Letters

A letter that has been sent is an ordinary Composition: class 51852-2 on type, a body in section.text, signed by a clinician like any other authored document. Letters are not exempt from the signature rule -- a letter goes out over somebody's name.

A letter body is merge-field-body: the render kind says the prose was filled rather than typed, which is what lets a consumer offer to re-render it against current data or show which runs came from the record.

A template is a different resource entirely. It is a List holding prototype resources in a contained transaction Bundle. Instantiating it clones the prototypes, fills in what the template could not know, rewrites the references between them, and saves the result as ordinary resources. Three reasons that shape was chosen:

  • A List and not a Bundle. Bundle derives directly from Resource rather than DomainResource, so it has no extensions, no narrative and no contained resources. A template needs a name, a lifecycle status and a tenancy stamp; List carries all three natively.
  • Not a marked-up Composition. Marking a resource type "this one is a template" answers one case per resource type. A template that is a list of prototypes answers all of them: a letter template that grows a follow-up Task gains an entry and changes no definitions.
  • The clinical profile was not relaxed. A prototype has no patient. Base R4 already permits Composition.subject to be absent, so the profile is not weakened to 0..1, and no consumer of real letters has to carry a rule about templates.

Do not write sentinel values into a prototype

Where base R4 requires a value -- status, type, date, author, title -- supply the template's own real one and overwrite it at use time. A placeholder like 1900-01-01 validates cleanly and saves cleanly, which is exactly why it is dangerous: a letter dated 1900 is a defect no validator will report.

Migrating a letter classified with a local code

Letters classified with http://services.well.com/fhir/categories/letter-template migrate to LOINC 51852-2 on type. A nexus-letter-template code exists on this guide's own base if a template kind still needs naming -- it is a refinement, so it sits on category under the letter class rather than in the class slot -- but templates are a List, so check whether the code is needed at all before reaching for it.

At most one current List may carry a given template identifier

More than one is a corrupt state in which a consumer must raise an error rather than choose between them. Not newest-by-date, not first-result, not both shown to the user -- every such heuristic makes the defect permanent and invisible while the picker keeps working.

Enclosures: how a letter names what travels with it

A letter can go out with chart items attached -- recent bloodwork, the report that prompted a referral, an earlier letter from a colleague. The letter names them in one coded section, and that section is the only record there is.

The section is the enclosures slice on nexus-emr-core-composition: 0..1, coded LOINC 28562-7, with one entry per attachment packet. A packet is a List holding the items in order, each able to say which of its pages were left out. The letter points at the packet; the packet does not point back.

Composition (letter)
├── section[0]              the body
└── section[enclosures]     code = LOINC 28562-7, title = "Enclosures"
    └── entry ──────────▶  List (Attachment Packet)
                            ├── entry ──▶ DocumentReference   consult summary
                            ├── entry ──▶ DiagnosticReport    CBC, pages 1 and 4 excluded
                            └── entry ──▶ DocumentReference   filed form

Four rules follow, and each has a way of being got wrong:

  • Resolve the section by its coding, not by its position. The body is the section at a known index; the enclosures section follows it and a producer that adds sections of its own may put anything between them. Match system and code together: 28562-7 under another producer's system is that producer's section.
  • Enclosures is the section.title, not the coding's display. LOINC 2.83 names 28562-7 "Chart section Set". Writing "Enclosures" into display claims a term the concept does not have; it is a heading, and headings live on title.
  • 28562-7 is a section code. 11503-0 is not. 11503-0 is the document code for the rendered packet PDF and belongs on DocumentReference.type. A document code in a section slot is the mistake the section value set's extensible binding exists to catch.
  • Membership lives on the packet. The section names packets, never chart items directly. Which items a packet holds, in what order, and which of their pages are excluded are all on the List -- which is why the section carries one entry per packet rather than one per document.

A section coded 28562-7 with no entry means nothing travels with the letter; a block raised and left blank is an ordinary empty section and carries section.emptyReason. The slice does not require an entry for exactly that reason.

What the packet records, and what it does not

A packet carries two assembly facts beside its membership: whether the render leads with a generated cover page (nexus-packet-cover-page -- absence there is not false), and which way out of the EMR it was built for (a meta.tag coding under nexus-send-channel). Both say what was chosen while the packet was assembled. Neither says anything went out. The record of a send is the rendered document stored at that moment, and this guide does not model that workflow yet.

Generated summaries

A generated summary is a Composition the AI harness writes and maintains. Three kinds, one class, and the one document nobody signs. Generated Summaries is the full treatment; what matters here is how it sits in the shared shape.

Graph runs Monday Graph runs Tuesday Graph runs a year later find by business key ONE resource content replaced in place superseded text lives in version history Provenance one attributed fact per claim, with the verbatim quote behind it

The business key carries no period, so regeneration converges on one resource instead of leaving the chart accumulating stale summaries nobody deletes. The exception is a summary whose period is part of what it is -- a day sheet for one date is not an update of yesterday's.

All three kinds are 34133-9 as a class, and no LOINC code distinguishes them, which is what the three local codes on category are for. They are not interchangeable. An encounter recap asserts that the encounter it covers took place. A day-sheet summary is written ahead of a clinic for a booked patient. An interval summary covers a span of time and may have no upcoming visit at all.

Nobody signs a summary. The profile requires a final document to name a legal or professional attester. Generated summaries are exempt, deliberately: final on a summary means this generation is complete, not a human has taken responsibility for the contents. Both alternatives were worse -- leaving them permanently preliminary would be false, and naming an attester who never attested would fabricate a medico-legal signature.

Citations are a separate grain from inputs. Provenance.entity records which resources the activity read; nexus-attributed-fact records which statement rests on which passage, with the verbatim quote. Each fact carries at least one citation by construction, and an invariant rejects an empty quote -- a citation that cites nothing satisfies the cardinality and leaves a fact nobody can audit.

An absent AIAST label means unknown, not human

Nothing backfills the AI attribution label. Read it as evidence a machine was involved, never as evidence one was not. And preserve it when writing a labelled resource onward -- dropping it turns machine-produced content into apparently human-authored content.

Billing, sharing and editor setup

Three extensions ride the document.

composition-billing carries what the clinician entered at note closeout: the service (fee) code, the diagnostic codes supporting it, and units where the fee code is charged per unit. dx repeats -- a claim routinely carries more than one, and a reader taking only the first has a different claim than the clinician entered. Absent units means the code is not charged per unit, not that one unit was billed.

composition-ehr-share carries one record per completed share of a signed note to an EHR outside the practice. It repeats: a note shared three times carries three. Completed only -- this is read back to patients as a disclosure record, and listing a pending share would tell a patient their record went somewhere it has not yet gone.

composition-note-setup carries the editor setup a note was opened on: the visit type the provider chose, and the template the note started from, so a draft reopens on the footing it was left on. Both sub-extensions or neither -- a template without its visit type cannot be offered again. It records what the note was opened from, never what it now contains: a provider may switch templates, delete every heading the template gave, or type their own, and the authoritative structure is always section.

Why billing rides the note rather than a Claim. R4 gives Composition no element for billing, and a separate Claim has its own lifecycle: submitted, adjudicated, reassessed, written off, all without the note changing. Carrying the billing on the note keeps what the clinician entered at closeout attached to what they wrote. A Claim raised from it answers a different question and is expected to exist alongside.

Why the share destination is a string. It is routinely an external system with no Organization in this practice's directory, and a reference that cannot resolve is worse than a name that can be read.

Why the setup template codes are not nexus-template. CodeSystem/nexus-template is the coding view of the stored template registry -- per-tenant slugs of template List resources, which a letter carries on meta.tag to say which stored template it was composed from. The setup codes name the fixed catalogue of note structures compiled into the application. Both are kebab-case slugs over different value spaces, and they are not interchangeable. Both setup bindings are extensible, for the same reason the block-kind binding is: a required binding would make this guide a release gate on the note editor.

Signing, amending, dating

The lifecycle is status. There is no extension for it and none should be created.

Note state status
draft, being typed preliminary
signed final
signed, then edited amended
voided / created in error entered-in-error

Once status is final or amended, the document must carry an attester with mode legal or professional. A signed note with nobody identified as having signed it is not a record anyone can stand behind. Generated summaries are the one exemption; letters are not exempt.

Amendments are version history, not a second resource

Update the same resource in place and set status to amended. The business identifier does not change. Do not create a second Composition and relate it back: that leaves two resources where the chart must know to display one, and every consumer has to learn to filter. relatesTo is deliberately left unprofiled so it stays available if a true addendum workflow appears -- original untouched, new signed content appended with its own author. That is a different clinical act.

Three dates answer three different questions, and the one most readers want is not the one they find first.

event.period.start The clinical date. When the documented care actually happened. It does not move. Sort a chart timeline by this
date the composition editing time, and it moves when a note is amended. It is only the fallback arm of the effective date, reached when event is absent -- so a note that leaves event empty re-positions on a timeline to the date of its own amendment
attester.time when the signature happened. A third question again

Populate event whenever the note documents care that happened at an identifiable time, which for a progress note, a consult note or a discharge summary is always. If you need a stable "when was this written", none of the three answers it: use the encounter's period, or the date of the earliest version in history.

Reconstructing the as-signed note is reliable only on the authoring server

Version history is server-local: _history does not travel in bundles, $everything, or sync pipelines, all of which move current versions only. Across a system boundary an amended note arrives as its amended text, with the as-signed content absent. meta.versionId is server-opaque in R4 -- consumers must not infer ordering, authorship or "which version was signed" by parsing or sorting it. A published versioning policy is what would make the amendment model safe to rely on across a boundary; this guide does not declare one yet.

AI-drafted notes

A Device may appear in author alongside the human. The attester is always the clinician, never the Device: generation and responsibility are different acts, which is why they are different elements. And absence of a Device author means origin unknown, not "typed" -- notes migrated from OSCAR Pro carry no origin information at all.

Where the canonicals live

services.well.com fhir.apps.health composition-block-type · composition-block-data moved before publication — no record carried the old address composition-billing · composition-ehr-share · UpdatedBy live clinical data behind them declared here · records filed earlier carry the legacy address

Published canonical URLs are forever, which is why the free moment to move one is the moment before the first write.

composition-block-type and composition-block-data were first minted under services.well.com and moved to this guide's own base before publication. Those canonicals are settled and will not be renamed.

composition-billing, composition-ehr-share, composition-note-setup and UpdatedBy have live clinical data behind them, so the address cannot be changed by editing a constant.

Writers: write the canonicals this guide declares. Readers: expect both

These four are declared at https://fhir.apps.health/..., and stored records also carry them at the legacy http://services.well.com/fhir/StructureDefinition/.... Match on the URL suffix -- resolving only the declared canonical silently misses billing, sharing and the editor setup on every note filed under the legacy address. UpdatedBy is the long tail: it rides meta on every version of every resource ever saved, so history keeps the legacy address indefinitely even once new versions stop carrying it.

Which further artifacts under that host the guide adopts, which it redeclares at its own base, and what a migration costs where records already carry the old address, is not settled by declaring these six. It is stated here so that a reader who finds a services.well.com URL on the wire knows the guide is aware of it rather than silent on it.

The whole model on one page

The document
type the CLASS: note, summary, letter
category the refinement under it
subject the patient. Exactly one
status preliminary / final / amended
attester who signed. Absent on summaries
author a person, or a Device
event.period.start the clinical date
date the editing time. It moves
identifier a business key, when there is one
The section
id the block's identity
title its heading
text its narrative. Always populated
code what it clinically IS
author who produced it
entry structured clinical facts
emptyReason why a raised block is blank
order is the sequence. Nothing records it
Extensions this guide adds
composition-block-type how to render a section
composition-block-data that render kind's own value
composition-billing closeout billing
composition-ehr-share one completed share
composition-note-setup the visit type and template a draft was opened on
meta.security AIAST, if a machine wrote it
meta UpdatedBy who last saved a version

The four invariants

  1. A consumer that ignores every extension still reads a correct document. Every section carries its narrative whatever its kind. Not machine-checkable.
  2. Both carriers are complete. Anything the UI needs is structured; anything clinically meaningful is also in the narrative, negatives included. Not machine-checkable.
  3. A signed document names its signer. Unless it is a generated summary, which nobody signs. Enforced.
  4. A section code is a section code, never a document code. Enforced, as a warning.

Two of the four are checkable by a machine and two are not. That is worth knowing when deciding what to test.

The narrative-in-FHIR model is what makes a note greppable, diffable and readable by a model without a second fetch. The moment clinical content moves into a payload only one UI can parse, that stops being true -- quietly, and for everyone downstream at once.

What is not settled

Most of the model above is stable. These are the parts that are not, with what would decide each.

  • Whether a block's structured value belongs in an extension at all. The alternative is section.entry, which the profile already nominates as a section's structured counterpart. They hold different things today: editor state versus separately-addressable clinical facts. Decided by: the first render kind that promotes its own facts to entry on save.
  • Which mechanism carries AI authorship detail. AIAST settles whether a machine was involved. Which agent, from what inputs, under whose supervision, is carried either by a Device in author or by a Provenance record. Neither is foreclosed. Decided by: what consuming code needs to read.
  • How a Device author is referenced. Whether AI agents are registered as Device resources in the store, whether one static Device denotes "a Nexus AI agent", or whether the reference is by identifier into the harness's own graph with no resource on the server. The profile deliberately does not constrain it.
  • Assessment: the note section, or the ClinicalImpression? A note carrying an assessment section and a ClinicalImpression has two homes for the same text. The guide does not yet rule which is authoritative.
  • Whether type should be tightened. The class binding is extensible today, so a document classified with an off-vocabulary code is still conformant. Decided by: the letters migration -- while an off-vocabulary code is still in stored data, tightening would make existing records invalid.

Composition publishes no search parameters

The generated-summary design needs three -- subject, type and above all identifier. Without identifier, a producer cannot find the summary it wrote last time and update-in-place silently becomes duplicate-on-every-run. The CapabilityStatement lists the search interaction, which is easy to mistake for a guarantee about parameters. Measuring the three against the store and declaring what works is owed IG work.

What is deliberately not open. One resource for three document kinds. type as the classifier. Narrative in FHIR rather than behind a blob pointer. Amendment as version history. One section per block, ordered, not nested. Those shipped, they are what the examples demonstrate, and reopening them costs more than it buys.