To turn a medical document into FHIR, first extract its fields, then pick the resource that matches the document type, fill that resource's elements from the fields, link each resource to a Patient, and keep the original file as a DocumentReference. EOBs become ExplanationOfBenefit, lab reports become DiagnosticReport with Observations, and intake forms fill Patient and Coverage.
Which FHIR resources match each medical document type?
Each medical document type maps to one or two FHIR resources, chosen by what the paper records. FHIR (Fast Healthcare Interoperability Resources) is the HL7 standard that models health data as typed resources. A payer's decision on a claim maps to ExplanationOfBenefit, a lab result to Observation, and a prescription to MedicationRequest.
The table below is a vendor-neutral starting point for FHIR R4. It names the resource and the elements the most common extracted fields fill. Treat it as a first draft for your own mapping, not a profile.
| Document | FHIR resource | Key elements the extracted fields fill |
|---|---|---|
| EOB | ExplanationOfBenefit | identifier (payer claim number), patient, insurer, item.servicedDate, item.productOrService, item.adjudication (category, reason, amount), total, payment.amount, payment.identifier |
| Insurance claim (CMS-1500, UB-04) | Claim | patient, provider, billablePeriod, diagnosis.diagnosis[x], item.productOrService, item.diagnosisSequence, item.quantity, item.net, total |
| Lab report | DiagnosticReport and Observation | Report: code, status, effective[x], issued, performer, result. Each test: code, value[x], referenceRange, interpretation |
| Prescription | MedicationRequest | medication[x], subject, authoredOn, requester, dosageInstruction, dispenseRequest.quantity, dispenseRequest.numberOfRepeatsAllowed |
| Discharge summary | Composition | type, subject, encounter, date, author, title, section (title, text, entry) |
| Billing statement | DocumentReference | type, subject, date, content.attachment; charges and balances stay in your billing system |
| Patient intake form | Patient and Coverage | Patient: name, birthDate, gender, address, telecom. Coverage: subscriberId, beneficiary, payor, class |
| Medical record | DocumentReference | type, subject, date, context.encounter, content.attachment |
Three rows deserve a note. The HL7 FHIR R4 Claim page says that when both the request and the adjudication are to be reported, ExplanationOfBenefit should be used instead, which is why an EOB does not become a Claim plus a response. The HL7 FHIR R4 Composition page lists "Discharge Summary" as an example type and says a Composition only becomes a document when it is the first entry in a Bundle of type document. The HL7 FHIR R4 Coverage page defines subscriberId as the insurer-assigned ID for the subscriber, the closest home for the member ID on an intake form or insurance card.
Billing statements and long medical records rarely map cleanly to one resource. Keeping them as indexed documents is often the honest choice until you know which facts a consumer needs. How each EOB column lands on item and adjudication is covered in the guide to EOB fields mapped onto items and adjudication. The lab side has its own walk-through in one lab result as an Observation, worked example.
The fields themselves come from an extraction step that runs before any of this mapping. Schema Care reads the eight document types in the table into fields with a confidence score and a link to the source page, which you can see listed on the document types Schema Care reads. Schema Care offers no API, SDK, webhooks, FHIR output, 835 output or 837 output, so the mapping in this post is something your own integration would do.
Keeping the source file as a FHIR DocumentReference
The FHIR DocumentReference resource keeps the original scan or PDF next to the structured resources made from it. The HL7 FHIR R4 DocumentReference page says it "is used to index a document, clinical note, and other binary objects to make them available to a healthcare system."
A DocumentReference holds metadata, and the file sits inside content.attachment. The required elements are status (current, superseded or entered-in-error) and at least one content. The rest is optional but worth filling: type for the kind of document, subject for the patient, date for when the reference was created, and context.encounter for the visit it belongs to.
The attachment itself is the FHIR Attachment data type. The HL7 FHIR R4 data types page lists its parts: contentType for the MIME type, data for base64 content, url for where the file can be fetched, size, hash, title and creation. For scanned paperwork, a url pointing at your document store usually beats inlining a large PDF as base64.
The real value comes from linking structured resources back to the file. The HL7 FHIR R4 Observation page allows derivedFrom to reference a DocumentReference, so each lab value can point at the report it was read from. The HL7 FHIR R4 DiagnosticReport page offers presentedForm, an Attachment for the report as it was presented. ExplanationOfBenefit has a form Attachment for the printed form. A reviewer who doubts a number can then open the page it came from.
Page-level and coordinate-level links do not fit DocumentReference's base elements. If you want the field to point at page 2, line 4, that detail goes in an extension or stays in your extraction store, which the next sections cover.
Linking FHIR resources to a Patient and Encounter
FHIR resources made from one document are tied together by references to a shared Patient and, where there is one, an Encounter. A reference is a pointer from one resource to another. Without these links, an ExplanationOfBenefit, its DocumentReference and the patient's chart sit in the server as unrelated records.
The HL7 FHIR R4 references page describes three ways to point at a resource. A literal reference is a URL such as Patient/034AB16. A logical identifier is used when no URL is known, for example a member ID or a medical record number. A display is a short text label. At least one must be present, and the literal reference is preferred when both exist.
Extracted documents usually start with only logical identifiers. A paper EOB prints a member ID and a name, not your server's patient ID. The safe pattern is to match the printed identifiers to an existing Patient first, then write the literal reference. If no match is found, send the document to a person rather than creating a second Patient for the same human.
An Encounter is one visit or stay. The HL7 FHIR R4 Encounter page requires status and class, and allows subject, period and serviceProvider. A discharge summary or a lab report drawn during an admission has an encounter to link. An EOB covers billed services and links to the patient, insurer and provider, not to an encounter.
Posting one document's resources in a single transaction
A FHIR transaction Bundle sends a Patient, a DocumentReference and the resources derived from it in one request. The HL7 FHIR R4 RESTful API page says that when a server assigns a new id on a POST, it "SHALL also update any references to that resource in the same bundle." Temporary urn:uuid full URLs let the entries point at each other before any of them exist.
The request below is an example only. It posts to https://api.example.com/fhir, a placeholder address, and the patient, member ID and values are fictional. Schema Care has no FHIR endpoint.
POST https://api.example.com/fhir
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:7f1c2a40-0000-4000-8000-000000000001",
"resource": {
"resourceType": "DocumentReference",
"status": "current",
"subject": { "reference": "Patient/example-jordan-avery" },
"description": "Example lab report, fictional patient",
"content": [{ "attachment": { "contentType": "application/pdf", "url": "https://api.example.com/files/lab-0042.pdf" } }]
},
"request": { "method": "POST", "url": "DocumentReference" }
},
{
"fullUrl": "urn:uuid:7f1c2a40-0000-4000-8000-000000000002",
"resource": {
"resourceType": "Observation",
"status": "final",
"code": { "text": "Potassium" },
"subject": { "reference": "Patient/example-jordan-avery" },
"valueQuantity": { "value": 4.1, "unit": "mmol/L" },
"derivedFrom": [{ "reference": "urn:uuid:7f1c2a40-0000-4000-8000-000000000001" }]
},
"request": { "method": "POST", "url": "Observation" }
}
]
}
When the server stores the DocumentReference, it rewrites the Observation's derivedFrom to the new id. The code system inside code, here left as text, is a separate decision covered in which code systems appear in extracted medical data.
What should a FHIR mapping do with fields that have no element?
A FHIR mapping has three choices for an extracted field with no matching element: put it in an extension, put it in a free-text note, or leave it out. Pick by asking who will read the value. Software that needs to act on it calls for an extension. A person who needs context calls for a note.
The HL7 FHIR R4 extensibility page says every element in a resource or data type includes an optional extension child, which carries "additional information that is not part of the basic definition of the resource." An extension needs a url that identifies its definition, and it holds either a value or sub-extensions, not both. Publish your extension definitions so the systems that read your data know what the URL means.
Confidence scores and source coordinates are the usual candidates. Neither has a base element in ExplanationOfBenefit, Observation or Patient. An extension such as https://api.example.com/fhir/StructureDefinition/extraction-confidence (an example URL) could hold a decimal on each element. Before you add one, ask whether any consumer will read it. Many teams keep confidence and coordinates in their extraction store and send only reviewed values to FHIR. The shape of that extraction store is covered in JSON schema for medical documents.
Free-text notes suit context a person needs. Observation and MedicationRequest both have a note element for annotations, and ExplanationOfBenefit has processNote for the payer's explanatory text. A remark printed on an EOB that has no code fits there.
Avoid modifier extensions for extracted data. The extensibility page reserves modifierExtension for information that changes the meaning of the element that holds it, and readers that do not understand one must reject or warn. A confidence score does not change what a lab value means, so it is never a modifier.
Some fields are best left out. A fax header, a clinic logo or a page footer may be worth extracting for routing, but nothing downstream needs them in the patient's record.
FHIR document extraction edge cases: partial pages and low confidence
FHIR document extraction breaks down most often on pages that are cut off and on fields the extractor is unsure of. The rule of thumb: do not let an uncertain value become an unremarkable FHIR resource. Once a value sits in a chart as a normal Observation, nothing marks it as doubtful.
Low-confidence fields have two good outcomes. The first is to hold them back until a person has confirmed them, and post only reviewed values. The second, for Observations, is to post with a status that tells readers the data is unverified. The HL7 FHIR R4 Observation status value set defines preliminary as an initial observation whose data may be incomplete or unverified. If a reviewer later corrects a value already marked final, the same value set offers corrected, a modification made to fix an error.
Partial pages leave real gaps. When a lab result is missing, Observation's dataAbsentReason says why. The HL7 FHIR R4 data absent reason value set includes unknown, as-text for data present only in the narrative, and error for a system or workflow failure. The Observation page allows dataAbsentReason only when value[x] is absent, so a missing value never sits beside a guess.
ExplanationOfBenefit has no such element. If the last page of an EOB is missing, the total and payment amounts are unknown. Leave those elements out rather than summing the lines you have, because a computed total that silently omits a page looks exactly like a real one.
Multi-patient documents are their own edge case. A bulk EOB or a fax batch must be split by patient before mapping, or one patient's lines end up under another's ExplanationOfBenefit. Uncertain values and how they are routed are covered in clinical document extraction for clinical paperwork and in what a medical document extraction API should do for the extraction step itself.
Reading FHIR data back with SMART on FHIR apps
SMART on FHIR apps for documents read the resources your mapping created and can show a DocumentReference's file beside the values derived from it. SMART App Launch is an HL7 specification that lets a user-facing app connect to a FHIR server, from inside an EHR or on its own, under permissions the user or EHR grants.
The HL7 SMART App Launch 2.2.0 specification describes the pattern as based on OAuth 2.0 and uses "scopes" to define the access an app is granted. Its example scope user/Encounter.rs lets an app read and search the Encounters the user can access. The specification names two ways to start an app, an EHR launch and a standalone launch, and the launch context can carry the currently selected patient.
For document-derived data, a SMART app typically does three things. It searches for the patient's DocumentReferences to list the documents on file. It reads the Observations, ExplanationOfBenefits or MedicationRequests that point at one of those documents through derivedFrom, presentedForm or form. It fetches the attachment url so the user can see the original page.
The mapping choices earlier in this post decide how useful that app can be. If Observations carry derivedFrom, the app can jump from a value to its source. If preliminary values carry preliminary status, the app can show them differently. If confidence lives only in your extraction store, the app cannot see it at all, which may be exactly what you want.
Writing back is a separate permission. An app that lets a reviewer correct a value needs a scope that allows writes to that resource type, and the EHR decides whether to grant it. Test write-back on a non-production server with fictional patients before any real chart is involved.