diff --git a/eng/bruno/Fhir/History/folder.bru b/eng/bruno/Fhir/History/folder.bru
new file mode 100644
index 0000000..eceecfb
--- /dev/null
+++ b/eng/bruno/Fhir/History/folder.bru
@@ -0,0 +1,4 @@
+meta {
+ name: History
+ seq: 1
+}
diff --git a/eng/bruno/Fhir/History/instance-history.bru b/eng/bruno/Fhir/History/instance-history.bru
new file mode 100644
index 0000000..18e5447
--- /dev/null
+++ b/eng/bruno/Fhir/History/instance-history.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Instance history
+ type: http
+ seq: 1
+}
+
+get {
+ url: {{fhirBase}}/Patient/{{patientId}}/_history
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Every version of one patient, as a `history` `Bundle`. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/History/system-history.bru b/eng/bruno/Fhir/History/system-history.bru
new file mode 100644
index 0000000..147534b
--- /dev/null
+++ b/eng/bruno/Fhir/History/system-history.bru
@@ -0,0 +1,24 @@
+meta {
+ name: System history
+ type: http
+ seq: 3
+}
+
+get {
+ url: {{fhirBase}}/_history?_count=5
+ body: none
+ auth: inherit
+}
+
+params:query {
+ _count: 5
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Version history across every resource type on the server.
+}
diff --git a/eng/bruno/Fhir/History/type-history.bru b/eng/bruno/Fhir/History/type-history.bru
new file mode 100644
index 0000000..0b4a878
--- /dev/null
+++ b/eng/bruno/Fhir/History/type-history.bru
@@ -0,0 +1,24 @@
+meta {
+ name: Type history
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{fhirBase}}/Patient/_history?_count=5
+ body: none
+ auth: inherit
+}
+
+params:query {
+ _count: 5
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Version history across all patients.
+}
diff --git a/eng/bruno/Fhir/History/version-read.bru b/eng/bruno/Fhir/History/version-read.bru
new file mode 100644
index 0000000..272829d
--- /dev/null
+++ b/eng/bruno/Fhir/History/version-read.bru
@@ -0,0 +1,21 @@
+meta {
+ name: Read version
+ type: http
+ seq: 4
+}
+
+get {
+ url: {{fhirBase}}/Patient/{{patientId}}/_history/{{patientVersionId}}
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Patient
+}
+
+docs {
+ Reads one specific version. Needs `patientId` and `patientVersionId`, both saved by
+ **Create Patient (valid)**.
+}
diff --git a/eng/bruno/Fhir/Operations/everything-instance.bru b/eng/bruno/Fhir/Operations/everything-instance.bru
new file mode 100644
index 0000000..d3b91fa
--- /dev/null
+++ b/eng/bruno/Fhir/Operations/everything-instance.bru
@@ -0,0 +1,15 @@
+meta {
+ name: Everything (instance)
+ type: http
+ seq: 1
+}
+
+get {
+ url: {{fhirBase}}/Patient/{{patientId}}/$everything
+ body: none
+ auth: inherit
+}
+
+docs {
+ All resources related to the patient instance, as a `Bundle`. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/Operations/folder.bru b/eng/bruno/Fhir/Operations/folder.bru
new file mode 100644
index 0000000..f8fbbe4
--- /dev/null
+++ b/eng/bruno/Fhir/Operations/folder.bru
@@ -0,0 +1,4 @@
+meta {
+ name: Operations
+ seq: 3
+}
diff --git a/eng/bruno/Fhir/Operations/meta-read.bru b/eng/bruno/Fhir/Operations/meta-read.bru
new file mode 100644
index 0000000..3570891
--- /dev/null
+++ b/eng/bruno/Fhir/Operations/meta-read.bru
@@ -0,0 +1,25 @@
+meta {
+ name: Meta (read)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Patient/{{patientId}}/$meta
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Parameters"
+ }
+}
+
+docs {
+ The resource's `meta` (tags, profiles, security labels) as `Parameters`. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Encounter/create-encounter-invalid-status.bru b/eng/bruno/Fhir/Resources/Encounter/create-encounter-invalid-status.bru
new file mode 100644
index 0000000..c4034c5
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Encounter/create-encounter-invalid-status.bru
@@ -0,0 +1,35 @@
+meta {
+ name: Create Encounter (invalid status code)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Encounter
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Encounter",
+ "status": "closed",
+ "class": {
+ "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
+ "code": "AMB"
+ }
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `status` must be an `EncounterStatus` code. Expect `400` at `Encounter.status`.
+}
diff --git a/eng/bruno/Fhir/Resources/Encounter/create-encounter-valid.bru b/eng/bruno/Fhir/Resources/Encounter/create-encounter-valid.bru
new file mode 100644
index 0000000..27760d0
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Encounter/create-encounter-valid.bru
@@ -0,0 +1,43 @@
+meta {
+ name: Create Encounter (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Encounter
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Encounter",
+ "status": "finished",
+ "class": {
+ "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
+ "code": "AMB",
+ "display": "ambulatory"
+ },
+ "subject": {
+ "reference": "Patient/{{patientId}}"
+ }
+ }
+}
+
+vars:pre-request {
+ patientId: 123
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq Encounter
+}
+
+docs {
+ An ambulatory encounter for the patient. Expect `201`.
+}
diff --git a/eng/bruno/Fhir/Resources/Encounter/folder.bru b/eng/bruno/Fhir/Resources/Encounter/folder.bru
new file mode 100644
index 0000000..93c7f26
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Encounter/folder.bru
@@ -0,0 +1,7 @@
+meta {
+ name: Encounter
+}
+
+docs {
+ Create examples for `Encounter`. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Observation/create-observation-invalid-quantity.bru b/eng/bruno/Fhir/Resources/Observation/create-observation-invalid-quantity.bru
new file mode 100644
index 0000000..638a7ee
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Observation/create-observation-invalid-quantity.bru
@@ -0,0 +1,43 @@
+meta {
+ name: Create Observation (invalid quantity value)
+ type: http
+ seq: 3
+}
+
+post {
+ url: {{fhirBase}}/Observation
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Observation",
+ "status": "final",
+ "code": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "8867-4"
+ }
+ ]
+ },
+ "valueQuantity": {
+ "value": "not-a-number",
+ "unit": "beats/minute"
+ }
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `valueQuantity.value` must be a number, not a string. Expect `400`.
+}
diff --git a/eng/bruno/Fhir/Resources/Observation/create-observation-invalid-status.bru b/eng/bruno/Fhir/Resources/Observation/create-observation-invalid-status.bru
new file mode 100644
index 0000000..7420e8f
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Observation/create-observation-invalid-status.bru
@@ -0,0 +1,39 @@
+meta {
+ name: Create Observation (invalid status code)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Observation
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Observation",
+ "status": "done",
+ "code": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "8867-4"
+ }
+ ]
+ }
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `status` must be an `ObservationStatus` code. Expect `400` at `Observation.status`.
+}
diff --git a/eng/bruno/Fhir/Resources/Observation/create-observation-valid.bru b/eng/bruno/Fhir/Resources/Observation/create-observation-valid.bru
new file mode 100644
index 0000000..5aef5e9
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Observation/create-observation-valid.bru
@@ -0,0 +1,49 @@
+meta {
+ name: Create Observation (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Observation
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Observation",
+ "status": "final",
+ "code": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "8867-4",
+ "display": "Heart rate"
+ }
+ ]
+ },
+ "subject": {
+ "reference": "Patient/{{patientId}}"
+ },
+ "valueQuantity": {
+ "value": 72,
+ "unit": "beats/minute",
+ "system": "http://unitsofmeasure.org",
+ "code": "/min"
+ }
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq Observation
+}
+
+docs {
+ A heart-rate observation for the patient. Expect `201`.
+}
diff --git a/eng/bruno/Fhir/Resources/Observation/folder.bru b/eng/bruno/Fhir/Resources/Observation/folder.bru
new file mode 100644
index 0000000..5416d0e
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Observation/folder.bru
@@ -0,0 +1,7 @@
+meta {
+ name: Observation
+}
+
+docs {
+ Create and search examples for `Observation`. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Observation/search-observations-for-patient.bru b/eng/bruno/Fhir/Resources/Observation/search-observations-for-patient.bru
new file mode 100644
index 0000000..0205f08
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Observation/search-observations-for-patient.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Search Observations for patient
+ type: http
+ seq: 4
+}
+
+get {
+ url: {{fhirBase}}/Observation?subject=Patient/{{patientId}}
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Expect `200` with a `searchset` `Bundle`.
+}
diff --git a/eng/bruno/Fhir/Resources/Organization/create-organization-valid.bru b/eng/bruno/Fhir/Resources/Organization/create-organization-valid.bru
new file mode 100644
index 0000000..381de12
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Organization/create-organization-valid.bru
@@ -0,0 +1,43 @@
+meta {
+ name: Create Organization (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Organization
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Organization",
+ "name": "Incendi Clinic",
+ "telecom": [
+ {
+ "system": "phone",
+ "value": "+47 22 22 22 22"
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq Organization
+}
+
+script:post-response {
+ if (res.body && res.body.id) {
+ bru.setVar("organizationId", res.body.id);
+ }
+}
+
+docs {
+ Expect `201`. Saves `organizationId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Organization/folder.bru b/eng/bruno/Fhir/Resources/Organization/folder.bru
new file mode 100644
index 0000000..14b1169
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Organization/folder.bru
@@ -0,0 +1,7 @@
+meta {
+ name: Organization
+}
+
+docs {
+ Create and search examples for `Organization`.
+}
diff --git a/eng/bruno/Fhir/Resources/Organization/search-organizations.bru b/eng/bruno/Fhir/Resources/Organization/search-organizations.bru
new file mode 100644
index 0000000..3b9cc45
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Organization/search-organizations.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Search Organizations by name
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{fhirBase}}/Organization?name=Incendi
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Expect `200` with a `searchset` `Bundle`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-garbage-json.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-garbage-json.bru
new file mode 100644
index 0000000..9a3d203
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-garbage-json.bru
@@ -0,0 +1,28 @@
+meta {
+ name: Create Patient (garbage body, JSON route)
+ type: http
+ seq: 6
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: text
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:text {
+ this is not json
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ Not JSON at all, sent as `application/fhir+json`. Expect `400` with the parser message.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-garbage-xml.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-garbage-xml.bru
new file mode 100644
index 0000000..3ad7a6f
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-garbage-xml.bru
@@ -0,0 +1,28 @@
+meta {
+ name: Create Patient (garbage body, XML route)
+ type: http
+ seq: 11
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: text
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+xml
+ Accept: application/fhir+xml
+}
+
+body:text {
+ this is not xml
+}
+
+assert {
+ res.status: eq 400
+}
+
+docs {
+ Not XML at all, sent as `application/fhir+xml`. Expect `400` with the parser message.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-invalid-gender-xml.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-invalid-gender-xml.bru
new file mode 100644
index 0000000..a988897
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-invalid-gender-xml.bru
@@ -0,0 +1,33 @@
+meta {
+ name: Create Patient (invalid gender code, XML)
+ type: http
+ seq: 9
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: xml
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+xml
+ Accept: application/fhir+xml
+}
+
+body:xml {
+
+
+
+
+
+
+}
+
+assert {
+ res.status: eq 400
+}
+
+docs {
+ Same invalid `gender` on the XML route; the `OperationOutcome` comes back as XML.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-invalid-gender.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-invalid-gender.bru
new file mode 100644
index 0000000..3842664
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-invalid-gender.bru
@@ -0,0 +1,37 @@
+meta {
+ name: Create Patient (invalid gender code)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Test"
+ }
+ ],
+ "gender": "mann"
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `gender` is bound to `AdministrativeGender`. Expect `400` with
+ `expression: ["Patient.gender"]`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-missing-resourcetype.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-missing-resourcetype.bru
new file mode 100644
index 0000000..6c2a8d8
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-missing-resourcetype.bru
@@ -0,0 +1,34 @@
+meta {
+ name: Create Patient (missing resourceType)
+ type: http
+ seq: 5
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "name": [
+ {
+ "family": "Test"
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ Valid JSON, no `resourceType`. Expect `400` with `code: structure`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-multiple-invalid-elements.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-multiple-invalid-elements.bru
new file mode 100644
index 0000000..d3a53a4
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-multiple-invalid-elements.bru
@@ -0,0 +1,34 @@
+meta {
+ name: Create Patient (multiple invalid elements)
+ type: http
+ seq: 3
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "gender": "bogus",
+ "birthDate": "not-a-date",
+ "active": true
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ Two bad elements. Expect `400` with one issue each — the parser does not stop at
+ the first.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-unknown-element.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-unknown-element.bru
new file mode 100644
index 0000000..ee6b263
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-unknown-element.bru
@@ -0,0 +1,32 @@
+meta {
+ name: Create Patient (unknown element)
+ type: http
+ seq: 4
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "favoriteColor": "blue",
+ "active": true
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `favoriteColor` is not a `Patient` element. The parser is strict, so expect `400`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-valid-xml.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-valid-xml.bru
new file mode 100644
index 0000000..926dcdb
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-valid-xml.bru
@@ -0,0 +1,36 @@
+meta {
+ name: Create Patient (valid, XML)
+ type: http
+ seq: 8
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: xml
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+xml
+ Accept: application/fhir+xml
+}
+
+body:xml {
+
+
+
+
+
+
+
+
+}
+
+assert {
+ res.status: eq 201
+}
+
+docs {
+ Happy path on the XML route. FHIR XML is order-sensitive: `name`, then `gender`,
+ then `birthDate`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-valid.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-valid.bru
new file mode 100644
index 0000000..6ce3fda
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-valid.bru
@@ -0,0 +1,49 @@
+meta {
+ name: Create Patient (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Test",
+ "given": ["Ada"]
+ }
+ ],
+ "gender": "female",
+ "birthDate": "1980-01-01",
+ "active": true
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq Patient
+}
+
+script:post-response {
+ // Stash the new id and version so the other examples can reuse them.
+ if (res.body && res.body.id) {
+ bru.setVar("patientId", res.body.id);
+ }
+ if (res.body && res.body.meta && res.body.meta.versionId) {
+ bru.setVar("patientVersionId", res.body.meta.versionId);
+ }
+}
+
+docs {
+ Happy path. Expect `201`. Saves `patientId` for the other requests.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/create-patient-wrong-element-order-xml.bru b/eng/bruno/Fhir/Resources/Patient/create-patient-wrong-element-order-xml.bru
new file mode 100644
index 0000000..379903a
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/create-patient-wrong-element-order-xml.bru
@@ -0,0 +1,33 @@
+meta {
+ name: Create Patient (wrong element order, XML)
+ type: http
+ seq: 10
+}
+
+post {
+ url: {{fhirBase}}/Patient
+ body: xml
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+xml
+ Accept: application/fhir+xml
+}
+
+body:xml {
+
+
+
+
+
+
+}
+
+assert {
+ res.status: eq 400
+}
+
+docs {
+ `gender` before `name`. Expect `400` — element order matters in XML, not in JSON.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/delete-patient.bru b/eng/bruno/Fhir/Resources/Patient/delete-patient.bru
new file mode 100644
index 0000000..fc489a1
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/delete-patient.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Delete Patient
+ type: http
+ seq: 16
+}
+
+delete {
+ url: {{fhirBase}}/Patient/{{patientId}}
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: lt 300
+}
+
+docs {
+ Deletes the patient. Run last — the other requests need `patientId`. A later read
+ returns `410 Gone`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/folder.bru b/eng/bruno/Fhir/Resources/Patient/folder.bru
new file mode 100644
index 0000000..039b771
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/folder.bru
@@ -0,0 +1,8 @@
+meta {
+ name: Patient
+}
+
+docs {
+ Success and failure bodies for `Patient`. Failures are `400` with one
+ `OperationOutcome` issue per invalid element.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/patch-patient.bru b/eng/bruno/Fhir/Resources/Patient/patch-patient.bru
new file mode 100644
index 0000000..a02c6df
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/patch-patient.bru
@@ -0,0 +1,39 @@
+meta {
+ name: Patch Patient
+ type: http
+ seq: 14
+}
+
+patch {
+ url: {{fhirBase}}/Patient/{{patientId}}
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Parameters",
+ "parameter": [
+ {
+ "name": "operation",
+ "part": [
+ { "name": "type", "valueCode": "replace" },
+ { "name": "path", "valueString": "Patient.active" },
+ { "name": "value", "valueBoolean": false }
+ ]
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 200
+}
+
+docs {
+ FHIRPath patch setting `active` to `false`. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/read-patient.bru b/eng/bruno/Fhir/Resources/Patient/read-patient.bru
new file mode 100644
index 0000000..d712840
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/read-patient.bru
@@ -0,0 +1,15 @@
+meta {
+ name: Read Patient
+ type: http
+ seq: 12
+}
+
+get {
+ url: {{fhirBase}}/Patient/{{patientId}}
+ body: none
+ auth: inherit
+}
+
+docs {
+ Reads a `Patient` by id. Needs `patientId` from **Create Patient (valid)**.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/search-patients-post.bru b/eng/bruno/Fhir/Resources/Patient/search-patients-post.bru
new file mode 100644
index 0000000..c5dc081
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/search-patients-post.bru
@@ -0,0 +1,26 @@
+meta {
+ name: Search Patients (POST)
+ type: http
+ seq: 15
+}
+
+post {
+ url: {{fhirBase}}/Patient/_search
+ body: formUrlEncoded
+ auth: inherit
+}
+
+body:form-urlencoded {
+ name: Test
+ _count: 10
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Search parameters in a form body instead of the query string. Same result as
+ **Search Patients**.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/search-patients.bru b/eng/bruno/Fhir/Resources/Patient/search-patients.bru
new file mode 100644
index 0000000..bf49a54
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/search-patients.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Search Patients
+ type: http
+ seq: 13
+}
+
+get {
+ url: {{fhirBase}}/Patient?name=Test&_count=10
+ body: none
+ auth: inherit
+}
+
+params:query {
+ name: Test
+ _count: 10
+}
+
+docs {
+ Type-level search. Expect `200` with a `searchset` `Bundle`.
+}
diff --git a/eng/bruno/Fhir/Resources/Patient/update-patient-invalid-gender.bru b/eng/bruno/Fhir/Resources/Patient/update-patient-invalid-gender.bru
new file mode 100644
index 0000000..9840e19
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Patient/update-patient-invalid-gender.bru
@@ -0,0 +1,32 @@
+meta {
+ name: Update Patient (invalid gender code)
+ type: http
+ seq: 7
+}
+
+put {
+ url: {{fhirBase}}/Patient/{{patientId}}
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "id": "{{patientId}}",
+ "gender": "bogus"
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ Per-element errors work the same on update as on create. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Practitioner/create-practitioner-invalid-gender.bru b/eng/bruno/Fhir/Resources/Practitioner/create-practitioner-invalid-gender.bru
new file mode 100644
index 0000000..ea70593
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Practitioner/create-practitioner-invalid-gender.bru
@@ -0,0 +1,36 @@
+meta {
+ name: Create Practitioner (invalid gender code)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Practitioner
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Practitioner",
+ "name": [
+ {
+ "family": "Losen"
+ }
+ ],
+ "gender": "doctor"
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ Expect `400` with `expression: ["Practitioner.gender"]`.
+}
diff --git a/eng/bruno/Fhir/Resources/Practitioner/create-practitioner-valid.bru b/eng/bruno/Fhir/Resources/Practitioner/create-practitioner-valid.bru
new file mode 100644
index 0000000..df7af90
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Practitioner/create-practitioner-valid.bru
@@ -0,0 +1,44 @@
+meta {
+ name: Create Practitioner (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Practitioner
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Practitioner",
+ "name": [
+ {
+ "family": "Losen",
+ "given": ["Leo"],
+ "prefix": ["Dr."]
+ }
+ ],
+ "gender": "female"
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq Practitioner
+}
+
+script:post-response {
+ if (res.body && res.body.id) {
+ bru.setVar("practitionerId", res.body.id);
+ }
+}
+
+docs {
+ Expect `201`. Saves `practitionerId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Practitioner/folder.bru b/eng/bruno/Fhir/Resources/Practitioner/folder.bru
new file mode 100644
index 0000000..29b6ced
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Practitioner/folder.bru
@@ -0,0 +1,7 @@
+meta {
+ name: Practitioner
+}
+
+docs {
+ Create and search examples for `Practitioner`.
+}
diff --git a/eng/bruno/Fhir/Resources/Practitioner/search-practitioners.bru b/eng/bruno/Fhir/Resources/Practitioner/search-practitioners.bru
new file mode 100644
index 0000000..9a234e2
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Practitioner/search-practitioners.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Search Practitioners by name
+ type: http
+ seq: 3
+}
+
+get {
+ url: {{fhirBase}}/Practitioner?name=Losen
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Expect `200` with a `searchset` `Bundle`.
+}
diff --git a/eng/bruno/Fhir/Resources/Questionnaire/create-questionnaire-invalid-status.bru b/eng/bruno/Fhir/Resources/Questionnaire/create-questionnaire-invalid-status.bru
new file mode 100644
index 0000000..b4925d1
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Questionnaire/create-questionnaire-invalid-status.bru
@@ -0,0 +1,38 @@
+meta {
+ name: Create Questionnaire (invalid status code)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Questionnaire
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Questionnaire",
+ "status": "published",
+ "item": [
+ {
+ "linkId": "q1",
+ "text": "Do you smoke?",
+ "type": "boolean"
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `status` must be a `PublicationStatus` code. Expect `400` at `Questionnaire.status`.
+}
diff --git a/eng/bruno/Fhir/Resources/Questionnaire/create-questionnaire-valid.bru b/eng/bruno/Fhir/Resources/Questionnaire/create-questionnaire-valid.bru
new file mode 100644
index 0000000..861d850
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Questionnaire/create-questionnaire-valid.bru
@@ -0,0 +1,50 @@
+meta {
+ name: Create Questionnaire (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Questionnaire
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Questionnaire",
+ "status": "active",
+ "title": "Intake questionnaire",
+ "item": [
+ {
+ "linkId": "q1",
+ "text": "Have you had food last 6 hours?",
+ "type": "boolean"
+ },
+ {
+ "linkId": "q2",
+ "text": "Known allergies",
+ "type": "string"
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq Questionnaire
+}
+
+script:post-response {
+ if (res.body && res.body.id) {
+ bru.setVar("questionnaireId", res.body.id);
+ }
+}
+
+docs {
+ Expect `201`. Saves `questionnaireId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Questionnaire/folder.bru b/eng/bruno/Fhir/Resources/Questionnaire/folder.bru
new file mode 100644
index 0000000..2c589d7
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Questionnaire/folder.bru
@@ -0,0 +1,7 @@
+meta {
+ name: Questionnaire
+}
+
+docs {
+ Create and read examples for `Questionnaire`.
+}
diff --git a/eng/bruno/Fhir/Resources/Questionnaire/read-questionnaire.bru b/eng/bruno/Fhir/Resources/Questionnaire/read-questionnaire.bru
new file mode 100644
index 0000000..0017c64
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Questionnaire/read-questionnaire.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Read Questionnaire
+ type: http
+ seq: 3
+}
+
+get {
+ url: {{fhirBase}}/Questionnaire/{{questionnaireId}}
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Questionnaire
+}
+
+docs {
+ Needs `questionnaireId`. Expect `200` with `meta.versionId` and an `ETag` header.
+}
diff --git a/eng/bruno/Fhir/Resources/QuestionnaireResponse/create-questionnaire-response-invalid-status.bru b/eng/bruno/Fhir/Resources/QuestionnaireResponse/create-questionnaire-response-invalid-status.bru
new file mode 100644
index 0000000..3806c5a
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/QuestionnaireResponse/create-questionnaire-response-invalid-status.bru
@@ -0,0 +1,42 @@
+meta {
+ name: Create QuestionnaireResponse (invalid status code)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/QuestionnaireResponse
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "QuestionnaireResponse",
+ "status": "done",
+ "item": [
+ {
+ "linkId": "q1",
+ "answer": [
+ {
+ "valueBoolean": true
+ }
+ ]
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `status` must be a `QuestionnaireResponseStatus` code. Expect `400` at
+ `QuestionnaireResponse.status`.
+}
diff --git a/eng/bruno/Fhir/Resources/QuestionnaireResponse/create-questionnaire-response-valid.bru b/eng/bruno/Fhir/Resources/QuestionnaireResponse/create-questionnaire-response-valid.bru
new file mode 100644
index 0000000..2208190
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/QuestionnaireResponse/create-questionnaire-response-valid.bru
@@ -0,0 +1,53 @@
+meta {
+ name: Create QuestionnaireResponse (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/QuestionnaireResponse
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "QuestionnaireResponse",
+ "status": "completed",
+ "subject": {
+ "reference": "Patient/{{patientId}}"
+ },
+ "item": [
+ {
+ "linkId": "q1",
+ "answer": [
+ {
+ "valueBoolean": false
+ }
+ ]
+ },
+ {
+ "linkId": "q2",
+ "answer": [
+ {
+ "valueString": "Penicillin"
+ }
+ ]
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq QuestionnaireResponse
+}
+
+docs {
+ Expect `201`. `questionnaire` is omitted on purpose — its type differs across FHIR
+ versions.
+}
diff --git a/eng/bruno/Fhir/Resources/QuestionnaireResponse/folder.bru b/eng/bruno/Fhir/Resources/QuestionnaireResponse/folder.bru
new file mode 100644
index 0000000..28c6f78
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/QuestionnaireResponse/folder.bru
@@ -0,0 +1,7 @@
+meta {
+ name: QuestionnaireResponse
+}
+
+docs {
+ Create examples for `QuestionnaireResponse`. Needs `questionnaireId` and `patientId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Task/create-task-invalid-status.bru b/eng/bruno/Fhir/Resources/Task/create-task-invalid-status.bru
new file mode 100644
index 0000000..98938eb
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Task/create-task-invalid-status.bru
@@ -0,0 +1,33 @@
+meta {
+ name: Create Task (invalid status code)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Task
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Task",
+ "status": "todo",
+ "intent": "order",
+ "description": "Follow up on abnormal heart rate observation"
+ }
+}
+
+assert {
+ res.status: eq 400
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ `status` must be a `TaskStatus` code. Expect `400` at `Task.status`.
+}
diff --git a/eng/bruno/Fhir/Resources/Task/create-task-valid.bru b/eng/bruno/Fhir/Resources/Task/create-task-valid.bru
new file mode 100644
index 0000000..320e534
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Task/create-task-valid.bru
@@ -0,0 +1,47 @@
+meta {
+ name: Create Task (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Task
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Task",
+ "status": "requested",
+ "intent": "order",
+ "description": "Follow up on abnormal heart rate observation",
+ "for": {
+ "reference": "Patient/{{patientId}}"
+ },
+ "owner": {
+ "reference": "Practitioner/{{practitionerId}}"
+ },
+ "authoredOn": "2026-07-22T10:00:00Z"
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq Task
+}
+
+script:post-response {
+ if (res.body && res.body.id) {
+ bru.setVar("taskId", res.body.id);
+ }
+}
+
+docs {
+ A follow-up task for the patient, owned by the practitioner. Expect `201`.
+ Saves `taskId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Task/folder.bru b/eng/bruno/Fhir/Resources/Task/folder.bru
new file mode 100644
index 0000000..f25dab9
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Task/folder.bru
@@ -0,0 +1,7 @@
+meta {
+ name: Task
+}
+
+docs {
+ Create, update and search examples for `Task`. Needs `patientId` and `practitionerId`.
+}
diff --git a/eng/bruno/Fhir/Resources/Task/search-tasks-for-patient.bru b/eng/bruno/Fhir/Resources/Task/search-tasks-for-patient.bru
new file mode 100644
index 0000000..fc5e966
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Task/search-tasks-for-patient.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Search Tasks for patient
+ type: http
+ seq: 4
+}
+
+get {
+ url: {{fhirBase}}/Task?patient=Patient/{{patientId}}
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Tasks where `Task.for` references the patient. Expect `200` with a `searchset` `Bundle`.
+}
diff --git a/eng/bruno/Fhir/Resources/Task/update-task-complete.bru b/eng/bruno/Fhir/Resources/Task/update-task-complete.bru
new file mode 100644
index 0000000..5a338df
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/Task/update-task-complete.bru
@@ -0,0 +1,42 @@
+meta {
+ name: Update Task (mark completed)
+ type: http
+ seq: 3
+}
+
+put {
+ url: {{fhirBase}}/Task/{{taskId}}
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Task",
+ "id": "{{taskId}}",
+ "status": "completed",
+ "intent": "order",
+ "description": "Follow up on abnormal heart rate observation",
+ "for": {
+ "reference": "Patient/{{patientId}}"
+ },
+ "owner": {
+ "reference": "Practitioner/{{practitionerId}}"
+ },
+ "authoredOn": "2026-07-22T10:00:00Z",
+ "lastModified": "2026-07-22T12:00:00Z"
+ }
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Task
+}
+
+docs {
+ Moves the task to `completed`. Needs `taskId`. Expect `200` with a bumped `meta.versionId`.
+}
diff --git a/eng/bruno/Fhir/Resources/folder.bru b/eng/bruno/Fhir/Resources/folder.bru
new file mode 100644
index 0000000..3886d12
--- /dev/null
+++ b/eng/bruno/Fhir/Resources/folder.bru
@@ -0,0 +1,4 @@
+meta {
+ name: Resources
+ seq: 4
+}
diff --git a/eng/bruno/Fhir/Terminology/create-valueset.bru b/eng/bruno/Fhir/Terminology/create-valueset.bru
new file mode 100644
index 0000000..1dce458
--- /dev/null
+++ b/eng/bruno/Fhir/Terminology/create-valueset.bru
@@ -0,0 +1,46 @@
+meta {
+ name: Create ValueSet (valid)
+ type: http
+ seq: 3
+}
+
+post {
+ url: {{fhirBase}}/ValueSet
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "ValueSet",
+ "url": "http://example.org/ValueSet/gender-subset",
+ "status": "active",
+ "compose": {
+ "include": [
+ {
+ "system": "http://hl7.org/fhir/administrative-gender",
+ "concept": [{ "code": "male" }, { "code": "female" }]
+ }
+ ]
+ }
+ }
+}
+
+assert {
+ res.status: eq 201
+ res.body.resourceType: eq ValueSet
+}
+
+script:post-response {
+ if (res.body && res.body.id) {
+ bru.setVar("valueSetId", res.body.id);
+ }
+}
+
+docs {
+ Expect `201`. Saves `valueSetId` for the stored-expansion requests.
+}
diff --git a/eng/bruno/Fhir/Terminology/expand-by-url.bru b/eng/bruno/Fhir/Terminology/expand-by-url.bru
new file mode 100644
index 0000000..16feb17
--- /dev/null
+++ b/eng/bruno/Fhir/Terminology/expand-by-url.bru
@@ -0,0 +1,25 @@
+meta {
+ name: Expand ValueSet by url
+ type: http
+ seq: 1
+}
+
+get {
+ url: {{fhirBase}}/ValueSet/$expand?url=http://hl7.org/fhir/ValueSet/administrative-gender
+ body: none
+ auth: inherit
+}
+
+params:query {
+ url: http://hl7.org/fhir/ValueSet/administrative-gender
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq ValueSet
+}
+
+docs {
+ Expands a canonical that resolves from the loaded packages. `filter`, `offset` and
+ `count` narrow the result.
+}
diff --git a/eng/bruno/Fhir/Terminology/expand-from-body.bru b/eng/bruno/Fhir/Terminology/expand-from-body.bru
new file mode 100644
index 0000000..4d10a70
--- /dev/null
+++ b/eng/bruno/Fhir/Terminology/expand-from-body.bru
@@ -0,0 +1,34 @@
+meta {
+ name: Expand ValueSet from body
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/ValueSet/$expand
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Parameters",
+ "parameter": [
+ { "name": "url", "valueUri": "http://hl7.org/fhir/ValueSet/administrative-gender" },
+ { "name": "filter", "valueString": "fem" }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq ValueSet
+}
+
+docs {
+ Same operation via a `Parameters` body, which also accepts an inline `valueSet`.
+}
diff --git a/eng/bruno/Fhir/Terminology/expand-stored-conflicting-url.bru b/eng/bruno/Fhir/Terminology/expand-stored-conflicting-url.bru
new file mode 100644
index 0000000..70d26e2
--- /dev/null
+++ b/eng/bruno/Fhir/Terminology/expand-stored-conflicting-url.bru
@@ -0,0 +1,33 @@
+meta {
+ name: Expand stored ValueSet (conflicting url)
+ type: http
+ seq: 5
+}
+
+post {
+ url: {{fhirBase}}/ValueSet/{{valueSetId}}/$expand
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Parameters",
+ "parameter": [
+ { "name": "url", "valueUri": "http://hl7.org/fhir/ValueSet/administrative-gender" }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 400
+}
+
+docs {
+ The id in the path already identifies the ValueSet, so `url` or `valueSet` in the
+ body is rejected. Modifier parameters like `filter` are fine.
+}
diff --git a/eng/bruno/Fhir/Terminology/expand-stored.bru b/eng/bruno/Fhir/Terminology/expand-stored.bru
new file mode 100644
index 0000000..03b1428
--- /dev/null
+++ b/eng/bruno/Fhir/Terminology/expand-stored.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Expand stored ValueSet
+ type: http
+ seq: 4
+}
+
+get {
+ url: {{fhirBase}}/ValueSet/{{valueSetId}}/$expand
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq ValueSet
+}
+
+docs {
+ Expands a ValueSet held in the store. Needs `valueSetId` from **Create ValueSet**.
+}
diff --git a/eng/bruno/Fhir/Terminology/folder.bru b/eng/bruno/Fhir/Terminology/folder.bru
new file mode 100644
index 0000000..7730410
--- /dev/null
+++ b/eng/bruno/Fhir/Terminology/folder.bru
@@ -0,0 +1,4 @@
+meta {
+ name: Terminology
+ seq: 5
+}
diff --git a/eng/bruno/Fhir/Validation/folder.bru b/eng/bruno/Fhir/Validation/folder.bru
new file mode 100644
index 0000000..093bf42
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/folder.bru
@@ -0,0 +1,4 @@
+meta {
+ name: Validation
+ seq: 6
+}
diff --git a/eng/bruno/Fhir/Validation/validate-patient-against-profile.bru b/eng/bruno/Fhir/Validation/validate-patient-against-profile.bru
new file mode 100644
index 0000000..f48bd8c
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/validate-patient-against-profile.bru
@@ -0,0 +1,38 @@
+meta {
+ name: Validate Patient against profile
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/Patient/$validate?profile=http://hl7.org/fhir/StructureDefinition/Patient
+ body: json
+ auth: inherit
+}
+
+params:query {
+ profile: http://hl7.org/fhir/StructureDefinition/Patient
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Test",
+ "given": ["Ada"]
+ }
+ ],
+ "gender": "female",
+ "birthDate": "1990-01-01"
+ }
+}
+
+docs {
+ Validates against an explicit `?profile=` canonical. `meta.profile` on the resource
+ is validated too.
+}
diff --git a/eng/bruno/Fhir/Validation/validate-patient-base-fnr-ok.bru b/eng/bruno/Fhir/Validation/validate-patient-base-fnr-ok.bru
new file mode 100644
index 0000000..a44bc9f
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/validate-patient-base-fnr-ok.bru
@@ -0,0 +1,35 @@
+meta {
+ name: Validate Patient against base (same FNR body — passes)
+ type: http
+ seq: 5
+}
+
+post {
+ url: {{fhirBase}}/Patient/$validate
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "identifier": [
+ {
+ "system": "urn:oid:2.16.578.1.12.4.1.4.1"
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ The same body against base R4, which allows a missing `Identifier.value` — passes.
+}
diff --git a/eng/bruno/Fhir/Validation/validate-patient-instance.bru b/eng/bruno/Fhir/Validation/validate-patient-instance.bru
new file mode 100644
index 0000000..3fa1a47
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/validate-patient-instance.bru
@@ -0,0 +1,38 @@
+meta {
+ name: Validate Patient (instance)
+ type: http
+ seq: 6
+}
+
+post {
+ url: {{fhirBase}}/Patient/{{patientId}}/$validate
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Test",
+ "given": ["Ada"]
+ }
+ ],
+ "gender": "female"
+ }
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq OperationOutcome
+}
+
+docs {
+ Instance-level `$validate`. The body is still what gets validated; the id only
+ addresses the endpoint. Needs `patientId`.
+}
diff --git a/eng/bruno/Fhir/Validation/validate-patient-no-basis-fnr-invalid.bru b/eng/bruno/Fhir/Validation/validate-patient-no-basis-fnr-invalid.bru
new file mode 100644
index 0000000..b76343e
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/validate-patient-no-basis-fnr-invalid.bru
@@ -0,0 +1,41 @@
+meta {
+ name: Validate Patient against no-basis (FNR without value)
+ type: http
+ seq: 4
+}
+
+post {
+ url: {{fhirBase}}/Patient/$validate?profile=http://hl7.no/fhir/StructureDefinition/no-basis-Patient
+ body: json
+ auth: inherit
+}
+
+params:query {
+ profile: http://hl7.no/fhir/StructureDefinition/no-basis-Patient
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "identifier": [
+ {
+ "system": "urn:oid:2.16.578.1.12.4.1.4.1"
+ }
+ ]
+ }
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq OperationOutcome
+ res.body.issue[0].severity: eq error
+}
+
+docs {
+ The FNR slice in no-basis-Patient requires `value`, omitted here — expect an `error`
+ on `identifier[FNR]`. Needs `hl7.fhir.no.basis` loaded on the server.
+}
diff --git a/eng/bruno/Fhir/Validation/validate-patient-unresolvable-profile.bru b/eng/bruno/Fhir/Validation/validate-patient-unresolvable-profile.bru
new file mode 100644
index 0000000..e51cd4f
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/validate-patient-unresolvable-profile.bru
@@ -0,0 +1,36 @@
+meta {
+ name: Validate Patient (unresolvable profile)
+ type: http
+ seq: 3
+}
+
+post {
+ url: {{fhirBase}}/Patient/$validate?profile=http://example.org/StructureDefinition/DoesNotExist
+ body: json
+ auth: inherit
+}
+
+params:query {
+ profile: http://example.org/StructureDefinition/DoesNotExist
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Test",
+ "given": ["Ada"]
+ }
+ ]
+ }
+}
+
+docs {
+ Unknown `?profile=` canonical. Expect `200` with an issue saying the profile could
+ not be resolved.
+}
diff --git a/eng/bruno/Fhir/Validation/validate-patient-valid.bru b/eng/bruno/Fhir/Validation/validate-patient-valid.bru
new file mode 100644
index 0000000..b7aec6c
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/validate-patient-valid.bru
@@ -0,0 +1,38 @@
+meta {
+ name: Validate Patient (valid)
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/Patient/$validate
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Test",
+ "given": ["Ada"]
+ }
+ ],
+ "gender": "female",
+ "birthDate": "1990-01-01"
+ }
+}
+
+vars:pre-request {
+ baseUrl: https://localhost:5201/
+}
+
+docs {
+ Validates against the base `Patient` type. Expect `200` with an information-only
+ `OperationOutcome`.
+}
diff --git a/eng/bruno/Fhir/Validation/validate-questionnaire-cross-version-profile.bru b/eng/bruno/Fhir/Validation/validate-questionnaire-cross-version-profile.bru
new file mode 100644
index 0000000..d998109
--- /dev/null
+++ b/eng/bruno/Fhir/Validation/validate-questionnaire-cross-version-profile.bru
@@ -0,0 +1,40 @@
+meta {
+ name: Validate Questionnaire (cross-version profile)
+ type: http
+ seq: 7
+}
+
+post {
+ url: {{fhirBase}}/Questionnaire/$validate?profile=http://hl7.org/fhir/5.0/StructureDefinition/profile-Questionnaire
+ body: json
+ auth: inherit
+}
+
+params:query {
+ profile: http://hl7.org/fhir/5.0/StructureDefinition/profile-Questionnaire
+}
+
+headers {
+ Content-Type: application/fhir+json
+}
+
+body:json {
+ {
+ "resourceType": "Questionnaire",
+ "status": "draft",
+ "title": "Cross-version validation example"
+ }
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq OperationOutcome
+ res.body.issue[0].code: eq not-supported
+}
+
+docs {
+ A cross-version profile pins an extension version the loaded packages do not carry, so
+ its schema will not compile. Expect `200` with a `not-supported` error naming the
+ missing dependency — not a `500`. These profiles are kept out of `$profiles`, so the
+ canonical has to be typed by hand. Needs `hl7.fhir.uv.xver-r5.r4` loaded.
+}
diff --git a/eng/bruno/Fhir/capability-statement.bru b/eng/bruno/Fhir/capability-statement.bru
new file mode 100644
index 0000000..ffc216d
--- /dev/null
+++ b/eng/bruno/Fhir/capability-statement.bru
@@ -0,0 +1,16 @@
+meta {
+ name: Capability statement
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{fhirBase}}/metadata
+ body: none
+ auth: none
+}
+
+docs {
+ The server's `CapabilityStatement`.
+ Anonymous — no token needed.
+}
diff --git a/eng/bruno/Fhir/folder.bru b/eng/bruno/Fhir/folder.bru
new file mode 100644
index 0000000..991b6b2
--- /dev/null
+++ b/eng/bruno/Fhir/folder.bru
@@ -0,0 +1,8 @@
+meta {
+ name: Fhir
+ seq: 2
+}
+
+docs {
+ Resource CRUD, search, operations and validation. All requests target `{{fhirBase}}`.
+}
diff --git a/eng/bruno/Ignis/Auth/get-token.bru b/eng/bruno/Ignis/Auth/get-token.bru
new file mode 100644
index 0000000..ce9f6f3
--- /dev/null
+++ b/eng/bruno/Ignis/Auth/get-token.bru
@@ -0,0 +1,33 @@
+meta {
+ name: Get token
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{tokenUrl}}
+ body: formUrlEncoded
+ auth: none
+}
+
+headers {
+ Accept: application/json
+}
+
+body:form-urlencoded {
+ grant_type: client_credentials
+ client_id: {{clientId}}
+ client_secret: {{clientSecret}}
+}
+
+script:post-response {
+ // Store the token so the collection's Bearer auth ({{accessToken}}) can use it.
+ if (res.body && res.body.access_token) {
+ bru.setVar("accessToken", res.body.access_token);
+ }
+}
+
+docs {
+ OAuth2 `client_credentials` against `/connect/token`. Stores `accessToken` for every
+ other request — run this first, and again on 401.
+}
diff --git a/eng/bruno/Ignis/Conformance/packages.bru b/eng/bruno/Ignis/Conformance/packages.bru
new file mode 100644
index 0000000..5f092fd
--- /dev/null
+++ b/eng/bruno/Ignis/Conformance/packages.bru
@@ -0,0 +1,20 @@
+meta {
+ name: Loaded packages
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{fhirBase}}/$packages
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Parameters
+}
+
+docs {
+ FHIR packages installed on the server, one `package` parameter each.
+}
diff --git a/eng/bruno/Ignis/Conformance/profiles.bru b/eng/bruno/Ignis/Conformance/profiles.bru
new file mode 100644
index 0000000..85a5797
--- /dev/null
+++ b/eng/bruno/Ignis/Conformance/profiles.bru
@@ -0,0 +1,21 @@
+meta {
+ name: Supported profiles
+ type: http
+ seq: 1
+}
+
+get {
+ url: {{fhirBase}}/StructureDefinition/$profiles
+ body: none
+ auth: inherit
+}
+
+assert {
+ res.status: eq 200
+ res.body.resourceType: eq Bundle
+}
+
+docs {
+ Resource-constraint profiles from the loaded packages, as a `searchset` `Bundle` of
+ metadata-only `StructureDefinition`s.
+}
diff --git a/eng/bruno/Ignis/Import/archive-import-limits.bru b/eng/bruno/Ignis/Import/archive-import-limits.bru
new file mode 100644
index 0000000..8b720b4
--- /dev/null
+++ b/eng/bruno/Ignis/Import/archive-import-limits.bru
@@ -0,0 +1,16 @@
+meta {
+ name: Archive import limits
+ type: http
+ seq: 1
+}
+
+get {
+ url: {{fhirBase}}/$archive-import
+ body: none
+ auth: inherit
+}
+
+docs {
+ Configured upload limits as a `Parameters` resource. Requires `operations.import`
+ and the `AllowImport` flag.
+}
diff --git a/eng/bruno/Ignis/Import/archive-import.bru b/eng/bruno/Ignis/Import/archive-import.bru
new file mode 100644
index 0000000..79572f4
--- /dev/null
+++ b/eng/bruno/Ignis/Import/archive-import.bru
@@ -0,0 +1,22 @@
+meta {
+ name: Archive import
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/$archive-import
+ body: multipartForm
+ auth: inherit
+}
+
+body:multipart-form {
+ file: @file(path/to/archive.zip)
+}
+
+docs {
+ Uploads a zip of FHIR resources for background ingestion; point the
+ `body:multipart-form` block at a real file first. Expect `202` with the operation id.
+
+ Requires `operations.import` and the `AllowImport` flag.
+}
diff --git a/eng/bruno/Ignis/Maintenance/clear-store.bru b/eng/bruno/Ignis/Maintenance/clear-store.bru
new file mode 100644
index 0000000..cf58ac7
--- /dev/null
+++ b/eng/bruno/Ignis/Maintenance/clear-store.bru
@@ -0,0 +1,17 @@
+meta {
+ name: Clear store (destructive)
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{fhirBase}}/$clear-store
+ body: none
+ auth: inherit
+}
+
+docs {
+ ⚠️ DESTRUCTIVE — wipes the entire FHIR store. Dev only.
+
+ Requires `maintenance/database.destructive` and the `AllowClearStore` flag.
+}
diff --git a/eng/bruno/Ignis/Maintenance/rebuild-index.bru b/eng/bruno/Ignis/Maintenance/rebuild-index.bru
new file mode 100644
index 0000000..33324f6
--- /dev/null
+++ b/eng/bruno/Ignis/Maintenance/rebuild-index.bru
@@ -0,0 +1,16 @@
+meta {
+ name: Rebuild index
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{fhirBase}}/$rebuild-index
+ body: none
+ auth: inherit
+}
+
+docs {
+ Rebuilds the search index from store contents. Requires the
+ `maintenance/database.write` scope — see README → Scopes.
+}
diff --git a/eng/bruno/Ignis/folder.bru b/eng/bruno/Ignis/folder.bru
new file mode 100644
index 0000000..cc182ef
--- /dev/null
+++ b/eng/bruno/Ignis/folder.bru
@@ -0,0 +1,9 @@
+meta {
+ name: Ignis
+ seq: 1
+}
+
+docs {
+ Ignis-specific endpoints: auth, loaded profiles and packages, archive import,
+ maintenance and health.
+}
diff --git a/eng/bruno/Ignis/health.bru b/eng/bruno/Ignis/health.bru
new file mode 100644
index 0000000..4e5e5ca
--- /dev/null
+++ b/eng/bruno/Ignis/health.bru
@@ -0,0 +1,19 @@
+meta {
+ name: Health
+ type: http
+ seq: 1
+}
+
+get {
+ url: {{baseUrl}}/healthz
+ body: none
+ auth: none
+}
+
+vars:pre-request {
+ baseUrl: https://localhost:5201
+}
+
+docs {
+ Liveness probe. Anonymous — returns `ok`.
+}
diff --git a/eng/bruno/README.md b/eng/bruno/README.md
new file mode 100644
index 0000000..a91a136
--- /dev/null
+++ b/eng/bruno/README.md
@@ -0,0 +1,38 @@
+# Ignis FHIR server — Bruno collection
+
+[Bruno](https://www.usebruno.com/) requests for poking at a running Ignis FHIR
+API by hand.
+
+## Setup
+
+1. **Open Collection** → this `eng/bruno` folder, then select the
+ **ignis-local** environment.
+2. Fill in `clientSecret` in the environment editor. It's a Bruno _secret
+ variable_ (never committed); the dev value matches `ignis-client` in
+ `appsettings.json`.
+3. Disable TLS verification for the collection (self-signed dev cert).
+4. Run the API, then run **Auth → Get token**: it stores `accessToken`, which
+ every request sends as a Bearer header. Re-run it on 401.
+
+## Scoped operations
+
+Most requests need no scope. Exceptions:
+
+| Request | Scope | Also needs |
+| ---------------------------------- | ---------------------------------- | ---------------------- |
+| Maintenance → Rebuild index | `maintenance/database.write` | — |
+| Maintenance → Clear store | `maintenance/database.destructive` | `AllowClearStore` flag |
+| Import → Archive import (+ limits) | `operations.import` | `AllowImport` flag |
+
+The default token has no scopes, so these return `403`. To fix: add the scopes
+to the `ignis-client` entry's `AllowedScopes` in `appsettings.local.json`
+(otherwise the token request fails with `invalid_scope`), then add a
+space-separated `scope` field to the **Get token** body and re-run it. A missing
+feature flag returns `404`/`503`.
+
+## Gotchas
+
+- Keep `baseUrl` and `tokenUrl` on the same `https://localhost:5201` origin — an
+ http→https redirect drops the `Authorization` header (→ 401).
+- Bruno's built-in collection OAuth2 didn't attach the token reliably, hence the
+ explicit Get token request + Bearer variable.
diff --git a/eng/bruno/bruno.json b/eng/bruno/bruno.json
new file mode 100644
index 0000000..f117733
--- /dev/null
+++ b/eng/bruno/bruno.json
@@ -0,0 +1,6 @@
+{
+ "version": "1",
+ "name": "Ignis FHIR server",
+ "type": "collection",
+ "ignore": ["node_modules", ".git"]
+}
diff --git a/eng/bruno/collection.bru b/eng/bruno/collection.bru
new file mode 100644
index 0000000..ca0fb8f
--- /dev/null
+++ b/eng/bruno/collection.bru
@@ -0,0 +1,18 @@
+headers {
+ Accept: application/fhir+json
+}
+
+auth {
+ mode: bearer
+}
+
+auth:bearer {
+ token: {{accessToken}}
+}
+
+docs {
+ # Ignis FHIR API
+
+ Select the **ignis-local** environment, then run **Ignis → Auth → Get token** first —
+ every request inherits its Bearer token. Setup, scopes and TLS: see the README.
+}
diff --git a/eng/bruno/environments/hapi-public.bru b/eng/bruno/environments/hapi-public.bru
new file mode 100644
index 0000000..ef4cfcb
--- /dev/null
+++ b/eng/bruno/environments/hapi-public.bru
@@ -0,0 +1,4 @@
+vars {
+ baseUrl: https://hapi.fhir.org
+ fhirBase: https://hapi.fhir.org/baseR4
+}
diff --git a/eng/bruno/environments/ignis-local.bru b/eng/bruno/environments/ignis-local.bru
new file mode 100644
index 0000000..193841a
--- /dev/null
+++ b/eng/bruno/environments/ignis-local.bru
@@ -0,0 +1,9 @@
+vars {
+ baseUrl: https://localhost:5201
+ fhirBase: https://localhost:5201/fhir
+ tokenUrl: https://localhost:5201/connect/token
+ clientId: ignis-client
+}
+vars:secret [
+ clientSecret
+]
diff --git a/eng/bruno/environments/ignis-public.bru b/eng/bruno/environments/ignis-public.bru
new file mode 100644
index 0000000..05e5d92
--- /dev/null
+++ b/eng/bruno/environments/ignis-public.bru
@@ -0,0 +1,9 @@
+vars {
+ baseUrl: https://ignis.incendi.no
+ fhirBase: https://ignis.incendi.no/fhir
+ tokenUrl: https://ignis.incendi.no/connect/token
+ clientId: ignis-client
+}
+vars:secret [
+ clientSecret
+]
diff --git a/eng/bruno/environments/spark-local.bru b/eng/bruno/environments/spark-local.bru
new file mode 100644
index 0000000..3c9a2a5
--- /dev/null
+++ b/eng/bruno/environments/spark-local.bru
@@ -0,0 +1,4 @@
+vars {
+ baseUrl: https://localhost:5001
+ fhirBase: https://localhost:5001/fhir
+}
diff --git a/eng/bruno/environments/spark-public.bru b/eng/bruno/environments/spark-public.bru
new file mode 100644
index 0000000..d9df64d
--- /dev/null
+++ b/eng/bruno/environments/spark-public.bru
@@ -0,0 +1,4 @@
+vars {
+ baseUrl: https://spark.incendi.no
+ fhirBase: https://spark.incendi.no/fhir
+}