RESTful APIs
Note
Swagger API overview is available on base openFHIR context. If one is set up locally on port 8080 without and context, this would be http://localhost:8080. For sandbox example see https://sandbox.open-fhir.com
Overview
openFHIR Engine is a standalone component that exposes FHIR Operations-framework endpoints for mapping (/$tofhir and /$toopenehr,
as defined by the FHIRconnect REST API specification, plus the legacy /openfhir/tofhir and /openfhir/toopenehr)
and 3 restful APIs for state configuration (/opt, /fc/context and /fc/model). Additional APIs are exposed for Terminology and Multitenancy.
FHIRconnect operations
Once operational templates, context mappers and model mappers are correctly set up (i.e. state of the engine has been configured), you can
run your mappings by invoking the FHIR operations. Requests and responses use the R4 FHIR envelope (application/fhir+json);
errors are returned as an OperationOutcome.
- POST /$tofhir
Map an openEHR Composition to FHIR.
Body is a FHIR (R4)
Parametersresource with acompositionparameter (stringified openEHR Composition, flat or canonical), an optionaltemplateIdparameter (required when the composition is flat) and an optional nestedcontextparameter (parts:ehr_id,patient,who,onBehalfOf). Short context fields may also be passed as query parameters; the body takes precedence on conflict.Example request:
POST /$tofhir?templateId=Growth%20chart HTTP/1.1 Content-Type: application/fhir+json Body: { "resourceType": "Parameters", "parameter": [ { "name": "composition", "valueString": "<stringified flat or canonical composition>" } ] }
- Query Parameters:
templateId – Required when the composition is flat (may alternatively be passed as a
templateIdparameter in the body).
The response is a FHIR Bundle with the mapped resources, an engine-generated
Provenanceentry and, when some elements could not be mapped, anOperationOutcomeentry with warnings. The same entry carries anerrorissue for every mapping whose execution failed (see Error responses below); the other mappings are still present in the Bundle.
- POST /$toopenehr
Map a FHIR Bundle to an openEHR Composition.
Body is the FHIR Bundle itself (no
Parameterswrapper; a single resource is also accepted).Example request:
POST /$toopenehr?templateId=Growth%20chart&format=flat HTTP/1.1 Content-Type: application/fhir+json Body: <fhir bundle>
- Query Parameters:
templateId – Template id used to select the mapping context; when omitted, the engine tries to deduce it from the available contexts.
format –
canonical(default) orflat.
The response is a FHIR (R4)
Parametersresource with acompositionparameter (valueString) and, when gaps were reported during mapping, anoutcomeparameter holding anOperationOutcome— a partial result may coexist with issues. Besides warnings, the outcome carries anerrorissue for every mapping whose execution failed (see Error responses below).
Error responses
Failures are returned as an OperationOutcome. The status code tells you whether the request is yours to correct:
400— the request or the engine state it refers to needs correcting. This covers a malformed body, a missingtemplateIdfor a flat composition, no context mapper matching the input, and an operational template that is referenced by a context mapper but has never been uploaded. In the last case thediagnosticsnames the template, so a context mapper pointing atGrowth chart1whenGrowth chartis loaded is reported as such rather than as a server error.500— the engine failed. Either the stored operational template no longer parses (diagnosticsnames it), or an unexpected error occurred, in which casediagnosticscarries a reference id to quote when reporting the problem and the detail is written to the engine log rather than returned.
Input that maps to nothing is not an error: if the payload contains none of the resource type the template starts from, the response is a success with an empty result and a warning explaining that nothing was mapped.
A failure inside a single mapping does not fail the request either. The engine records it, carries on with the
remaining mappings and returns the partial result with a 200; the OperationOutcome (the Bundle entry for
$tofhir, the outcome parameter for $toopenehr) then holds an issue with severity error whose
diagnostics name the model mapper, archetype, mapping and the openEHR/FHIR paths being processed, so the failing
mapping can be located without reproducing the request. The issue code tells you whose problem it is:
processingorstructure— the cause is yours to correct (an unparseable value in the input, an argument the mapper feeds the engine that it rejects); the cause’s message is includedexception— the engine failed;diagnosticscarry the exception’s class name and a reference id, and the detail is written to the engine log under that reference
A FHIRPath expression in a model mapping that cannot be evaluated against the input (an unknown function, a malformed
condition path, a resolve() that finds nothing) is not an error but a warning with code incomplete naming
the mapping, the expression and the FHIRPath engine’s message; that mapping is skipped and the rest is mapped.
OperationOutcome verbosity
An input that skips many elements can produce many warnings, and every one of them lands in the response. How much
the OperationOutcome carries is configurable:
Environment variable |
Default |
Description |
|---|---|---|
|
|
|
Issues below the configured level are never collected, so the payload does not grow with them. The setting does not
change what the engine logs, and it does not apply to the error responses above (a 400 / 500 is always an
OperationOutcome).
Mappings (legacy)
The following endpoints predate the FHIRconnect operations above and remain available:
- POST /openfhir/tofhir
Map an openEHR Composition to a FHIR Resource.
Body can either be a FLAT representation of the openEHR Composition or a Canonical JSON.
FLAT examples: https://github.com/ehrbase/openEHR_SDK/tree/develop/test-data/src/main/resources/composition/flat/simSDT/conformance
Canonical JSON examples: https://github.com/ehrbase/openEHR_SDK/tree/develop/test-data/src/main/resources/composition/canonical_json
Example request:
POST /openfhir/tofhir HTTP/1.1 Content-Type: application/json Body: <canonical json>
POST /openfhir/tofhir?templateId=dataset_poc_rso-zl_acp HTTP/1.1 Content-Type: application/json Body: <flat json>
- Query Parameters:
templateId – If an engine can not deduce a template it (i.e. if body is NOT a canonical json), then you need to provide a template id in a query parameter. When body of the request is a canonical json, this information is present within the canonical json and a templateId query param is not required.
Response is a FHIR Resource.
- POST /openfhir/toopenehr
Map a FHIR Resource to an openEHR Composition.
Body needs to be a valid FHIR Resource in a JSON format.
Response is an openEHR Composition in either a FLAT format or a Canonical JSON.
Example request:
POST /openfhir/toopenehr HTTP/1.1 Content-Type: application/json Body: <fhir resource>
- Query Parameters:
flat – If you’d like to receive response in a FLAT format instead of a canonical JSON. default is false.
templateId – For the purpose of using the correct context for mapping, the engine needs to know a template id. Template id can either come as an input query parameter or it can be deduced based on all available context mappings. If no templateId is provided in the request, the engine will check all available contexts (that were persisted as part of the state configuration) and try to find one that is able to map the incoming FHIR Resource. If more than 1 is available, it will log a message and take the first one it finds.
State configuration
Note
An alternative state configuration option is via a Bootstrapping mechanism. See more at: State configuration
Context mappers
Context mappers are yaml files representing context on which a mapping takes place. See more at FHIR Connect. Structure needs to comply to a schema available at https://github.com/better-care/fhir-connect-mapping-spec.
Body when creating/updating a context mapper is plain text, directly yaml itself.
- POST /fc/context
Create a new context mapper.
Example request:
POST /fc/context HTTP/1.1 Content-Type: application/x-yaml Body: grammar: FHIRConnect/v0.0.1 type: context metadata: name: "Growth chart" version: 1.0.0 spec: system: FHIR version: R4 context: profile: url: "Observation" template: id: "Growth chart" archetypes: - "openEHR-EHR-OBSERVATION.body_weight.v2" - "openEHR-EHR-OBSERVATION.height.v2" start: "openEHR-EHR-OBSERVATION.body_weight.v2"
- PUT /fc/context/(uuid: context_mapper_unique_id)
Update an existing context mapper.
Example request:
PUT /fc/context/6b7fe776-e4de-49ce-849e-406b280e5338 HTTP/1.1 Content-Type: text/plain Body: grammar: FHIRConnect/v0.0.1 type: context metadata: name: "Growth chart" version: 1.0.0 spec: system: FHIR version: R4 context: profile: url: "Observation" template: id: "Growth chart" archetypes: - "openEHR-EHR-OBSERVATION.body_weight.v2" - "openEHR-EHR-OBSERVATION.height.v2" start: "openEHR-EHR-OBSERVATION.body_weight.v2"
Model mappers
Model mappers are yaml files representing model mappings. See more at FHIR Connect. Structure needs to comply to a schema available at https://github.com/better-care/fhir-connect-mapping-spec.
Body when creating/updating a model mapper is plain text, directly yaml itself.
- POST /fc/model
Create a new model mapper.
Example request:
POST /fc/model HTTP/1.1 Content-Type: text/plain Body: grammar: FHIRConnect/v0.0.1 type: model metadata: name: "openEHR-EHR-OBSERVATION.body_weight.v2" version: 1.0.0 spec: system: FHIR version: R4 openEhrConfig: archetype: "openEHR-EHR-OBSERVATION.body_weight.v2" fhirConfig: structureDefinition: http://hl7.org/fhir/StructureDefinition/Observation condition: - targetRoot: "$resource" targetAttribute: "code.coding.code" operator: "one of" criteria: "29463-7" mappings: - name: "weight" with: fhir: "$resource.value" openehr: "$archetype/data[at0002]/events[at0003]/data[at0001]/items[at0004]" type: "QUANTITY" fhirCondition: targetRoot: "$resource" targetAttribute: "code.coding.code" operator: "one of" criteria: "[$loinc.29463-7, $snomed.27113001]" - name: "time" with: fhir: "$resource.effective" openehr: "$archetype/data[at0002]/events[at0003]/time" type: "DATETIME" - name: "comment" with: fhir: "$resource.note.text" openehr: "$archetype/data[at0002]/events[at0003]/data[at0001]/items[at0024]" type: "STRING"
- PUT /fc/model/(uuid: model_mapper_unique_id)
Update an existing model mapper.
Example request:
PUT /fc/model/9ae2278a-c02b-4165-96fc-79dd91495db3 HTTP/1.1 Content-Type: text/plain Body: grammar: FHIRConnect/v0.0.1 type: model metadata: name: "openEHR-EHR-OBSERVATION.body_weight.v2" version: 1.0.0 spec: system: FHIR version: R4 openEhrConfig: archetype: "openEHR-EHR-OBSERVATION.body_weight.v2" fhirConfig: structureDefinition: http://hl7.org/fhir/StructureDefinition/Observation condition: - targetRoot: "$resource" targetAttribute: "code.coding.code" operator: "one of" criteria: "29463-7" mappings: - name: "weight" with: fhir: "$resource.value" openehr: "$archetype/data[at0002]/events[at0003]/data[at0001]/items[at0004]" type: "QUANTITY" fhirCondition: targetRoot: "$resource" targetAttribute: "code.coding.code" operator: "one of" criteria: "[$loinc.29463-7, $snomed.27113001]" - name: "time" with: fhir: "$resource.effective" openehr: "$archetype/data[at0002]/events[at0003]/time" type: "DATETIME" - name: "comment" with: fhir: "$resource.note.text" openehr: "$archetype/data[at0002]/events[at0003]/data[at0001]/items[at0024]" type: "STRING"
Operational Templates
If openFHIR Engine is not integrated (see Integrations) with an existing openEHR repository where it’s fetching/searching templates for, then they need to exist within the database of the engine itself.
Note
When the engine is integrated with an openEHR CDR (OPENFHIR_CDR_ENABLED=true), templates are owned by the CDR and the endpoints
below change behaviour: POST, PUT and DELETE return 409 Conflict, GET /opt returns an empty array and
GET /opt/{id} returns 404. Load templates into the CDR directly instead. See Integrations.
- POST /opt
Create a new operational template
Example request:
POST /opt HTTP/1.1 Content-Type: application/xml Body: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <template xmlns="http://schemas.openehr.org/v1"> <language> <terminology_id> <value>ISO_639-1</value> </terminology_id> <code_string>en</code_string> </language> <description> <original_author id="date">2020-09-17</original_author> <original_author id="name">test</original_author>....
- PUT /opt/(uuid: opt_unique_id)
Update an existing operational template
Example request:
PUT /opt/d07ca1b7-0d1b-4aac-82b4-cb76b9dd55e0 HTTP/1.1 Content-Type: application/xml Body: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <template xmlns="http://schemas.openehr.org/v1"> <language> <terminology_id> <value>ISO_639-1</value> </terminology_id> <code_string>en</code_string> </language> <description> <original_author id="date">2020-09-17</original_author> <original_author id="name">test</original_author>....
- GET /opt/(uuid: opt_unique_id)
Read an existing operational template
Example request:
GET /opt/d07ca1b7-0d1b-4aac-82b4-cb76b9dd55e0 HTTP/1.1 Accept: application/xml
- POST /opt/$refresh
Evict a cached operational template so that it is re-fetched from the openEHR CDR on the next mapping request. Only meaningful when the engine is integrated with a CDR (see Integrations); returns
409 Conflictotherwise, where templates are evicted automatically when they are upserted. The eviction is broadcast to all nodes in the cluster. Returns204 No Contenton success.Example request:
POST /opt/$refresh?templateId=Vital%20Signs.v1 HTTP/1.1
Bootstrap
State configuration that was applied from the bootstrap directory can be inspected and re-applied at runtime. See more at State configuration.
- POST /$bootstrap
Re-run the bootstrap directory scan, without restarting the engine. Files not seen before are created, files whose content changed since they were last bootstrapped are updated in place (keeping the id of the entity they originally created), and unchanged files are skipped. Files that were bootstrapped before but are no longer on disk are reported in the log; their entities are left untouched.
Example request:
POST /$bootstrap HTTP/1.1 Accept: application/json
Example response:
HTTP/1.1 200 OK Content-Type: application/json Body: { "created": 1, "updated": 1, "unchanged": 1, "failed": 0, "files": [ { "path": "growth-chart.context.yaml", "outcome": "CREATED", "entityType": "CONTEXT", "entityId": "6b7fe776-e4de-49ce-849e-406b280e5338" }, { "path": "nested/body-weight.model.yaml", "outcome": "UPDATED", "entityType": "MODEL", "entityId": "8c1f0a12-2a44-4f0e-9a1c-2b3d4e5f6071" }, { "path": "GrowthChart.opt", "outcome": "UNCHANGED", "entityType": "OPT", "entityId": "d07ca1b7-0d1b-4aac-82b4-cb76b9dd55e0" } ] }
- Status Codes:
200 OK – Summary of the bootstrap run
409 Conflict – A bootstrap run is already in progress
- GET /bootstrap
Lists the bootstrap ledger of the logged-in user: one entry per file that has been bootstrapped so far, recording the file’s path relative to the bootstrap directory, a hash of the content that was applied, and the id and type of the entity it created.
Example request:
GET /bootstrap HTTP/1.1 Accept: application/json
Mapping Insights
Note
See more about mappings insights: Mapping insights.
- POST /openfhir/insights
Get all mapping insights that suit provided filter
Example request:
POST /openfhir/insights HTTP/1.1 Content-Type: application/json Body: { "requestId": "456", "direction": "TO_FHIR" }
- GET /openfhir/insights/payload/{payloadId}
Get specific payload of a mapping insight
Example request:
GET /openfhir/insights/payload/123 HTTP/1.1 Accept: application/json
Status
- GET /status
Diagnostic overview of a running engine. Unauthenticated.
Example request:
GET /status HTTP/1.1 Accept: application/json
The response reports:
engineVersion/openFhirVersion— the versions of the engine and of theopen-fhir-coreit runs on,properties— every configuration property the engine is running on, so you can verify that a setting was picked up,cache— entry counts and configured TTLs of thefhirConnect,optandtenantcaches (see Caching),cdr— whether templates are resolved from an openEHR CDR and whether it answers (see Integrations),messaging— whether queue support is enabled and, per subscription, itsstate(STARTING,RUNNING,FAILED,STOPPED) and the number of messages this nodeprocessed, published aspartialanddeadLetteredsince it started. With the feature off it is{"enabled": false}.
Because the endpoint is unauthenticated, secrets among the
propertiesare masked as******: the value of every property whose name containspassword,secret,jaas,credential,passphrase,private-key,api-keyortoken(other than atoken-url) or ends inkey, and the password of credentials embedded in a URI (mongodb://user:******@host/db) — the rest of the URI stays readable.