Easy-to-use JSON-based REST API for OpenEMR FHIR. All code is done in classes and separate from the view to help with codebase modernization efforts. See standard OpenEMR API docs here
FHIR endpoints are defined in the primary routes file. The routes file maps an external, addressable endpoint to the OpenEMR FHIR controller which handles the request, and also handles the JSON data conversions.
"GET /fhir/Patient" => function () {
RestConfig::authorization_check("patients", "demo");
$return = (new FhirPatientRestController())->getAll($_GET);
RestConfig::apiLog($return);
return $return;
}At a high level, the request processing flow consists of the following steps:
JSON Request -> FHIR Controller Component -> FHIR Validation -> Parsing FHIR Resource -> Standard Service Component -> Validation -> Database
The logical response flow begins with the database result:
Database Result -> Service Component -> FHIR Service Component -> Parse OpenEMR Record -> FHIR Controller Component -> RequestControllerHelper -> JSON Response
Enable the Standard FHIR service (/fhir/ endpoints) in OpenEMR menu: Administration->Globals->Connectors->"Enable OpenEMR Standard FHIR REST API"
There are several ways to make API calls from an authorized session and maintain security:
- See the script at tests/api/InternalApiTest.php for examples of internal API use cases.
Multisite is supported by including the site in the endpoint. When not using multisite or using the default multisite site, then a typical path would look like apis/default/fhir/patient. If you were using multisite and using a site called alternate, then the path would look like apis/alternate/fhir/patient.
OpenEMR uses OIDC compliant authorization for API. SSL is required and setting baseurl at Administration->Globals->Connectors->'Site Address (required for OAuth2 and FHIR)' is required.
See Authorization for more details.
Standard FHIR endpoints Use http://localhost:8300/apis/default/fhir as base URI.
Note that the default component can be changed to the name of the site when using OpenEMR's multisite feature.
Example: http://localhost:8300/apis/default/fhir/Patient returns a Patient's bundle resource, etc
The Bearer token is required for each OpenEMR FHIR request (except for the Capability Statement), and is conveyed using an Authorization header. Note that the Bearer token is the access_token that is obtained in the Authorization section.
Request:
curl -X GET 'http://localhost:8300/apis/fhir/Patient' \
-H 'Authorization: Bearer eyJ0b2tlbiI6IjAwNnZ3eGJZYmFrOXlxUjF4U290Y1g4QVVDd3JOcG5yYXZEaFlqaHFjWXJXRGNDQUtFZmJONkh2cElTVkJiaWFobHBqOTBYZmlNRXpiY2FtU01pSHk1UzFlMmgxNmVqZEhcL1ZENlNtaVpTRFRLMmtsWDIyOFRKZzNhQmxMdUloZmNJM3FpMGFKZ003OXdtOGhYT3dpVkx5b3BFRXQ1TlNYNTE3UW5TZ0dsUVdQbG56WjVxOVYwc21tdDlSQ3RvcDV3TEkiLCJzaXRlX2lkIjoiZGVmYXVsdCIsImFwaSI6ImZoaXIifQ=='This will return the Capability Statement.
curl -X GET 'http://localhost:8300/apis/default/fhir/metadata'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Patient'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Patient/90a8923c-0b1c-4d0a-9981-994b143381a7'- Supported Search Parameters
- address
- address-city
- address-postalcode
- address-state
- birthdate
- family
- gender
- given
- name
- phone
- telecom
Request:
curl -X POST -H 'Content-Type: application/fhir+json' 'http://localhost:8300/apis/default/fhir/Patient' -d \
'{
"resourceType": "Patient",
"identifier": [ { "system": "urn:oid:1.2.36.146.595.217.0.1", "value": "12345" } ],
"name": [ {
"family": "Chalmers",
"given": [ "Peter", "James" ]
} ],
"gender": "male",
"birthDate": "1974-12-25"
}'Request:
curl -X PUT -H 'Content-Type: application/fhir+json' 'http://localhost:8300/apis/default/fhir/Patient/90a8923c-0b1c-4d0a-9981-994b143381a7' -d \
'{
"resourceType": "Patient",
"id": "1",
"identifier": [ { "system": "urn:oid:1.2.36.146.595.217.0.1", "value": "12345" } ],
"name": [ {
"family": "Chalmers",
"given": [ "Peter", "James" ]
} ],
"gender": "male",
"birthDate": "1974-01-13",
"address": [ {
"line": [ "534 Erewhon St" ],
"city": "PleasantVille",
"state": "Vic",
"postalCode": "3999"
} ]
}'Request:
curl -X PUT -H 'Content-Type: application/fhir+json' 'http://localhost:8300/apis/default/fhir/Patient/90a8923c-0b1c-4d0a-9981-994b143381a7' -d \
'[
{
"op": "replace",
"path": "/address/0/postalCode",
"value": "M5C 2X8"
},
{
"op": "replace",
"path": "/birthDate",
"value": "1974-02-13"
}
]'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Coverage'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Coverage/926c051b-9eaf-4753-b5d3-65c3f329e656'- Supported Search Parameters
- _id
- patient
- payor
Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Encounter'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Encounter/90c196f2-51cc-4655-8858-3a80aebff3ef'- Supported Search Parameters
- _id
- patient
- date {gt|lt|ge|le}
Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Practitioner'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Practitioner/90a8923c-0b1c-4d0a-9981-994b143381a7'- Supported Search Parameters
- address
- address-city
- address-postalcode
- address-state
- active
- family
- given
- name
- phone
- telecom
Request:
curl -X POST -H 'Content-Type: application/fhir+json' 'http://localhost:8300/apis/default/fhir/Practitioner' -d \
'{
"resourceType": "Practitioner",
"identifier": [ { "system": "http://hl7.org/fhir/sid/us-npi", "value": "1122334499" } ],
"name": [ {
"use": "official",
"family": "Chalmers",
"given": [ "Peter", "James" ]
} ]
}'Request:
curl -X PUT -H 'Content-Type: application/fhir+json' 'http://localhost:8300/apis/default/fhir/Practitioner/90a8923c-0b1c-4d0a-9981-994b143381a7' -d \
'{
"resourceType": "Practitioner",
"identifier": [ { "system": "http://hl7.org/fhir/sid/us-npi", "value": "1155667799" } ],
"name": [ {
"use": "official",
"family": "Theil",
"given": [ "Katy", "Wilson" ]
} ],
"address": [ {
"line": [ "534 Erewhon St" ],
"city": "PleasantVille",
"state": "Vic",
"postalCode": "3999"
} ]
}'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/PractitionerRole'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/PractitionerRole/90de091a-91e9-4bbe-9a81-75ed623f65bf'- Supported Search Parameters
- speciality
- practitioner
Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Immunization'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Immunization/90feaaa2-4097-4437-966e-c425d1958dd6'- Supported Search Parameters
- patient
Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/AllergyIntolerance'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/AllergyIntolerance/90feaaa2-4097-4437-966e-c425d1958dd6'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Organization'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Organization/1'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Observation'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Observation/9150635b-0705-4a27-8820-df8b56cf07eb'Request:
curl -X POST -H 'Content-Type: application/fhir+json' 'http://localhost:8300/apis/default/fhir/QuestionnaireResponse' -d \
'{
"resourceType": "QuestionnaireResponse",
"id": "697485",
"meta": {
"versionId": "1",
"lastUpdated": "2020-03-22T09:11:45.181+00:00",
"source": "#L0otRLyoImuOVD2S"
},
"status": "completed",
"item": [ {
"linkId": "1",
"text": "Do you have allergies?"
}, {
"linkId": "2",
"text": "General questions",
"item": [ {
"linkId": "2.1",
"text": "What is your gender?"
}, {
"linkId": "2.2",
"text": "What is your date of birth?"
}]
}]
} ]
}'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Condition'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Condition/9109890a-6756-44c1-a82d-bdfac91c7424'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Procedure'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Procedure/9109890a-6756-44c1-a82d-bdfac91c7424'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/MedicationRequest'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/MedicationRequest/9128a1ec-95be-4649-8a66-d3686b7ab0ca'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Medication'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Medication/9109890a-6756-44c1-a82d-bdfac91c7424'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Location'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/Location/90f3d0e9-2a19-453b-84bd-1fa2b533f96c'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/CareTeam'Request:
curl -X GET 'http://localhost:8300/apis/default/fhir/CareTeam/915e8fb4-86b2-4365-a420-d46fc07d5aed'Provenance resources are requested by including _revinclude=Provenance:target in the search of a resource. Is currently supported for the following resources:
- AllergyIntolerance
curl -X GET 'http://localhost:8300/apis/default/fhir/AllergyIntolerance?_revinclude=Provenance:target'
An export operation that implements the BULK FHIR Export ONC requirements can be requested by issuing a GET request to the following endpoints:
- System Export, requires the system/*.$export scope. Exports All supported FHIR resources
curl -X GET 'https://localhost:9300/apis/default/fhir/$export' - Group Export, requires the system/Group.$export scope. Exports all data in the Patient Compartment for the group.
There is only one group defined in the system currently. If OpenEMR defines additional patient population groups you would change the Group ID in the API call.
curl -X GET 'https://localhost:9300/apis/default/fhir/Group/1/$export' - Patient Export, requires the system/Group.$export scope. Exports all data for all patients in the Patient Compartment.
curl -X GET 'https://localhost:9300/apis/default/fhir/Patient/$export'
You will get an empty body response with a Content-Location header with the URL you can query for status updates on the export.
To query the status update operation you need the system/*.$bulkdata-status scope. An example query:
- Status Query
curl -X GET 'https://localhost:9300/apis/default/fhir/$bulkdata-status?job=92a94c00-77d6-4dfc-ae3b-73550742536d'
A status Query will return a result like the following:
{
"transactionTime": {
"date": "2021-02-05 20:48:38.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"request": "\/apis\/default\/fhir\/Group\/1\/%24export",
"requiresAccessToken": true,
"output": [
{
"url": "https:\/\/localhost:9300\/apis\/default\/fhir\/Document\/97552\/Binary",
"type": "Patient"
},
{
"url": "https:\/\/localhost:9300\/apis\/default\/fhir\/Document\/105232\/Binary",
"type": "Encounter"
}
],
"error": []
}
You can download the exported documents which are formatted in Newline Delimited JSON (NDJSON) by making a call to:
sh curl -X GET 'https://10.0.0.9:9300/apis/default/fhir/Document/105232/Binary'