A FHIR DocumentReference is the R4 resource an EHR uses to index a clinical note: it holds the metadata (note type, status, author, encounter, dates) and points to the note body, either as inline base64 in content.attachment.data or as a link to a Binary resource in content.attachment.url. To read a note, you search DocumentReference for the patient with category=clinical-note, pick one attachment, and fetch the Binary with an Accept header that matches its contentType.
That is the spec answer. Production has more edges. Epic returns each note as HTML and RTF, Oracle Health adds a proprietary XML rendition, athenahealth's profile removes relatesTo, and write-back rules differ on exactly the points an AI scribe cares about. This guide is for teams pulling notes into scribes, RAG pipelines, chart review and prior authorization packaging, and writing generated notes back. Vendor behavior was checked against published specs, live CapabilityStatements and Oracle's public sandbox in September 2026.
Key takeaways
- DocumentReference is an index, not the note. The text lives in an attachment: inline
dataor aurlto Binary. US Core requires at least one of the two. - Always filter by category or type. DocumentReference also carries correspondence, generated C-CDAs and scans. Epic needs one of them to restrict results to clinical notes.
- One note, several renditions. Epic returns
text/htmlandtext/rtffor the same note; Oracle returns PDF or HTML plusapplication/xml. Pick one per note or you will embed everything twice. - The Accept header decides what Binary returns. Match the attachment's contentType for raw bytes, or send
application/fhir+jsonfor a JSON wrapper. Oracle answers a mismatched Accept with HTTP 406. - Write-back is vendor-specific. Epic's create takes plain text only and needs an encounter. Oracle's takes base64 PDF, RTF, HTML, text or XML. athenahealth's R4 CapabilityStatement does not list create. HL7 v2 MDM is the fallback.
What is a FHIR DocumentReference?
DocumentReference is the FHIR R4 resource that indexes a document so a system can find it: a progress note, a discharge summary, a scanned PDF or a C-CDA. It describes the document (type, category, status, author, encounter, dates) and carries or links to the content. The note body itself sits in content.attachment, usually pointing at a Binary resource.
The R4 DocumentReference specification makes almost everything optional; the US Core DocumentReference profile tightens it for US notes (see our guide to profiles and Must Support).
| Element | R4 base | US Core (9.0.0) | What it means for notes |
|---|---|---|---|
status | 1..1 | Required | State of the reference: current, superseded or entered-in-error. |
docStatus | 0..1 | Optional | State of the note itself: preliminary, final, amended, entered-in-error. This is where signed vs draft shows up. |
type | 0..1 | Required, required binding | The note type, LOINC where possible (for example 11506-3 Progress note). |
category | 0..* | Required | US Core's category value set holds one concept today: clinical-note. |
subject | 0..1 | Required | Reference to the Patient. |
date | 0..1 instant | Must Support | When the reference was created. Epic warns this may not be clinically relevant. |
author | 0..* | Must Support | Practitioner, organization, device or patient who wrote it. |
context.encounter, context.period | 0..*, 0..1 | Must Support | The visit the note belongs to, and the time of service. |
relatesTo | 0..* | Not Must Support | replaces, transforms, signs or appends links to another DocumentReference. |
content.attachment.contentType | 0..1 | Required | MIME type of this rendition: text/html, text/rtf, application/pdf and so on. |
content.attachment.data / .url | 0..1 each | Must Support, one of them required (us-core-6) | Inline base64 content, or a link (usually to Binary). |
content.format | 0..1 | Must Support | Format code. Epic and Oracle both send urn:ihe:iti:xds:2017:mimeTypeSufficient. |
The FHIR Binary resource is the other half: a required contentType and base64 data. It has no search in the base spec and is served in native form unless you ask for the FHIR representation.
Which clinical note types does US Core require?
US Core 8.0.0 and 9.0.0 require servers to support ten "Common Clinical Notes" identified by LOINC. US Core 6.1.0 and 7.0.0 required five. That gap matters, because the certified APIs at Epic, Oracle Health and athenahealth all declare the US Core DocumentReference profile at 6.1.0 in their CapabilityStatements today.
The ONC (g)(10) standardized API criterion references US Core 6.1.0 and USCDI v3, with US Core 7.0.0 and 8.0.1 available through SVAP, per the healthit.gov test method page. The five-note set is the floor; the extra five are a per-vendor question. The table combines the US Core Clinical Notes guidance with the LOINC lists in Epic's Clinical Notes search and create specs.
| LOINC | Note type | US Core 6.1.0 / 7.0.0 | US Core 8.0.0 / 9.0.0 | Epic search list | Epic create list |
|---|---|---|---|---|---|
| 11488-4 | Consultation Note | Yes | Yes | Yes | Yes |
| 18842-5 | Discharge Summary | Yes | Yes | Yes | Yes |
| 34117-2 | History and Physical Note | Yes | Yes | Yes | Yes |
| 28570-0 | Procedures Note | Yes | Yes | Yes | Yes |
| 11506-3 | Progress Note | Yes | Yes | Yes | Yes |
| 34111-5 | Emergency Department Note | No | Yes | Yes | Yes |
| 11504-8 | Surgical Operation Note | No | Yes | Yes | No |
| 18748-4 | Imaging Narrative | No | Yes | No | No |
| 11502-2 | Laboratory Report Narrative | No | Yes | No | No |
| 11526-1 | Pathology Report Narrative | No | Yes | No | No |
Epic's lists also include Nurse Note (34746-8), and sites can map more LOINC codes or use local note types, so treat any fixed list as a starting point.
DocumentReference vs DiagnosticReport for clinical notes
Use DocumentReference for narrative that is broader than one order, such as progress notes and discharge summaries. Use DiagnosticReport when the report has discrete results or coded interpretations, with the narrative in presentedForm. Scanned and narrative-only reports sit in both, and US Core requires servers to expose them through both resources.
The rule is specific: when a scanned report is exposed as DiagnosticReport.presentedForm.url, the same attachment SHALL also be reachable at DocumentReference.content.attachment.url. US Core also says imaging, laboratory and pathology narratives and procedure notes SHOULD be exposed through DiagnosticReport. For a pipeline this means one thing: if you pull both resources, dedupe on the attachment URL, or a pathology report will reach your model twice. Our US Core implementation guide covers the DiagnosticReport side in more depth.
How do you search for clinical notes correctly?
Search by patient plus category=clinical-note, add a date range, and follow the Bundle's next links. US Core makes _id, patient, patient+category, patient+category+date and patient+type mandatory for servers, and patient+status and patient+type+period recommended. Beyond that, each vendor has its own rules.
# All clinical notes for a patient since January 2026 (US Core SHALL combination)
curl -s "$FHIR_BASE/DocumentReference?patient=12724066\
&category=http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category|clinical-note\
&date=ge2026-01-01T00:00:00Z" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/fhir+json"
# Only discharge summaries
curl -s "$FHIR_BASE/DocumentReference?patient=12724066&type=http://loinc.org|18842-5" \
-H "Authorization: Bearer $TOKEN" -H "Accept: application/fhir+json" | Parameter | Epic (Clinical Notes) | Oracle Health Millennium | athenahealth |
|---|---|---|---|
patient | Required unless subject is given | Required unless _id is given; _id takes no other parameter except _revinclude | patient or _id required |
category / type | One of them needed to restrict to clinical notes | Supported; type accepts a comma-separated list | Supported |
date | Supported (note creation) | Supported; only one of date, period, _lastUpdated per request | Supported |
period | Supported (dates of service) | Must be sent twice, ge and lt | Not listed |
encounter | Supported | Supported, comma-separated list allowed | Supported |
_lastUpdated | Listed in the sandbox CapabilityStatement but not in the Clinical Notes spec, and Epic's search parameter guide marks it unsupported in R4. Test before relying on it | Supported, clinically significant updates only | Supported |
docstatus / status | docstatus supported; status is always current and, like author and contenttype, is applied as a post-filter | Not in documented parameters | Not listed |
| Paging | _count | _count default 10, max 100 | _count plus cursor |
Two behaviors we confirmed in Oracle's open sandbox: date plus _lastUpdated returns HTTP 400 ("date, period, and _lastUpdated may not be set at the same time"), and status=current still returned entered-in-error notes, so filter status in code. Oracle can also add an OperationOutcome with code suppressed to the Bundle. For paging, see our FHIR Bundle guide and the ten search patterns developers get wrong.
attachment.data vs attachment.url: how do you fetch the note text?
If the attachment has data, base64-decode it and you are done. If it has url, GET that exact URL. Send an Accept header equal to the attachment's contentType to receive raw bytes, or application/fhir+json to receive a Binary resource with the content base64-encoded in data. Never rebuild the URL yourself.
Here is a real clinical note DocumentReference from Oracle Health's public R4 sandbox, trimmed and with display names removed. Note the three things a naive client gets wrong: two renditions of one note, a proprietary type coding next to the LOINC code, and docStatus: amended on version 2 of the same id.
{
"resourceType": "DocumentReference",
"id": "203508833",
"meta": { "versionId": "2", "lastUpdated": "2023-01-23T18:01:24.000Z" },
"status": "current",
"docStatus": "amended",
"type": {
"coding": [
{ "system": "https://fhir.cerner.com/ec2458f2-1e24-41c8-b71b-0e701af7583d/codeSet/72",
"code": "2820507", "display": "Admission Note Physician", "userSelected": true },
{ "system": "http://loinc.org", "code": "83805-2",
"display": "Physician Admission evaluation note", "userSelected": false }
]
},
"category": [
{ "coding": [ { "system": "http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category",
"code": "clinical-note" } ] }
],
"subject": { "reference": "Patient/12724066" },
"date": "2023-01-23T17:59:30Z",
"author": [ { "reference": "Practitioner/12742069" } ],
"content": [
{ "attachment": { "contentType": "application/pdf",
"url": "https://fhir-open.cerner.com/r4/ec2458f2-1e24-41c8-b71b-0e701af7583d/Binary/XR-203508833" } },
{ "attachment": { "contentType": "application/xml",
"url": "https://fhir-open.cerner.com/r4/ec2458f2-1e24-41c8-b71b-0e701af7583d/Binary/XML-203508833" } }
],
"context": {
"encounter": [ { "reference": "Encounter/97953477" } ],
"period": { "start": "2023-01-23T18:01:22Z", "end": "2029-01-23T18:01:22Z" }
}
} Fetching the Binary, three ways. These ran against the same sandbox:
BASE=https://fhir-open.cerner.com/r4/ec2458f2-1e24-41c8-b71b-0e701af7583d
# 1. Raw bytes: Accept matches attachment.contentType -> 200 application/pdf
curl -s "$BASE/Binary/XR-209121356" -H "Accept: application/pdf" -o note.pdf
# 2. FHIR wrapper -> 200 application/fhir+json, content in Binary.data
curl -s "$BASE/Binary/TR-209121356" -H "Accept: application/fhir+json"
{"resourceType":"Binary","id":"TR-209121356","contentType":"text/html","data":"PCFET0NUWVBFIGh0bWwg..."}
# 3. Mismatched Accept -> 406
curl -s "$BASE/Binary/XR-209121356" -H "Accept: application/json"
{"resourceType":"OperationOutcome","issue":[{"severity":"error","code":"invalid",
"diagnostics":"The Accept Header is invalid. See the documentation ...","expression":["http.Accept"]}]} Epic's Binary.Read (Clinical Notes) works the same way: application/fhir+json or +xml returns the wrapper, anything else returns the raw note with its Content-Type. Oracle's Binary docs refuse application/json unless the data is JSON. A fetcher that handles both shapes and the 406:
import base64
import requests
PREFERRED = ["text/plain", "text/html", "application/xhtml+xml", "text/rtf", "application/pdf"]
def mime(att):
return att.get("contentType", "application/octet-stream").split(";")[0].strip().lower()
def pick_attachment(docref):
"""One rendition per note: prefer the easiest format to turn into text."""
atts = [c["attachment"] for c in docref.get("content", []) if "attachment" in c]
ranked = sorted(atts, key=lambda a: PREFERRED.index(mime(a)) if mime(a) in PREFERRED else 99)
return ranked[0] if ranked else None
def fetch_bytes(att, base_url, token):
if att.get("data"): # inline base64
return base64.b64decode(att["data"])
url = att["url"]
if not url.startswith("http"): # relative, e.g. "Binary/123"
url = f"{base_url.rstrip('/')}/{url}"
auth = {"Authorization": f"Bearer {token}"}
r = requests.get(url, headers={**auth, "Accept": mime(att)}, timeout=30)
if r.status_code == 406: # ask for the FHIR wrapper instead
r = requests.get(url, headers={**auth, "Accept": "application/fhir+json"}, timeout=30)
r.raise_for_status()
if r.headers.get("Content-Type", "").startswith("application/fhir+json"):
return base64.b64decode(r.json().get("data", ""))
return r.content | contentType | Where you will see it | How to get text |
|---|---|---|
text/plain | Epic with NoteReader CDI only (August 2025); Oracle | Decode with the declared charset. Epic warns styling and some Unicode are lost. |
text/html, application/xhtml+xml | Epic, Oracle | Extract text, keeping headings as line breaks so sections survive. |
text/rtf | Epic; accepted on Oracle create | RTF-to-text converter. Prefer HTML when both exist. |
application/pdf | Oracle, scanned notes everywhere | Text layer first, OCR only when empty. Flag OCR output. |
application/xml | Oracle, next to PDF or HTML | In the sandbox, a proprietary Cerner XML report (urn:com-cerner-patient-ehr:v3), not C-CDA. |
What Epic, Oracle Health and athenahealth return for clinical notes
All three expose clinical notes through US Core DocumentReference search and read, with content behind Binary. They differ in which renditions you get, which search parameters work, how versions and confidential notes show up, and whether FHIR create exists. For the wider API comparison across these vendors, see our EHR integration API comparison.
| Behavior | Epic | Oracle Health (Millennium) | athenahealth (athenaOne) |
|---|---|---|---|
| Interactions in live CapabilityStatement | create, read, search, update | read, search, create, update (secure endpoint) | read, search |
| Note body | Binary URLs, text/html and text/rtf; scans add another attachment | Binary URLs; content never embedded; PDF, HTML, XHTML, text plus application/xml | Binary read (profile ah-binary) |
| Status signals | status always current; docStatus preliminary, final, amended, entered-in-error (deleted) | status current, superseded, entered-in-error; docStatus adds appended | Standard; relatesTo removed from profile |
| Confidential notes | securityLabel NOPAT, omitted in patient-facing apps | meta.security tags for physician documents and radiology | meta.security NOPAT; SUBSETTED when _query is used |
| FHIR create | Plain text only, encounter required, first attachment only | Base64 data only; one content entry; PDF, RTF, HTML, text, XML | Not listed in R4 CapabilityStatement |
Epic
Epic splits DocumentReference into sub-resources on fhir.epic.com: Clinical Notes, Correspondences, External CCDA, Generated CDAs, Radiology Results, Labs, Outside Record Clinical Notes and more. Your query lands in Clinical Notes only when you pass category=clinical-note or a note type. Each note returns one attachment per format, HTML and RTF. Scanned notes add an attachment for the scan, and apps configured for NoteReader CDI get a third text/plain attachment since the August 2025 release.
The type can carry LOINC plus organization-specific and Epic-released category codings, depending on site mapping. context.related can reference the procedure order, SmartData Observations and problems tied to the note, which is useful model context. Epic's error table also lists code 4135, "Maximum document queries has been reached for the day", so plan backfills with the site. Our Epic FHIR integration guide covers auth and sandbox setup.
Oracle Health (Millennium)
Oracle's DocumentReference search covers charted and staged clinical notes plus cardiology, radiology, microbiology and pathology documents. Content is never embedded; each rendition is a Binary URL (prefixes like XR-, TR-, XML-) that Oracle says to use exactly as given. In the sandbox, category carries LOINC codings beside clinical-note, and type carries a Millennium code set 72 coding beside LOINC. _revinclude=Provenance:target is supported. Oracle also documents the $docref operation, which requires patient and a type carrying both system and code, with optional start and end.
athenahealth
athenaOne behavior here comes from its live R4 CapabilityStatement and published profiles. DocumentReference supports read and search, requires patient or _id, and adds parameters such as ah-practice and ah-chart-sharing-group. The ah-documentreference profile requires an athena Practice extension and sets relatesTo, authenticator, description and securityLabel to zero, so confidentiality lives in meta.security. Confirm attachment shapes and content types in the preview environment before you build the parser. Our athenahealth API developer guide and athenahealth integration page cover onboarding.
Scoping a multi-EHR notes pipeline? We build and run these integrations for product teams across Epic, Oracle Health, athenahealth, eClinicalWorks and NextGen. Talk to our team and we will map what each of your target EHRs actually exposes for notes, before you commit a parser to the wrong rendition.
How do you turn clinical notes into LLM-ready text?
Search, drop deleted and draft notes, pick one rendition per note, fetch and normalize it to text, dedupe versions, then chunk by section with provenance attached to every chunk. Keep PHI controls around the whole path. Most quality problems in notes-based AI come from duplicates, drafts and lost provenance, not from the model.
- Filter by status in code. Exclude
status=entered-in-erroranddocStatus=entered-in-error. Decide deliberately whetherpreliminarydrafts belong in retrieval; for chart summarization they usually do not. - One rendition per note. Prefer plain text, then HTML, then RTF, then PDF. Ignore Oracle's proprietary XML unless you have a parser for it.
- Normalize. Strip markup but keep section headings as boundaries. Record whether PDF text came from the text layer or OCR.
- Dedupe versions. Key on id plus
meta.versionId(ormeta.lastUpdated) with a content hash. Replace chunks when a note is amended; whererelatesTosaysreplaces, retire the target. - Chunk with provenance. Split on note sections, not token windows, and attach id, versionId, LOINC type, date, author and encounter to every chunk so answers cite the source note.
- Handle PHI as PHI. Use models covered by a HIPAA business associate agreement, request the narrowest SMART scopes (US Core defines granular
category=clinical-notescopes), keep note text out of logs, and honor NOPAT labels.
Incremental sync is where versioning bites. Poll with _lastUpdated, re-embed notes whose version changed, and delete any note that flips to entered-in-error from your vector store. Deeper design choices are in our posts on the unstructured clinical notes pipeline for AI agents and FHIR-native RAG retrieval.
Can you write AI-generated notes back with DocumentReference create?
Sometimes. Epic and Oracle Health document a FHIR DocumentReference create for clinical notes, each with tight constraints. athenahealth's R4 CapabilityStatement lists read and search only. When FHIR create is missing, or cannot carry the format you need, an HL7 v2 MDM message through an interface engine is the standard fallback.
Epic's DocumentReference.Create (Clinical Notes) files a plain text note to an open or closed encounter. The rules that matter: contentType must be text/plain and the decoded data cannot be RTF or HTML; only the first attachment is saved; context.encounter is required and must be a clinical encounter that can hold a note; docStatus can be preliminary (incomplete, so a clinician reviews and signs in Epic) or final (signed, the default). Notes filed during pre-charting must be preliminary. Type is a LOINC code from Epic's supported list or a site-specific note type, and Discharge Instructions and Patient Instructions cannot be created this way. This is Epic's sample request, trimmed:
POST https://hostname/instance/api/FHIR/R4/DocumentReference
Content-Type: application/fhir+json
Prefer: return=minimal
{
"resourceType": "DocumentReference",
"docStatus": "preliminary",
"type": { "coding": [ { "system": "http://loinc.org", "code": "11506-3", "display": "Progress note" } ] },
"subject": { "reference": "Patient/eUEKdnYPKuCXlSq8WIYaPTA3" },
"author": [ { "reference": "Practitioner/ejb8.EbaQ8UyziPoyHy.J3A3" } ],
"content": [ { "attachment": { "contentType": "text/plain",
"data": "VGhpcyBpcyBhIHNpbXBsZSBwbGFpbiB0ZXh0IG5vdGU=" } } ],
"context": { "encounter": [ { "reference": "Encounter/eKvkb8V4sgTKL9nm3MtDjXw3" } ] }
}
HTTP/1.1 201 Created
Location: DocumentReference/ePpOtpLCSTFBNsWp4uy1XWw3 Epic's own sample uses docStatus: final; we changed it to preliminary because a clinician should sign AI-drafted text. For rich text, In Basket interaction, encounters created on the fly or fuzzy encounter matching, Epic points to its Incoming MDM (Transcriptions) interface, listed in the open.epic HL7 v2 catalog.
Oracle's DocumentReference create is broader on format and stricter on shape: status must be current; docStatus must be final under provider access (system access also allows amended), with no preliminary option; content is exactly one entry with base64 data in PDF, plain text, RTF, HTML, XML or XHTML; context.period.end is required; type is LOINC or code set 72, not both; and under provider access the single author must be the authorized provider, while authenticator is accepted only under system access. Create supports Provider and System authorization, not Patient, and every date needs a time component. Because the note lands final, clinician review must happen in your product before the POST.
For athenahealth, and for any site where FHIR create is not enabled, an HL7 v2 MDM^T02 (original document with content) sent through an engine such as Mirth Connect is the usual route; confirm the inbound interface with the vendor or health system first. See our MDM routing guide, the FHIR write problem for AI agents, and engineering ambient clinical documentation.
Where teams get stuck
The failures below are the ones that pass sandbox testing and then surface with real charts. Each one costs a re-index, a parser rewrite or a blocked go-live, and every one of them is avoidable in design.
- Unfiltered searches. Leaving out
categoryortypepulls correspondence and generated CDAs into a "notes" corpus. It shows up as odd model answers weeks later, and the fix is a full re-ingest. - Embedding every rendition. HTML plus RTF, or PDF plus XML, doubles retrieval hits for the same note.
- Parsing the wrong XML. Treating Oracle's
application/xmlrendition as C-CDA; a CDA parser silently returns nothing. - Accept header drift. A generic client sends
application/jsonand gets 406 from Oracle, or a base64 wrapper from Epic when it expected raw HTML. - Drafts and deletions in the index. Preliminary notes answered as if signed, and entered-in-error notes never purged. This is a patient safety issue, not just a quality one.
- Date parameter errors. Oracle rejects
datewith_lastUpdated, and a singleperiod. Incremental jobs fail on their first scheduled run. - Write-back assumptions. Designing RTF notes for Epic's plain-text create, or planning FHIR create on athenaOne. Found late, this adds an MDM interface and a health system change request to the timeline.
- Per-site variation. Local note types and disabled APIs differ by customer; our CapabilityStatement guide shows what to check at onboarding.
Production checklist for a clinical notes integration
- Record each site's DocumentReference and Binary interactions, profiles and search parameters from its CapabilityStatement.
- Request DocumentReference and Binary read scopes, plus create only where you write.
- Search with
patientpluscategory=clinical-noteortype; follownextlinks. - Skip non-DocumentReference Bundle entries and log
suppressedwarnings. - Filter
statusanddocStatusclient-side; purge entered-in-error notes from every downstream store. - Choose one rendition per note with an explicit preference order, and record which one you used.
- Fetch Binary with Accept equal to the contentType; fall back to
application/fhir+jsonon 406. - Dedupe on id plus versionId or lastUpdated, and on attachment URL across DocumentReference and DiagnosticReport.
- Chunk by section with provenance on every chunk; keep OCR output flagged.
- Honor NOPAT and other security labels, keep PHI out of logs, and use BAA-covered model endpoints.
- For write-back, verify the vendor's create rules per site, send
preliminarywhere supported, include encounter, LOINC type and author, and read the created note back. - Keep an HL7 v2 MDM path scoped for sites where FHIR create is unavailable or too limited.
Clinical notes are where interoperability and AI meet, and most of the work is in the details above. Our healthcare interoperability solutions team builds the EHR integration layer for notes across Epic, Oracle Health and athenahealth, including Mirth Connect interfaces we run in production, and our healthcare AI agents team builds the scribes and retrieval systems that consume them. Talk to our team to scope your notes integration.



