FHIR R4 ingest
Partners write Observations, DiagnosticReports and Immunizations into a patient record over FHIR R4. Every write passes three gates, and a transaction Bundle is stored whole or not at all.
The flow
Scroll through the steps below the diagram, or hover a step, to highlight it in the diagram.
- POST /api/fhir/R4 Bundle A Bundle of type transaction with a bearer token. Single resources post to /Observation, /DiagnosticReport or /Immunization.
- Verify JWT and scope The token signature and scopes are checked; each entry needs the scope for its resource type.
- Gate 1: facility from token The writing facility is taken from the verified token only, never from the payload.
- Gate 2: existing Health ID The patient must resolve exactly to an existing Health ID. A partner never creates one.
- Gate 3: active consent grant The patient must hold an active grant to that facility. No grant, no write.
- Store every entry, or none All entries are written in one database transaction; any refusal rolls back the lot.
- 200 transaction-response Each entry reports 201 Created. A single-resource POST returns 201.
- Refusal: OperationOutcome Any failed gate returns a FHIR OperationOutcome and nothing is stored.
How this diagram is verified 21 of 21 checks pass
The picture, the steps, the PDF and the JSON are generated from one specification. An automated test proves the graph and checks every reference below against the running application.
- Every card has a unique id
- Every card has a known kind
- Every arrow joins two existing cards
- Exactly one starting point (a person, device or partner)
- Every card is reachable from the start
- Every card leads to an outcome (no dead ends)
- No cycles except declared retry loops
- Every numbered step follows real arrows
- Outcomes end the flow
- No two cards share a grid cell
- Every card names its evidence
- Every route, class, method and setting exists
- Every icon is from the Lucide set
- Every icon fits its card kind
- Every label exists in English and French
- Every label fits its card
- No arrow crosses a card
- No two unrelated arrows run on top of each other
- No arrow label covers a card or another label
- Every arrowhead is visible
- Connected cards of different kinds never share a colour
| Card | Evidence in the code |
|---|---|
| Partner system | uri:POST api/fhir/R4 uri:GET api/fhir/R4/metadata |
| Verify JWT and scope | method:App\Services\Fhir\FhirIngestGuard::scopes method:App\Services\Fhir\FhirIngestGuard::requireScope |
| Gate 1: facility from token | method:App\Services\Fhir\FhirIngestGuard::facilityId |
| Gate 2: existing Health ID | method:App\Services\Fhir\FhirIngestGuard::resolveSubject |
| Refusal: OperationOutcome | method:App\Services\Fhir\FhirIngestGuard::recordRefusal |
| 200 transaction- response | method:App\Services\Fhir\FhirIngestGuard::recordAccepted uri:POST api/fhir/R4/Observation |
| Store every entry, or none | method:App\Http\Controllers\Api\Fhir\FhirIngestController::transaction class:App\Services\Fhir\FhirIngestService |
| Gate 3: active consent grant | method:App\Services\Fhir\FhirIngestGuard::requireConsent class:App\Models\ConsentGrant |
What this guarantees
- The facility a write is attributed to cannot be spoofed from the payload.
- Partners can add to a record they have consent for; they cannot create patients.
- A transaction Bundle is atomic: partial writes never happen.
Where it lives in the code
uri:POST api/fhir/R4uri:GET api/fhir/R4/metadatamethod:App\Services\Fhir\FhirIngestGuard::scopesmethod:App\Services\Fhir\FhirIngestGuard::requireScopemethod:App\Services\Fhir\FhirIngestGuard::facilityIdmethod:App\Services\Fhir\FhirIngestGuard::resolveSubjectmethod:App\Services\Fhir\FhirIngestGuard::recordRefusalmethod:App\Services\Fhir\FhirIngestGuard::recordAccepteduri:POST api/fhir/R4/Observationmethod:App\Http\Controllers\Api\Fhir\FhirIngestController::transactionclass:App\Services\Fhir\FhirIngestServicemethod:App\Services\Fhir\FhirIngestGuard::requireConsentclass:App\Models\ConsentGrant