OpenEHR REST EHR API
API Endpoint
http://www.openehr.org/api/v1Release: 1.0.0
Status: TRIAL
Preface
Purpose
This specification describes service endpoints, resources, functions and operations as well as details of requests and responses that interacts with an openEHR API in a RESTful manner.
Related Documents
Prerequisite documents for reading this document include:
Related documents include:
-
The RM XSDs
-
The other openEHR REST APIs
Status
This specification is in the DEVELOPMENT state. It might not be complete and can still be subject to changes before the final release. Users are encouraged to comment on and/or advise on these paragraphs as well as the main content.
Feedback should be provided the Specification PR tracker or on specifications-ITS issue tracker.
Trademarks
- ‘openEHR’ is a trademark of the openEHR Foundation
Glossary and conventions
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 RFC 2119 RFC 8174 when, and only when, they appear in all capitals, as shown here.
Types defined by openEHR specification are always capitalized (e.g. EHR, COMPOSITION, VERSIONED_OBJECT, etc.) when used in this specification. For a list of all available types or a particular type definition please refer to the RM index.
Throughout this specification, a set of short terms is being used as described below:
Term | Description |
---|---|
ehr_id |
The value for an EHR identifier, stored under EHR.ehr_id.value, usually an UUID or GUID |
versioned_object_uid |
The value of a VERSIONED_OBJECT unique identifier, stored under VERSIONED_OBJECT.uid.value, e.g. 8849182c-82ad-4088-a07f-48ead4180515 |
version_uid |
The value of a VERSION unique identifier, stored under VERSION.uid.value, e.g. 8849182c-82ad-4088-a07f-48ead4180515::example.domain.com::2 |
preceding_version_uid |
The value of a previous VERSION unique identifier, used usually for PUT or DELETE methods, e.g. 8849182c-82ad-4088-a07f-48ead4180515::example.domain.com::1 |
version_at_time |
Time specifier used to retrieve the VERSION at specified time; the value is a timestamp in the ISO8601 format (e.g. 2015-01-20T19:30:22.765+01:00) |
timestamp |
A timestamp in the ISO8601 format (e.g. 2015-01-20T19:30:22.765+01:00) |
UUID |
A universally unique identifier (RFC 4122) (e.g. 8849182c-82ad-4088-a07f-48ead4180515) |
HTTP Methods used in this specification are described by RFC 7231. HTTP Status codes are described by RFC 7231 and in the IANA Status Code Registry.
See design considerations section section for more details on how some HTTP methods and status codes MUST be used by an openEHR REST API implementation to achieve good interaction between services and clients in the spirit of this specification.
JSON format is used in many of the example below, but this does not imply that XML might not be supported by the same service endpoint. See below how data representation MUST be done and negotiated.
Design considerations ¶
Options and conformance ¶
Headers
Accept: application/json
Headers
Content-Type: application/json
Allow: GET, POST, PUT, DELETE, OPTIONS
Body
{
"solution": "BestEHRSys",
"solution_version": "v0.9",
"vendor": "BestEHR",
"restapi_specs_version": "v1.0",
"conformance_profile": "STANDARD",
"endpoints": [
"/ehr",
"/definitions",
"/query"
]
}
HTTP headers ¶
This specification is using a few standard and custom HTTP headers that are described below.
The openEHR-VERSION
and openEHR-AUDIT_DETAILS
headers
When it comes to committing content to an openEHR system, for all change-controlled resources (e.g. COMPOSITION, EHR_STATUS, FOLDER, etc.) the services are performing versioning under the hood. Thus, the ‘native’ way of committing content is to wrap it as VERSION and use CONTRIBUTION.
But in order to keep things simpler and consistent, service MUST allow PUT
, POST
and DELETE
methods also directly on these change-controlled resources.
However, internally, these operations MUST be executed using the ‘native’ way with CONTRIBUTION.
In order to allow clients to provide committal metadata, services MUST accept openEHR-VERSION
and openEHR-AUDIT_DETAILS
request custom headers.
For clients, it is RECOMMENDED to provision these headers based on authentication and authorization runtime data.
Below is a complex example of these request headers used in a PUT action to update a COMPOSITION:
openEHR-VERSION.lifecycle_state: code_string="532"
openEHR-AUDIT_DETAILS.change_type: code_string="251"
openEHR-AUDIT_DETAILS.description: value="An updated composition contribution description"
openEHR-AUDIT_DETAILS.committer: name="John Doe", external_ref.id="BC8132EA-8F4A-11E7-BB31-BE2E44B06B34", external_ref.namespace="demographic", external_ref.type="PERSON"
None of these headers are mandatory, but whatever is provided it MUST be merged with the default VERSION and VERSION.audit_details attributes on commit runtime.
Below a list of code_string
values and their meaning (taken from openEHR terminology):
header | code | value / meaning |
---|---|---|
openEHR-VERSION.lifecycle_state | 532 | complete |
openEHR-VERSION.lifecycle_state | 553 | incomplete |
openEHR-VERSION.lifecycle_state | 523 | deleted |
openEHR-AUDIT_DETAILS.change_type | 249 | creation |
openEHR-AUDIT_DETAILS.change_type | 250 | amendment |
openEHR-AUDIT_DETAILS.change_type | 251 | modification |
openEHR-AUDIT_DETAILS.change_type | 252 | synthesis |
openEHR-AUDIT_DETAILS.change_type | 523 | deleted |
openEHR-AUDIT_DETAILS.change_type | 666 | attestation |
openEHR-AUDIT_DETAILS.change_type | 253 | unknown |
The If-Match
header and accidental overwrites
The use case of this is described by RFC 7232.
If-Match: 8849182c-82ad-4088-a07f-48ead4180515::example.domain.com::2
This HTTP request header SHOULD be used by the clients to prevent accidental overwrites when multiple user
agents might be acting in parallel on the same resource. This is only required by a small set of resources of this specification,
as in most of the other cases the preceding_version_uid
path segment is instead required in order to prevent such accidental overwrites.
In case a service receives this header and the condition evaluates to false
it MUST respond with
HTTP status code412 Precondition Failed
.
The Location
header
This response header indicates the resource location (URL). According to RFC 7231, it is used to refer to a specific resource in relation to the response. The type of relationship is defined by the combination of request method and status code semantics. Services MUST return this header whenever a create or update operation was performed, but it MAY return this header on other operation or action. Example:
Location: http://www.openehr.org/api/v1/ehr/347a5490-55ee-4da9-b91a-9bba710f730e/composition/8849182c-82ad-4088-a07f-48ead4180515::example.domain.com::2
See representation details negotiation section for more details on how use this header.
Services MAY generate resource URL as specified by DV_URI/DV_EHR_URI format, and if this feature is supported then the services
MUST use openEHR-uri
response header. Example:
openEHR-uri: ehr:/347a5490-55ee-4da9-b91a-9bba710f730e/compositions/87284370-2D4B-4e3d-A3F3-F303D2F4F34B
The Prefer
header
This request header MAY be used by clients for resource representation negotiation. See more details on representation details negotiation section.
The ETag
and Last-Modified
headers
According to RFC 7232,
The “ETag” header field in a response provides the current entity-tag for the selected representation, as determined at the conclusion of handling the request. An entity-tag is an opaque validator for differentiating between multiple representations of the same resource, regardless of whether those multiple representations are due to resource state changes over time, content negotiation resulting in multiple representations being valid at the same time, or both.
ETag: 8849182c-82ad-4088-a07f-48ead4180515::example.domain.com::2
The Last-Modified
response HTTP header contains the datetime of the last modification of targeted resource
which should be taken from VERSION.commit_audit.time_committed.value.
An example of such header value format is:
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
See more details on RFC 7232.
These headers SHOULD be present in all responses targeting VERSION, VERSIONED_OBJECT or other resources that have unique identifier for each of the changes in time. The value of this header MAY be the unique identifier of that resource (e.g. VERSIONED_OBJECT.uid.value, VERSION.uid.value, EHR.ehr_id.value, etc).
HTTP status codes ¶
To indicate the status of the request or the executed operation, one of the following subset of the HTTP status codes RFC 7231 MUST be used:
Code | Reason-Phrase | Meaning, usecase and details |
---|---|---|
200 | OK | The request succeeded, payload sent in a 200 response depends on the request method |
201 | Created | The request has been fulfilled and has resulted in one or more new resources being created |
204 | No content | The request has been fulfilled and there is no additional content to send in the response payload body |
400 | Bad request | The service cannot or will not process the request due to something that is perceived to be a client error |
401 | Unauthorized | If the service requires authorization, this indicates that the request has not been applied because it lacks valid authentication credentials for the target resource |
403 | Forbidden | The service understood the request but refuses to authorize it |
404 | Not found | The origin service did not find the target resource or is not willing to disclose that one exists |
405 | Method Not Allowed | The method received in the request-line is known by the origin service but not supported by the target resource |
406 | Not Acceptable | The target resource does not have a current representation that would be acceptable to the user |
409 | Conflict | Indicates that the request could not be processed because it might generate a duplicate or a conflict |
412 | Precondition Failed | One or more conditions given in the request header fields evaluated to false when tested on the server |
415 | Unsupported Media Type | The service is refusing the request because the payload is in a format not supported by this method on the target resource |
500 | Internal Server Error | The service encountered an unexpected condition that prevented it from fulfilling the request |
501 | Not Implemented | The service does not support the functionality required to fulfill the request |
Meaning of these codes may be further detailed (nuanced) in this specification by particular responses. If required, other status codes MAY be used by implementations as long as their usecase is not conflicting or overlapping with above codes.
In case of errors (HTTP codes 400-500), the services MAY return more details (if Prefer: return=representation
request header is present).
Example:
some codes/messages https://github.com/ppazos/cabolabs-ehrserver/wiki/API-error-codes-and-messages and http://veratechnas1.synology.me:13606/InstanceValidator/rules.html
{
"message": "Error message",
"code": 90000,
"errors": [
{
"_type": "DV_CODED_TEXT",
"value": "Error message",
"defining_code": {
"terminology_id": {
"value": "local"
},
"code_string": "9000"
}
},
{
"_type": "DV_CODED_TEXT",
"value": "Secondary error message",
"defining_code": {
"terminology_id": {
"value": "local"
},
"code_string": "8000"
}
}
]
}
Data representation ¶
Services MUST support at least one of the openEHR XML or JSON formats (described below) for resource representation.
XML Format
When resources representation is serialized as XML, the result MUST be valid against published XSDs.
A client MAY use the header Content-Type: application/xml
in the requests to specify the XML payload format.
If the service cannot process the request payload as XML format is not supported, it MUST respond with HTTP status code415 Unsupported Media Type
.
The Accept: application/xml
SHOULD be used in the request by the client in order to specify the expected XML response format.
If the service cannot fulfill this aspect of the request, it MUST respond with HTTP status code 406 Not Acceptable
.
Proper header Content-Type: application/xml
MUST be present in the response of the service
unless response has no body (status 204).
JSON Format
Attribute names must be lowercase snake_case names as specified in the equivalent RM type. For example:
{
"category": {
"value": "event",
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "433"
}
}
}
Metadata attributes (those that are not also RM attributes) will always be prefixed by a '_'
.
One example is the _type
attribute, which should be used to specify the RM type whenever polymorphism
is involved or the underlying definition in RM type is abstract
(dynamic type is different from the static type).
This follows same rule as for XML typing.
The value of this attribute MUST be the uppercase class name from the RM specification. For example:
{
"_type": "DV_TEXT",
"value": "Hello world!"
}
The RM attributes (even required ones) that are Null
, empty list or empty arrays SHOULD be absent when serialized as JSON.
The order of attributes in the resource MAY follow the order of attributes in the RM specification of the type of the resource, but this is not mandatory.
tbd json-schema
A client MAY use the header Content-Type: application/json
in the requests to specify the JSON payload format.
If the service cannot process the request payload as JSON format is not supported, it MUST respond with HTTP status code 415 Unsupported Media Type
.
The Accept: application/json
SHOULD be used in the request by the client in order to specify the expected JSON response format.
If the service cannot fulfill this aspect of the request, it MUST respond with HTTP status code 406 Not Acceptable
.
Proper header Content-Type: application/json
MUST be present in the response of the service
unless response has no body (status 204).
Datetime format
Query parameters and path segments that are date, datetime, or timestamp, MUST always use the canonical ISO8601 extended format, e.g. 2016-06-23T13:42:16.117+02:00.
Any date, datetime or timestamp value provided by a create or update action inside the COMPOSITION content will be preserved as it was sent by client (i.e. if COMPOSITION was saved as narrow format, it will always return the way it was). However, the strongly RECOMMENDED format is to use the ISO8601 extended format.
Representation details negotiation ¶
When using the HTTP methods to create or update a resource, the service SHOULD give clients the option of returning either a complete
representation of the (modified) resource, or a minimal or no content in the payload response (assuming the operation was successfully completed).
See RFC 7240 for more details on how achieve this using Prefer
header.
The client MAY choose any of the following:
-
send
Prefer: return=minimal
to inform the service that prefers only a minimal response to a successful request. ALocation
header indicating the direct URL to access the resource MUST be part of the service response. If there is no payload content to be returned, the service SHOULD use HTTP status code204 No Content
. -
send
Prefer: return=representation
to inform the service that prefers a full representation response to a successful request. ALocation
header indicating the direct URL to access the resource MAY be part of the service response and the payload content SHOULD include a full representation.
In case no Prefer
header is present in request, the default response policy is return=minimal
.
Another preference is related to following and resolving OBJ_REF identifiers. Under some circumstances a client
MAY indicate that prefers response containing full or partial resource representation instead of references to resources using OBJ_REF.
A typical case is a list of COMPOSITIONs part of an EHR, which, strictly following RM specification, should always return
list of OBJ_REF, but sometimes clients prefers to get COMPOSITIONs.
Services that have this capability implemented SHOULD accept and honor Prefer: resolve_refs
request header.
Prefer: return=representation, resolve_refs
EHR ¶
Headers
Prefer: return={representation|minimal}
Body
{
"_type": "EHR_STATUS",
"subject": {
"external_ref": {
"id": {
"_type": "GENERIC_ID",
"value": "ins01",
"scheme": "id_scheme"
},
"namespace": "ehr_craft",
"type": "PERSON"
}
},
"other_details": {
"_type": "ITEM_TREE",
"items": []
},
"is_modifiable": "true",
"is_queryable": "true"
}
201 Created
is returned when a new EHR has been successfully created.
The EHR resource is returned in the body when the Prefer
header has the value of return=representation
.
The default for Prefer
header (or when Prefer
header if missing) is return=minimal
.
The Location
header is always returned.
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}
ETag: {ehr_id}
Body
{
"system_id": {...},
"ehr_id": {...},
"ehr_status": {...},
"time_created", "..."
}
400 Bad Request
is returned when the request body (if provided) could not be parsed.
409 Conflict
Unable to create a new EHR due to a conflict with an already existing EHR
with the same subject id, namespace pair.
Create EHRPOST/ehr
Create a new EHR
with an auto-generated identifier.
An ehr_status
may be supplied as the request body. When provided it
will also be created as part of the same request,
otherwise default resources will be created automatically by the service
with the following defaults:
-
is_queryable
: true -
is_modifiable
: true
All other attributes will be created as needed by the backend to ensure that a valid RM type instance is created.
Headers
Prefer: return={representation|minimal}
Body
{
"ehr_status": {
"is_modifiable": "true"
...
}
}
201 Created
is returned when a new EHR has been successfully created.
The new EHR resource is returned in the body when the request’s Prefer
header value is return=representation
.
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}
ETag: {ehr_id}
Body
{
"system_id": {},
"ehr_id": {},
"ehr_status": {},
"time_created", "..."
}
400 Bad Request
is returned when unable to parse the request body (if provided) or when supplied ehr_id
doesn’t follow the specification (http://www.openehr.org/releases/RM/latest/docs/support/support.html#_uid_based_id_class).
409 Conflict
Unable to create a new EHR due to a conflict with an already existing EHR.
Can happen when the supplied ehr_id is already already used by an existing EHR.
Create EHR with idPUT/ehr/{ehr_id}
Create a new EHR
with the specified EHR identifier.
An ehr_status
may be supplied as the request body. When provided it
will also be created as part of the same request,
otherwise default resources will be created automatically by the service
with the following defaults:
-
is_queryable
: true -
is_modifiable
: true
All other attributes will be left empty (null).
It is recommended that the supplied ehr_id
is a UUID, but other formats are also
allowed (http://www.openehr.org/releases/RM/latest/docs/support/support.html#_uid_based_id_class)
- ehr_id
string
(required)User supplied EHR id
200 OK
is returned when the EHR resource is successfully retrieved.
Headers
Content-Type: application/json
Body
{
"system_id": {},
"ehr_id": {},
"ehr_status": {},
"time_created", "...",
... // to be defined, possibly counts of compositions, contributions, etc.
}
404 Not Found
is returned when an EHR
with ehr_id
does not exist.
Get EHR summary by idGET/ehr/{ehr_id}
Retrieve the EHR with the specified ehr_id
.
- ehr_id
string
(required)EHR identifier
200 OK
is returned when the EHR resource is successfully retrieved.
Headers
Content-Type: application/json
Body
{
"system_id": {},
"ehr_id": {},
"ehr_status": {},
"time_created", "...",
... // to be defined, possibly counts of compositions, contributions, etc.
}
404 Not Found
is returned when an EHR
with supplied subject parameters does not exist.
Get EHR summary by subject idGET/ehr{?subject_id,subject_namespace}
Retrieve the EHR with the specified subject_id
and subject_namespace
.
- subject_id
string
(required)subject id
- subject_namespace
string
(required)subject namespace
EHR_STATUS ¶
200 OK
is return with the EHR_STATUS resource in the body when it is successfully retrieved.
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/ehr_status/{version_uid}
ETag: {version_uid}
Body
{
"uid": "...", /* LOCATABLE */
"archetype_node_id": "", /* LOCATABLE */
"name": "", /* LOCATABLE */
"archetype_details": { /* LOCATABLE */
"archetype_id": "...",
"rm_version": "1.0.3",
},
"subject": {
"external_ref": {
"namespace": "DEMOGRAPHIC",
"type": "PERSON",
"id": {
"_type": "HIER_OBJECT_ID",
"value": "_valid_root_uid_::_extension_" /* root can be UUID or OID, UUID preferred '::_extension_' is optional */
}
}
}
"is_queryable": true,
"is_modifiable": true,
"other_details": { /* optional, ITEM_STRUCTURE, archetypable */
"_type": "ITEM_TREE",
"items": {
...
}
}
}
400 Bad Request
is returned when the request has invalid content such as an invalid version_at_time
format.
404 Not Found
returned when EHR
with ehr_id
does not exist or
a version of an EHR_STATUS
resource does not exist at the specified version_at_time
.
Get EHR_STATUS version by timeGET/ehr/{ehr_id}/ehr_status{?version_at_time}
Retrieve the EHR_STATUS
associated with the EHR specified by ehr_id
.
When the version_at_time
parameter is provided, the EHR_STATUS
version that existed at the specified time is returned,
otherwise the latest trunk version is returned.
- ehr_id
string
(required)EHR identifier
- version_at_time
string
(optional)A timestamp in the extended ISO8601 format
200 OK
is return when the EHR_STATUS
is successfully retrieved.
Headers
Content-Type: application/json
Body
{
"uid": "...", /* LOCATABLE */
"archetype_node_id": "", /* LOCATABLE */
"name": "", /* LOCATABLE */
"archetype_details": { /* LOCATABLE */
"archetype_id": "...",
"rm_version": "1.0.3",
},
"subject": {
"external_ref": {
"namespace": "DEMOGRAPHIC",
"type": "PERSON",
"id": {
"_type": "HIER_OBJECT_ID",
"value": "_valid_root_uid_::_extension_" /* root can be UUID or OID, UUID preferred '::_extension_' is optional */
}
}
}
"is_queryable": true,
"is_modifiable": true,
"other_details": { /* optional, ITEM_STRUCTURE, archetypable */
"_type": "ITEM_TREE",
"items": {
...
}
}
}
404 Not Found
is returned when an EHR
with ehr_id
does not exist or
an EHR_STATUS
with version_uid
does not exist.
Get EHR_STATUS by version idGET/ehr/{ehr_id}/ehr_status/{version_uid}
Retrieve the version of the EHR_STATUS
associated with the specified ehr_id
and version_uid
.
- ehr_id
string
(required)EHR identifier
- version_uid
string
(required)version UID
Headers
Content-Type: application/json
If-Match: {preceding_version_uid}
Prefer: return={representation|minimal}
Body
{
"uid": "...", /* LOCATABLE */
"archetype_node_id": "", /* LOCATABLE */
"name": "", /* LOCATABLE */
"archetype_details": { /* LOCATABLE */
"archetype_id": "...",
"rm_version": "1.0.3",
},
"subject": {
"external_ref": {
"namespace": "DEMOGRAPHIC",
"type": "PERSON",
"id": {
"_type": "HIER_OBJECT_ID",
"value": "_valid_root_uid_::_extension_" /* root can be UUID or OID, UUID preferred '::_extension_' is optional */
}
}
}
"is_queryable": true,
"is_modifiable": true,
"other_details": { /* optional, ITEM_STRUCTURE, archetypable */
"_type": "ITEM_TREE",
"items": {
...
}
}
}
200 OK
return when EHR_STATUS
resource is successfully updated.
The updated EHR_STATUS
resource is returned in the body when Prefer
header value is return=representation
.
Headers
Location: {baseUrl}/ehr/{ehr_id}/ehr_status/{version_uid}
ETag: {version_uid}
Body
{
"uid": "...", /* LOCATABLE */
"archetype_node_id": "", /* LOCATABLE */
"name": "", /* LOCATABLE */
"archetype_details": { /* LOCATABLE */
"archetype_id": "...",
"rm_version": "1.0.3",
},
"subject": {
"external_ref": {
"namespace": "DEMOGRAPHIC",
"type": "PERSON",
"id": {
"_type": "HIER_OBJECT_ID",
"value": "_valid_root_uid_::_extension_" /* root can be UUID or OID, UUID preferred '::_extension_' is optional */
}
}
}
"is_queryable": true,
"is_modifiable": true,
"other_details": { /* optional, ITEM_STRUCTURE, archetypable */
"_type": "ITEM_TREE",
"items": {
...
}
}
}
204 No Content
is returned when Prefer
header is missing or is set to return=minimal
.
Headers
Location: {baseUrl}/ehr/{ehr_id}/ehr_status/{version_uid}
ETag: {version_uid}
400 Bad Request
is returned when the request has invalid content.
404 Not Found
is returned when EHR with ehr_id does not exist.
412 Precondition Failed
is returned when If-Match
header doesn’t match the latest trunk version.
Returns latest trunk version in the Location
and ETag
headers.
Headers
Location: {baseUrl}/ehr/{ehr_id}/ehr_status/{version_uid}
ETag: {version_uid}
Update EHR_STATUSPUT/ehr/{ehr_id}/ehr_status
Update EHR_STATUS
associated with the specified ehr_id
.
The existing latest version_uid
of EHR_STATUS
resource must be specified in the If-Match
header.
The response will contain the updated EHR_STATUS
resource when the Prefer
header has a value of return=representation
- ehr_id
string
(required)EHR identifier
VERSIONED_EHR_STATUS ¶
Management of VERSIONED_EHR_STATUSes.
200 OK
is return when the requested EHR’s VERSIONED_EHR_STATUS
is successfully retrieved.
Headers
Content-Type: application/json
Body
{
"uid": "_a_valid_uid_", /* UUID or OID, UUID preferred */
"owner_id": "{ehr_id}",
"time_created": "DV_DATE_TIME" /* ISO8601 YYYY-MM-DDThh:mm:ss.SSS(Z|[+-]hh:mm) e.g. 2017-08-01T01:06:46.000+00:00 */
}
404 Not Found
is returned when an EHR
with ehr_id
does not exist.
Get versioned EHR_STATUSGET/ehr/{ehr_id}/versioned_ehr_status
Retrieve VERSIONED_EHR_STATUS
associated with an EHR specified by ehr_id
.
- ehr_id
string
(required)EHR identifier
200 OK
is return when the requested EHR’s VERSIONED_EHR_STATUS
revision history is successfully retrieved.
Headers
Content-Type: application/json
Body
[
{
"version_id": "OBJECT_VERSION_ID", /* object_id::creating_system_id::version_tree_id */
"audits": [
{
"system_id": "String",
"time_committed": "DV_DATE_TIME",
"change_type": {
"value": "String", /* value from openehr terminology, change_type http://openehr.org/releases/1.0.2/architecture/terminology.pdf */
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "String"
}
},
"description": {
"value": "String"
}
},
{...},
{...}
]
},
{...},
{...}
]
404 Not Found
is returned when an EHR
with ehr_id
does not exist.
Get versioned EHR_STATUS revision historyGET/ehr/{ehr_id}/versioned_ehr_status/revision_history
Retrieve VERSIONED_EHR_STATUS
revision history.
- ehr_id
string
(required)EHR identifier
200 OK
is return when the requested VERSION
is successfully retrieved, which is provided in the body.
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/versioned_ehr_status/version/{version_uid}
Body
{
"_type": "ORIGINAL_VERSION",
"uid": "OBJECT_VERSION_ID", /* _ehr_status_uid_::_system_id_::1 _system_id_ is the ID of the system from which the EHR was created */
"contribution": "OBJECT_REF",
"signature": "String", /* optional */
"data": {
"_type": "EHR_STATUS",
"subject": {
"external_ref": {
"namespace": "DEMOGRAPHIC",
"type": "PERSON",
"id": {
"_type": "HIER_OBJECT_ID",
"value": "_valid_root_uid_::_extension_" /* root can be UUID or OID, UUID preferred '::_extension_' is optional */
}
}
}
"is_queryable": true,
"is_modifiable": true,
"other_details": { /* optional, ITEM_STRUCTURE, archetypable */
"_type": "ITEM_TREE",
"items": {
...
}
}
},
"commit_audit": { /* the time committed attribute is set by the server */
"system_id": "_a_string_id_", /* same as the container version.uid.system_id */
"change_type": {
"value": "creation",
"terminology_id": {
"value": "openehr"
},
"code_string": "249"
},
"description": "optional description"
},
"attestations": [
{
"system_id": "String",
"time_committed": "DV_DATE_TIME",
"description": "String" /* optional */
"reason": "DV_TEXT",
"proof": "String", /* optional */
"is_pending": "Boolean"
},
{...},
{...}
]
}
400 Bad Request
is returned when the request is invalid such as an invalid version_at_time
value.
404 Not Found
is returned when EHR
with ehr_id
does not exist or
when EHR_STATUS
does not exist at the specified version_at_time
.
Get versioned EHR_STATUS version by timeGET/ehr/{ehr_id}/versioned_ehr_status/version{?version_at_time}
Retrieve the VERSION
of an EHR_STATUS
associated with the specified ehr_id
at version_at_time
.
When the version_at_time
parameter is provided, the VERSION
that existed at the specified version time is returned,
otherwise the latest trunk version is returned.
- ehr_id
string
(required)EHR identifier
- version_at_time
string
(optional)version time specifier
200 OK
is return when the requested VERSION
is successfully retrieved.
Headers
Content-Type: application/json
Body
{
"_type": "ORIGINAL_VERSION",
"uid": "OBJECT_VERSION_ID", /* _ehr_status_uid_::_system_id_::1 _system_id_ is the ID of the system from which the EHR was created */
"contribution": { /* does not include reference to other versions, even that is mandatory in the IM */
"uid": "HIER_OBJECT_ID",
"audit": {
"system_id": "String",
"time_committed": "DV_DATE_TIME",
"description": "String" /* optional */
}
},
"signature": "String", /* optional */
"data": {
"_type": "EHR_STATUS",
"subject": {
"external_ref": {
"namespace": "DEMOGRAPHIC",
"type": "PERSON",
"id": {
"_type": "HIER_OBJECT_ID",
"value": "_valid_root_uid_::_extension_" /* root can be UUID or OID, UUID preferred '::_extension_' is optional */
}
}
}
"is_queryable": true,
"is_modifiable": true,
"other_details": { /* optional, ITEM_STRUCTURE, archetypable */
"_type": "ITEM_TREE",
"items": {
...
}
}
},
"commit_audit": { /* the time committed attribute is set by the server */
"system_id": "_a_string_id_", /* same as the container version.uid.system_id */
"change_type": {
"value": "creation",
"terminology_id": {
"value": "openehr"
},
"code_string": "249"
},
"description": "optional description"
},
"attestations": [
{
"system_id": "String",
"time_committed": "DV_DATE_TIME",
"description": "String" /* optional */
"reason": "DV_TEXT",
"proof": "String", /* optional */
"is_pending": "Boolean"
},
{...},
{...}
]
}
404 Not Found
is returned when an EHR
with ehr_id
does not exist or EHR_STATUS
with version_uid
does not exist.
Get versioned EHR_STATUS version by idGET/ehr/{ehr_id}/versioned_ehr_status/version/{version_uid}
Retrieve VERSION
of an EHR_STATUS
associated with the specified ehr_id
and version_uid
.
- ehr_id
string
(required)EHR identifier
- version_uid
string
(required)version uid
COMPOSITION ¶
Headers
Content-Type: application/json
Prefer: return={representation|minimal}
Body
{
"_type": "COMPOSITION",
"archetype_node_id": "openEHR-EHR-COMPOSITION.encounter.v1",
"name": {
"value": "Vital Signs"
},
"uid": {
"_type": "OBJECT_VERSION_ID",
"value": "8849182c-82ad-4088-a07f-48ead4180515::example.domain.com::1"
},
"archetype_details": {
"archetype_id": {
"value": "openEHR-EHR-COMPOSITION.encounter.v1"
},
"template_id": {
"value": "Example.v1::c7ec861c-c413-39ff-9965-a198ebf44747"
},
"rm_version": "1.0.2"
},
"language": {
"terminology_id": {
"value": "ISO_639-1"
},
"code_string": "en"
},
"territory": {
"terminology_id": {
"value": "ISO_3166-1"
},
"code_string": "NL"
},
"category": {
"value": "event",
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "433"
}
},
"composer": {
"_type": "PARTY_IDENTIFIED",
"external_ref": {
"id": {
"_type": "GENERIC_ID",
"value": "16b74749-e6aa-4945-b760-b42bdc07098a"
},
"namespace": "example.domain.com",
"type": "PERSON"
},
"name": "A name"
},
"context": {
"start_time": {
"value": "2014-11-18T09:50:35.000+01:00"
},
"setting": {
"value": "other care",
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "238"
}
}
},
"content": []
}
New COMPOSITION was created. Content body is only returned when Prefer
header has return=representation
, otherwise only headers are returned.
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/composition/{version_uid}
ETag: {version_uid}
Body
{
"_type": "COMPOSITION",
"archetype_node_id": "openEHR-EHR-COMPOSITION.encounter.v1",
"name": {
"value": "Vital Signs"
},
"uid": {
"_type": "OBJECT_VERSION_ID",
"value": "8849182c-82ad-4088-a07f-48ead4180515::example.domain.com::1"
},
"archetype_details": {
"archetype_id": {
"value": "openEHR-EHR-COMPOSITION.encounter.v1"
},
"template_id": {
"value": "Example.v1::c7ec861c-c413-39ff-9965-a198ebf44747"
},
"rm_version": "1.0.2"
},
"language": {
"terminology_id": {
"value": "ISO_639-1"
},
"code_string": "en"
},
"territory": {
"terminology_id": {
"value": "ISO_3166-1"
},
"code_string": "NL"
},
"category": {
"value": "event",
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "433"
}
},
"composer": {
"_type": "PARTY_IDENTIFIED",
"external_ref": {
"id": {
"_type": "GENERIC_ID",
"value": "16b74749-e6aa-4945-b760-b42bdc07098a"
},
"namespace": "example.domain.com",
"type": "PERSON"
},
"name": "A name"
},
"context": {
"start_time": {
"value": "2014-11-18T09:50:35.000+01:00"
},
"setting": {
"value": "other care",
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "238"
}
}
},
"content": []
}
Invalid ehr_id
. E.g. parsing an inconrrectly formatted ehr_id
. Some
implementing systems may require that all ehr_id
are GUIDs, i.e.
formatted as five groups of characters separated by hyphens:
01234567-0123-0123-0123-012345678abc
The EHR with the supplied ehr_id
did not exist.
Headers
Content-Type: application/json
Body
{
"_type": "XYZ",
"value": "Vital Signs"
}
400 Bad Request
either the body of the request could not be read (or converted to a COMPOSITION object) or there were COMPOSITION validation errors.
Body
{ /* see overview */
"message": "Error message",
"validationErrors": [
"error1", "error2"
]
}
Create compositionPOST/ehr/{ehr_id}/composition
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
Headers
Content-Type: application/json
Prefer: return=representation
If-Match: {preceding_version_uid}
Body
{
"_type": "COMPOSITION",
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
},
...
}
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/composition/{version_uid}
ETag: {version_uid}
Body
{
"_type": "COMPOSITION",
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
},
...
}
Headers
Content-Type: application/json
If-Match: {preceding_version_uid}
Body
{
"_type": "XYZ",
"value": "Vital Signs"
}
Bad request: either the body of the request could not be read (or converted to a COMPOSITION object) or there were composition validation errors.
Headers
Content-Type: application/json
Body
{
"message": "Error message",
"validationErrors": [
"error1",
"error2"
]
}
Headers
Content-Type: application/json
If-Match: {preceding_version_uid}
Body
{
"_type": "COMPOSITION",
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
},
...
}
No EHR with the supplied ehr_id
or no COMPOSITION with the supplied object_id
.
Headers
Content-Type: application/json
If-Match: {preceding_version_uid}
Body
{
"_type": "COMPOSITION",
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
},
...
}
Returned when supplied version_uid
is not the latest version.
Returns latest version in the Location
and ETag
headers.
Headers
Location: {baseUrl}/ehr/{ehr_id}/composition/{version_uid}
ETag: {version_uid}
Update compositionPUT/ehr/{ehr_id}/composition/{versioned_object_uid}
If the request body already contains a composition uid it must match the
preceding_version_uid
in the URL. The existing latest version_uid
of COMPOSITION
resource must be
specified in the If-Match
header.
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- versioned_object_uid
string
(required)identifier of the VERSIONED COMPOSITION to be updated.
COMPOSITION was deleted.
Headers
Location: {baseUrl}/ehr/{ehr_id}/composition/{version_uid}
ETag: {version_uid}
The composition with preceding_version_uid
is already deleted.
No EHR with the supplied ehr_id
or no COMPOSITION with the supplied preceding_version_uid
.
Returned when supplied preceding_version_uid
doesn’t match the latest version.
Returns latest version in the Location
and ETag
headers.
Headers
Location: {baseUrl}/ehr/{ehr_id}/composition/{version_uid}
ETag: {version_uid}
Delete compositionDELETE/ehr/{ehr_id}/composition/{preceding_version_uid}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- preceding_version_uid
string
(required)identifier of the COMPOSITION to be updated. This MUST be the last (most recent) version.
Headers
Content-Type: application/json
Body
{
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
},
...
}
Returned when the composition is deleted (logically).
Headers
Content-Type: application/json
No EHR with the supplied ehr_id
or no COMPOSITION with the supplied version_uid
.
Get composition by version idGET/ehr/{ehr_id}/composition/{version_uid}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- version_uid
string
(required)VERSION identifier
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/composition/{version_uid}
ETag: {version_uid}
Body
{
"_type": "COMPOSITION",
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
},
...
}
The COMPOSITION at specified version_at_time
time has been deleted.
Headers
Content-Type: application/json
No EHR with the supplied ehr_id
or no VERSIONED_COMPOSITION with the supplied versioned_object_uid
or
no COMPOSITION at specified version_at_time
time.
Get composition at timeGET/ehr/{ehr_id}/composition/{versioned_object_uid}{?version_at_time}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- versioned_object_uid
string
(required)VERSIONED_COMPOSITION identifier taken from VERSIONED_COMPOSITION.uid.value
- version_at_time
string
(optional)A timestamp in the ISO8601 format
VERSIONED_COMPOSITION ¶
This resource is related to VERSIONED_COMPOSITION described in the openEHR specification
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}
Body
{
"uid": "xxx",
"owner_id": "{ehr_id}",
"time_created": "ISO8601 timestamp",
}
No EHR with the supplied ehr_id
or no VERSIONED_COMPOSITION with the supplied versioned_object_uid
.
Get versioned compositionGET/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}
Gets a VERSIONED_COMPOSITION
.
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- versioned_object_uid
string
(required)VERSIONED_COMPOSITION identifier taken from VERSIONED_COMPOSITION.uid.value
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}/revision_history
Body
[
{
"_type": "ORIGINAL_VERSION",
...
},
{
...
]
No EHR with the supplied ehr_id
or no VERSIONED_COMPOSITION with the supplied versioned_object_uid
.
Get versioned composition revision historyGET/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}/revision_history
Gets a VERSIONED_COMPOSITION
revision history.
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- versioned_object_uid
string
(required)VERSIONED_COMPOSITION identifier taken from VERSIONED_COMPOSITION.uid.value
Headers
Content-Type: application/json
Body
{
"_type": "ORIGINAL_VERSION",
"contribution": {},
"signature": "...",
"commit_audit": {},
"uid": "...",
"data": {
"_type": "COMPOSITION",
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
}
...
}
}
No EHR with the supplied ehr_id
or no VERSIONED_COMPOSITION with the supplied versioned_object_uid
or no VERSION with the supplied versione_uid
.
Get versioned composition version by idGET/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}/version/{version_uid}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- versioned_object_uid
string
(required)VERSIONED_COMPOSITION identifier taken from VERSIONED_COMPOSITION.uid.value
- version_uid
string
(required)VERSIONED identifier taken from VERSIONED.uid.value
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}/version/{version_uid}
ETag: {version_uid}
Body
{
"_type": "ORIGINAL_VERSION",
"contribution": {},
"signature": "...",
"commit_audit": {},
"uid": "...",
"data": {
"_type": "COMPOSITION",
"name": {
"_type": "DV_TEXT",
"value": "Vital Signs"
}
...
}
}
No EHR with the supplied ehr_id
or no VERSIONED_COMPOSITION with the supplied versioned_object_uid
or no VERSION with the supplied versione_uid
.
Get versioned composition version at timeGET/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}/version{?version_at_time}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- versioned_object_uid
string
(required)VERSIONED_COMPOSITION identifier taken from VERSIONED_COMPOSITION.uid.value
- version_at_time
string
(optional)A timestamp in the ISO8601 format
DIRECTORY ¶
Directory ¶
Release: 1.0.0
Headers
Content-Type: application/json
Prefer: return={representation|minimal}
Body
{
"items": [...],
"folders": [{}]
}
New directory was created. Content body is only returned when
Prefer
header has return=representation
otherwise only headers are
returned.
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/directory/{version_uid}
ETag: {version_uid}
Body
{
"uid": "...",
"items": [...],
"folders": [{}]
}
Bad request - error creating a directory.
No EHR with the given id.
Create directoryPOST/ehr/{ehr_id}/directory
Creates a new FOLDER
associated with the ehr_id
.
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
Headers
Content-Type: application/json
If-Match: {preceding_version_uid}
Prefer: return={representation|minimal}
Body
{
"items": [...],
"folders": [{}]
}
Directory was updated.
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/directory/{version_uid}
ETag: {version_uid}
Body
{
"uid": "...",
"items": [...],
"folders": [{}]
}
Directory was updated.
Returned when Prefer
header is NOT set to return=representation
.
Headers
Location: {baseUrl}/ehr/{ehr_id}/directory/{version_uid}
ETag: {version_uid}
Bad request - error when updating a directory.
No EHR with the given id.
If-Match
header doesn’t match the last version. Returns last version in the Location
and ETag
headers.
Headers
Location: {baseUrl}/ehr/{ehr_id}/directory/{version_uid}
ETag: {version_uid}
Update directoryPUT/ehr/{ehr_id}/directory
Update FOLDER
associated with the specified ehr_id
.
The existing latest version_uid
of FOLDER
resource must be specified in the If-Match
header.
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
Headers
If-Match: {preceding_version_uid}
Directory was deleted.
Bad request - error deleting directory.
No EHR with the given id.
If-Match
header doesn’t match the last version. Returns
last version in the Location
and ETag
headers.
Headers
Location: {baseUrl}/ehr/{ehr_id}/directory/{version_uid}
ETag: {version_uid}
Delete directoryDELETE/ehr/{ehr_id}/directory
Delete FOLDER
associated with the specified ehr_id
.
The existing latest version_uid
of FOLDER
resource must be specified in the If-Match
header.
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
Headers
Content-Type: application/json
Body
{
"uid": "...",
"items": [...],
"folders": [{}]
}
No Folder at provided path.
No EHR with the given id or no directory with specified version_uid.
Get folder in directory versionGET/ehr/{ehr_id}/directory/{version_uid}{?path}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- version_uid
string
(required)version UID
- path
string
(optional)path to a sub-folder
Headers
Content-Type: application/json
Location: {baseUrl}/ehr/{ehr_id}/directory/{version_uid}
ETag: {version_uid}
Body
{
"uid": "...",
"items": [...],
"folders": [{}]
}
EHR has no directory at version_at_time
time or no Folder at provided path.
No EHR with the given id.
Get folder in directory version at timeGET/ehr/{ehr_id}/directory{?version_at_time,path}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- version_at_time
string
(optional)A timestamp in the extended ISO8601 format
- path
string
(optional)path to a sub-folder
CONTRIBUTION ¶
CONTRIBUTION ¶
Release: 1.0.0
Headers
Content-Type: application/json
Prefer: return={representation|minimal}
Body
{
"versions": [
{
"data": {
/* optional JSON serialized COMPOSITION, FOLDER or EHR_STATUS object */
},
"preceding_version_uid": "<optional string>",
"signature": "<optional string>",
"lifecycle_state": 0,
"commit_audit": {
"change_type": {},
"description": {},
"committer": {
/* optional structure - will use the outer committer if absent */
}
}
}
],
"audit":{
"committer":{
"name": "<optional identifier of the committer>" ,
"external_ref":{
"namespace": "demographic",
"type": "PERSON",
"id":{
"value": "<OBJECT_ID>"
}
}
}
}
}
New Contribution was created. Content body is only returned when Prefer
header has return=representation
otherwise only headers are returned.
Headers
Location: {baseUrl}/ehr/{ehr_id}/contribution/{contribution_uid}
ETag: {contribution_uid}
Body
{
"_type": "CONTRIBUTION",
"uid": {
"value": "0826851c-c4c2-4d61-92b9-410fb8275ff0"
},
"versions": [
{
"_type": "OBJECT_REF",
"id": {
"_type": "OBJECT_VERSION_ID",
"value": "fb458d9c-1323-42bc-b7f8-787f3660a0b5::91215053-854b-45b8-bb2a-3b0d255858d1::1"
},
"namespace": "local",
"type": "COMPOSITION"
},
{
"_type": "OBJECT_REF",
"id": {
"_type": "OBJECT_VERSION_ID",
"value": "abcdefgh-1323-42bc-b7f8-787f3660a0ba::91215053-854b-45b8-bb2a-3b0d255858d1::1"
},
"namespace": "local",
"type": "FOLDER"
}
],
"audit": {
"system_id": "9624982A-9F42-41A5-9318-AE13D5F5031F",
"committer": {
"_type": "PARTY_IDENTIFIED",
"name": "bna"
},
"time_committed": {
"value": "2017-08-15T10:37:15+02:00"
},
"change_type": {
"value": "creation",
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "249"
}
},
"description": {
"value": "comment"
}
}
}
Bad request: validation errors in one of the attached locatables, modification type doesn’t match the operation - i.e. first version of a composition with MODIFICATION.
Body
{
"message": "Error message",
"validationErrors": [
"error1",
"...",
"errorN"
]
}
No EHR with the given id
Create contributionPOST/ehr/{ehr_id}/contribution
We will use the relaxed CONTRIBUTION XSD with the following attributes optional:
-
time_committed: server will always set it
-
UID: if provided will be accepted unless it is already used in which case an error will be returned
-
system_id: where provided will be validated
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
Headers
Content-Type: application/json
Body
{
"_type": "CONTRIBUTION",
"uid": {
"value": "0826851c-c4c2-4d61-92b9-410fb8275ff0"
},
"versions": [
{
"_type": "OBJECT_REF",
"id": {
"_type": "OBJECT_VERSION_ID",
"value": "fb458d9c-1323-42bc-b7f8-787f3660a0b5::91215053-854b-45b8-bb2a-3b0d255858d1::1"
},
"namespace": "local",
"type": "COMPOSITION"
},
{
"_type": "OBJECT_REF",
"id": {
"_type": "OBJECT_VERSION_ID",
"value": "abcdefgh-1323-42bc-b7f8-787f3660a0ba::91215053-854b-45b8-bb2a-3b0d255858d1::1"
},
"namespace": "local",
"type": "FOLDER"
}
],
"audit": {
"system_id": "9624982A-9F42-41A5-9318-AE13D5F5031F",
"committer": {
"_type": "PARTY_IDENTIFIED",
"name": "bna"
},
"time_committed": {
"value": "2017-08-15T10:37:15+02:00"
},
"change_type": {
"value": "creation",
"defining_code": {
"terminology_id": {
"value": "openehr"
},
"code_string": "249"
}
},
"description": {
"value": null
}
}
}
No EHR with the supplied id or no Contribution with the supplied id was found
Get contribution by idGET/ehr/{ehr_id}/contribution/{contribution_uid}
- ehr_id
string
(required)EHR identifier taken from EHR.ehr_id.value
- contribution_uid
string
(required)CONTRIBUTION uid
Generated by aglio on 07 Dec 2018