Bundle — Multi-resource container
The envelope that lets FHIR carry several resources at once: search result, atomic transaction, patient document, technical message.
Purpose of the resource
Bundle answers a simple question: how do you ship ten FHIR resources in a single payload? Whether it's a search result returning fifteen Patients, an atomic transaction that creates a Patient and its related Observations, or an IPS document that consolidates the patient summary, Bundle is the universal envelope.
The discrimination happens through the type field, which takes one of the
eight allowed values and dictates the Bundle's semantics (with or without
transaction, persistent or transient, paged or not).
The eight Bundle types
| Type | Usage | Persistence | Transaction semantics |
|---|---|---|---|
document | FHIR Document (IPS patient summary, CDA-like) | Yes — may be stored | No transaction. First entry = Composition. |
message | Asynchronous exchange, HL7 v2 style | Transient | No transaction. First entry = MessageHeader. |
transaction | Multi-resource atomic create / update | Transient | Atomic — all entries or none. Server resolves references. |
transaction-response | Server response to a transaction | Transient | List of individual outcomes. |
batch | Multi-resource independent operations | Transient | Non-atomic — each entry succeeds or fails independently. |
batch-response | Server response to a batch | Transient | List of individual outcomes. |
history | History of a resource or of the server | Transient | No transaction. |
searchset | Search result | Transient | Includes pagination via link. |
collection | Generic bag of resources, free interpretation | Variable | No imposed technical semantics. |
subscription-notification | Notification from an R5 Subscription | Transient | Real-time push mechanism. |
Key fields
| Field | Type | Cardinality | Role |
|---|---|---|---|
type | code | 1..1 | Mandatory. One of the eight (ten with subscription-notification) types above. |
timestamp | instant | 0..1 | Generation time of the Bundle. |
total | unsignedInt | 0..1 | Total matches for a searchset (may differ from entry.length in paged mode). |
link | BackboneElement[] | 0..* | Pagination links: self, next, previous, first, last. |
entry | BackboneElement[] | 0..* | List of carried resources. Each entry contains: |
entry.fullUrl | uri | 0..1 | Absolute URL of the resource. Critical for reference resolution. |
entry.resource | Resource | 0..1 | The resource itself. |
entry.search | BackboneElement | 0..1 | For searchset: mode (match, include, outcome) and score. |
entry.request | BackboneElement | 0..1 | For transaction / batch: HTTP method (POST, PUT, DELETE, PATCH, GET) and URL. |
entry.response | BackboneElement | 0..1 | For transaction-response / batch-response: status, location, etag, lastModified, outcome. |
signature | Signature | 0..1 | Digital signature of the Bundle (for document and message types in particular). |
Example — transaction
A transaction Bundle that creates a new Patient, updates an existing
Patient (idempotent PUT), and creates a body weight Observation linked to the first
Patient via a relative urn:uuid: reference:
{
"resourceType": "Bundle",
"id": "bundle-transaction",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:88f151c0-a954-468a-88bd-5ae15c08e059",
"resource": {
"resourceType": "Patient",
"identifier": [{
"system": "http:/example.org/fhir/ids",
"value": "234234"
}],
"active": true,
"name": [{ "family": "Chalmers", "given": ["Peter", "James"] }],
"gender": "male",
"birthDate": "1974-12-25"
},
"request": {
"method": "POST",
"url": "Patient"
}
},
{
"fullUrl": "http://example.org/fhir/Patient/123",
"resource": {
"resourceType": "Patient",
"id": "123",
"active": true,
"name": [{ "family": "Smith", "given": ["John"] }],
"gender": "male",
"birthDate": "1980-01-15"
},
"request": {
"method": "PUT",
"url": "Patient/123"
}
},
{
"fullUrl": "urn:uuid:79378cb8-8f72-4d33-8b8d-d6c0c5e1e3bb",
"resource": {
"resourceType": "Observation",
"status": "final",
"code": {
"coding": [{
"system": "http://loinc.org",
"code": "29463-7",
"display": "Body Weight"
}]
},
"subject": {
"reference": "urn:uuid:88f151c0-a954-468a-88bd-5ae15c08e059"
},
"effectiveDateTime": "2026-05-14T10:30:00Z",
"valueQuantity": {
"value": 72.5,
"unit": "kg",
"system": "http://unitsofmeasure.org",
"code": "kg"
}
},
"request": {
"method": "POST",
"url": "Observation"
}
}
]
} Key takeaways:
-
fullUrlasurn:uuid:: for resources created inside the bundle (no server-sideidyet), a temporary UUID is used. The server swaps it for the allocatedidand resolves internal references. - Internal reference:
Observation.subject.reference = "urn:uuid:88f151c0..."points to the Patient that will be created in the same transaction. The server guarantees referential consistency. - HTTP method per entry:
POSTfor creations (server allocates id),PUTfor idempotent updates (client provides id). - Atomicity: if any entry fails, the server rolls back the whole thing. No partial change.
Example — searchset
A searchset Bundle returned by GET /Patient?family=Chalmers
with two matches and a next pagination link:
{
"resourceType": "Bundle",
"id": "searchset-example",
"type": "searchset",
"total": 2,
"link": [
{
"relation": "self",
"url": "https://server/Patient?family=Chalmers"
},
{
"relation": "next",
"url": "https://server/Patient?family=Chalmers&_page=2"
}
],
"entry": [
{
"fullUrl": "https://server/Patient/example",
"resource": {
"resourceType": "Patient",
"id": "example",
"name": [{ "family": "Chalmers", "given": ["Peter"] }],
"gender": "male",
"birthDate": "1974-12-25"
},
"search": {
"mode": "match",
"score": 1.0
}
},
{
"fullUrl": "https://server/Patient/example2",
"resource": {
"resourceType": "Patient",
"id": "example2",
"name": [{ "family": "Chalmers", "given": ["Sarah"] }],
"gender": "female",
"birthDate": "1981-07-12"
},
"search": {
"mode": "match",
"score": 0.95
}
}
]
} total: two matches (may be omitted if the server is in count-disabled mode).link[self]: the original request.link[next]: next page in the result set. The client follows these links to paginate.entry.search.mode:match= resource matching the criteria;include= resource brought in by_includebut not part of the original criteria.entry.search.score: match relevance (0..1), only when the server supports semantic ranking.
Transaction vs batch
The two types transaction and batch share the same structure
but carry different semantics:
| Aspect | transaction | batch |
|---|---|---|
| Atomicity | Yes — all or nothing | No — each entry independent |
| Reference resolution | Server resolves urn:uuid: and circular references across entries | No resolution — each entry is an isolated request |
| Execution order | Server-side deterministic (DELETE, POST, PUT/PATCH, GET, HEAD) | Entry order |
| Use case | Building a complete patient record, posting a genetic test with its observations | Independent bulk update: refresh 50 unrelated Observations |
| Performance | More expensive server-side (lock + rollback) | Cheaper, parallelisable |
Common pitfalls
- Missing or non-URN
fullUrl: in a transaction, a missingfullUrlmakes internal reference resolution impossible. Always supply one, either asurn:uuid:<UUID>or as an absolute URL. - Absolute reference in a transaction: if the
Observationreferences the Patient via"reference": "http://server/Patient/123"while the Patient is being created in the same bundle without that id, the transaction fails. - Confusing
searchsetandcollection: some older servers returncollectioninstead ofsearchset— the client must handle both. Do not confuse the two on the creation side. - Bundle > 100 entries: the spec sets no hard limit, but most servers cap it (HAPI: 1000 by default, Epic R5: 100). Pagination becomes mandatory beyond.
- Unsigned document Bundle: an IPS document without a
signaturemay be rejected by certain national authorities (FR, NL, UK). Always include it for persistent documents. -
type=transactionPOSTed to/Patient: a transaction Bundle is POSTed to the server root (POST /), not to an individual resource URL.
Related resources
- Composition — mandatory first entry of a
documentBundle. - MessageHeader — mandatory first entry of a
messageBundle. - OperationOutcome — carries errors and warnings in transaction / batch responses.
- Subscription and SubscriptionStatus — fire
subscription-notificationBundles. - Parameters — alternative to Bundle for operation parameters (
$everything,$lastn…).
See also: Patient — the foundational demographic resource, the FHIR R5 hub, and the functional v2 equivalent of a multi-ORM Bundle: ORM^O01.