Written and maintained by CASRAI Editorial Board
Last updated
A FHIR Bundle is a foundational container resource in the Health Level Seven (HL7) Fast Healthcare Interoperability Resources standard used to package a collection of independent FHIR resources into a single atomic JSON or XML payload for RESTful transmission, clinical document exchange, event messaging, or search-result pagination. Unlike clinical resources such as Patient or Observation that inherit from DomainResource and carry a human-readable text narrative block, the Bundle resource inherits directly from the root Resource definition: it has no top-level narrative or extension array because its sole architectural role is to wrap an array of Bundle.entry elements governed by a mandatory Bundle.type code.
What Is a FHIR Bundle Resource?
In production FHIR API architectures (spanning FHIR R4 and FHIR R5), virtually every multi-resource HTTP interaction returns or accepts a Bundle. Each item inside the Bundle.entry array can contain up to five structural sub-elements depending on the bundle’s purpose:
fullUrl(URI): The complete identity URI of the packaged resource—either an absolute RESTful server URL (e.g.,https://fhir.example.org/r4/Patient/10482) or an ephemeral UUID (e.g.,urn:uuid:61ebe359-bfdc-4613-8bf2-c5e300945f0a) used to wire internal references before a server assigns permanent IDs.resource(Resource): The actual embedded FHIR resource instance (such as aPatient,Encounter,Condition, orObservation).search(BackboneElement): Present insearchsetbundles; indicates why the server included this entry—specifically"mode": "match"(matched the primary query filter),"mode": "include"(pulled in via an_includeor_revincludejoin), or"mode": "outcome"(anOperationOutcomewarning).request(BackboneElement): Required intransactionandbatchbundles; specifies the HTTP verb (GET,POST,PUT,PATCH,DELETE), relative targeturl, and conditional headers (ifNoneExist,ifMatch,ifNoneMatch).response(BackboneElement): Present intransaction-responseandbatch-responsebundles; reports the HTTPstatuscode (e.g.,"201 Created"),locationheader,etag, andlastModifiedtimestamp for each processed entry.
Bundle.type Code |
Mandatory Structural Constraint | Primary Healthcare & Research Interoperability Use Case |
|---|---|---|
document |
entry[0] must be a Composition resource; all referenced resources must be contained inside the Bundle |
Immutable, clinician-attested clinical documents (FHIR equivalent of HL7 C-CDA Continuity of Care Documents and discharge summaries) |
message |
entry[0] must be a MessageHeader resource specifying source, destination, and event code |
Event-driven routing (FHIR replacement for HL7 v2 ADT admission/discharge feeds and lab result notifications) |
transaction |
Every entry must carry a request element; no duplicate resource identities allowed |
Atomic “all-or-nothing” multi-resource ingestion (e.g., posting a new Patient, Encounter, and 12 lab Observations to HAPI FHIR) |
transaction-response |
Every entry carries a response element in the exact order of the submitted transaction |
Server confirmation returning newly assigned logical IDs and version ETags after committing an atomic transaction |
batch |
Every entry must carry a request element; no inter-entry dependencies permitted |
High-throughput bulk submission of independent reads or writes where partial failure is acceptable |
batch-response |
Every entry carries an independent response element (mixing 200/201 successes and 4xx/5xx errors) |
Server response to a batch POST, allowing the client to retry only the specific failed entries |
searchset |
Populated by the server in response to a FHIR RESTful search (GET [base]/[type]?params) |
Paginated query results with Bundle.total count and match vs. include entry modes |
history |
Every entry must carry both request and response showing chronological audit versions |
Retrieving the change log of a resource instance (GET /Patient/123/_history) for 21 CFR Part 11 audit trails |
collection |
Unstructured grouping with no first-entry constraint and no server execution semantics | Static file export, SMART Health Cards payload packaging, or clinical trial dataset snapshots |
What Is the Difference Between a Transaction Bundle and a Batch Bundle in FHIR?
Both transaction and batch Bundles are submitted via an HTTP POST directly to the FHIR server’s root base URL (POST [base]/), but the server processes them under fundamentally different ACID (Atomicity, Consistency, Isolation, Durability) and reference-resolution rules:
- Atomicity and Rollback Semantics: A
transactionBundle is strictly atomic: the FHIR server executes all entries inside a single database transaction. If entry #14 out of 15 fails validation (for example, an invalid LOINC code or broken profile invariant), the server rolls back entries #1 through #13 completely and returns a single HTTP400or422OperationOutcomeerror. Conversely, abatchBundle treats each entry as an isolated HTTP request multiplexed over one network round-trip: if entry #14 fails, entries #1 through #13 and #15 still commit, and the server returns HTTP200 OKwrapping abatch-responseBundle where entry #14’sresponse.statusshows"422 Unprocessable Entity". - Deterministic Verb Ordering in Transactions: To prevent foreign-key race conditions, the HL7 FHIR specification mandates that servers process
transactionBundle entries in a strict four-phase sequence regardless of their JSON array order: (1)DELETEoperations, (2)POST(create) operations, (3)PUT/PATCH(update) operations, and (4)GET(read/search) operations. - Internal
urn:uuid:Reference Rewriting: When a clinical trial eSource pipeline creates a newPatientand a linkedObservationsimultaneously, the client does not yet know the logical ID the server will assign to thePatient. In atransactionBundle, the client assigns"fullUrl": "urn:uuid:a1b2..."to thePatiententry and sets"subject": {"reference": "urn:uuid:a1b2..."}inside theObservation. During transaction processing, the FHIR server automatically mints a permanent ID (e.g.,Patient/9941) and rewrites every matchingurn:uuid:pointer across the entire Bundle before committing. In abatchBundle, inter-entry reference resolution is prohibited.
How Are Pagination Links Handled in a FHIR Searchset Bundle?
When a client queries a large clinical repository (such as AWS HealthLake) with GET /Observation?patient=10482&_count=50, returning 25,000 laboratory observations in a single JSON payload would exhaust client and server memory. FHIR implements RFC 5005 cursor pagination via the top-level Bundle.link array:
relation: "self": Echoes the exact URL and query parameters (including the server’s applied_countpage size) that produced the current Bundle page.relation: "next": Contains an opaque, server-generated continuation URL (e.g.,https://fhir.example.org/r4?_getpages=7a8b9c&_getpagesoffset=50&_count=50). Clients must treat thenextURL as an opaque string and follow it verbatim without attempting to construct manual SQL-style offset parameters, until a returnedsearchsetBundle no longer contains anextlink (signaling the end of the result set).relation: "previous"/"first"/"last": Optional directional cursors allowing clinical user interfaces to page backward through longitudinal histories.
Frequently Asked Questions
What is a FHIR Bundle resource?
A FHIR Bundle is a top-level HL7 FHIR container resource that packages multiple FHIR resources into a single JSON or XML payload for search results, atomic database transactions, batch uploads, clinical documents, or event messages, governed by one of nine Bundle.type codes.
What is the difference between a transaction Bundle and a batch Bundle in FHIR?
A transaction Bundle executes all entries atomically (rolling back the entire Bundle if any single entry fails) and automatically resolves internal urn:uuid cross-references between newly created resources, whereas a batch Bundle executes each entry independently (allowing partial successes alongside individual entry errors) and forbids inter-entry dependencies.
How are pagination links handled in a FHIR searchset Bundle?
FHIR searchset Bundles paginate large result sets using the top-level Bundle.link array (following RFC 5005), where the client reads the current page and iteratively requests the opaque URL provided in the link element with relation=”next” until no next link is returned.







