MDSi-Connect (1.0.0)

Download OpenAPI specification:Download

MDSi-Connect Support: info@protecdata.ch URL: https://savedata.ch

This is the API documentation for integrating with MDSi-Connect API. It is broken down into various sections such as authentication, code examples, and patient integration options.

Introduction

The API is implemented in a "RESTful" way where most of the interactions are done via entities. There are some extensions to the entities since we have ways of interacting with the entities.

All endpoints are protected by authentication to make sure that unauthorized parties do not access data they should not access.

Authentication

The MDSi-Connect API offers only a single way of integrating with it - API keys per ICU using public-key cryptography. Each ICU can have multiple pairs of currently valid public/private keys. The API keys are separately issued for DEV and PROD environments and can not be used interchangably.

We want to make sure that the private key can not be hijacked in-traffic. The public-key method that we use to prevent this is HMAC. In short, HMAC calculates a signature of the request including the following components using the private key:

  • string http_verb, // GET/POST/PUT
  • string request_path, // /v1/action
  • string content_sha256_hash, // JSON body stringified and sha256 hashed
  • int timestamp, // unix timestamp in seconds

The signature is then included in the request headers and sent to the MDSi-Connect server. The signature of the request will also be calculated on the MDSi-Connect server. The two signatures are then compared to see whether the request is valid and whether it should be processed.

PS. Some endpoints return a URL that the end user should be redirected to. After redirecting, a time-limited session is created for the user so that they can use the system. For example, view or update a previously existing form.

HMAC signature calculation examples

In the rare case of GET requests, replace the json_encoded body with an empty string ("") and use the hash SHA256 hash of it. You might need to refactor the examples below to allow an empty request body.

PHP

The way that you should implement signature calculation in PHP is as follows.

function getHmacSignature(
    string $http_verb, // GET/POST/PUT
    string $request_path, // /v1/action
    array $body, // JSON request body
    int $timestamp, // unix timestamp in seconds
    string $api_secret // api secret as string
) {
    $content_sha256_hash = hash("sha256", json_encode($body));
    $string_to_sign = $http_verb . "\n" . $request_path . "\n" . $content_sha256_hash . "\n" . $timestamp;

    return hash_hmac("sha256", $string_to_sign, $api_secret);
}

JavaScript

For JavaScript (the example below is TypeScript, but you can remove the types), the signature calculation is as follows.

import { createHash, createHmac } from "node:crypto";

const sha256 = (content: string) => {
    return createHash("sha256").update(content).digest("hex");
};

const getHmacSignature = (
    http_verb: string, // GET/POST/PUT
    request_path: string, // /v1/action
    body: object, // JSON request body
    timestamp: number, // unix timestamp in seconds
    api_secret: string // api secret as string
) => {
    const content_sha256_hash = sha256(JSON.stringify(body));
    const string_to_sign = http_verb + "\n" + request_path + "\n" + content_sha256_hash + "\n" + timestamp;

    return createHmac("sha256", api_secret).update(string_to_sign).digest("hex");
};

Note: Only do this on the backend as otherwise the credentials are exposed!

Required headers for authentication

To successfully authenticate, you need to provide the following headers with every request.

  • The public API key to identify who is making the request
  • The calculated request signature
  • The timestamp that was used to calculate the signature

See below for descriptions of each required header.

api_key_header

Public API key as a header.

Security Scheme Type: API Key
Header parameter name: X-API-Key

api_signature_header

API request signature as a header. Please see the general introduction for more info.

Security Scheme Type: API Key
Header parameter name: X-API-Signature

api_timestamp_header

API request timestamp (in seconds) as a header.

Security Scheme Type: API Key
Header parameter name: X-API-Timestamp

PS. Some endpoints return a URL that the end user should be redirected to. After redirecting, a session is created for them so that they can use the system.

Integrations scenarios

The API enables you to integrate with forms in various different ways. You can either integrate with a single form directly or you can integrate a whole "type of forms". In the "type of forms" option, the user will automatically also gain access to a per-patient or per-icu dashboard. The dashboards show all of the assigned forms and their status (filled, not filled) for each form.

Additionally, you can choose between different levels of patient identificators. They are described in more detail below.

Types of forms

The forms in MDSi-connect have various different types and can be requested to be filled at different points of time and sometimes even at different intervals.

For example, patient-based forms have the following types:

  • ENTRY - when the patient enters the ICU
  • VISIT - at any point of time during stay in the ICU
  • PER_SHIFT - during each shift of staying in the ICU (usually 2-3 shifts)
  • PER_DAY - during each day of staying in the ICU
  • EXIT - when the parient exits the ICU

ICU-based forms have the following types:

  • ICU - one-time forms to be filled in for the ICU
  • ICU_PER_DAY - forms to be filled in once per day for the ICU

Forms vs Dashboards

In most cases it makes sense to integrate using the dashboard option. You can hook the form dashboards into your processes and just provide the patient info and the type of form in the request. This means that you do not care how many and which (or if any) forms are assigned. You just call the same static endpoint.

If your workflows are very specific and you want the hospital staff to answer a certain form for each patient, you can also do that.

Patient identificators

The patient identificator are used by the integrating PDMS to identify the individual patients throughout the whole life-cycle. Initially, when creating form submission requests. And also when asking for the form status for each patient.

The fields do not include any direct personal values of patients, but enough to be able to later connect the patient entries with their respective MDSi exports.

Soft connect

The so-called soft connect patient identificator includes the following fields:

  • Age of the patient at the time of admission
  • Admission date of patient into the ICU (Format: YYYYMMDDHHmm. YYYY = year, MM = month, DD = day of the month, HH = hours, mm = minutes)
  • Chromosomal gender of the patient

Internal ID

This is the recommended integration option.

The internal ID logic is an extension of soft connect with an additional unique internal ID from your system. You only need to provide the soft connect fields in the form filling request step.

You can then use your internal ID for status queries instead of providing all of the soft connect fields every time. The internal ID can be anything that can be serialized to a string - floats, integers, strings. Ideally, a string should be provided already.

Code examples

The examples below include various languages and tech stacks to query the "/api/v1/api-key-test" endpoint. This is also the recommended way - first implementing the signature calculation logic and then the more specific actions.

PHP using Guzzle

Below is an example using PHP Guzzle and composer.

use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;

$client = new Client(
    array(
        "base_uri" => getenv("MDSI_CONNECT_BASE_URI")
    )
);

$api_key = "yourApiKey";
$api_secret = "yourApiSecret";
$current_timestamp = time();

$signature = getHmacSignature(
    "POST",
    "/api/v1/api-key-test/",
    ["key" => "value", "second" => []],
    $current_timestamp,
    $api_secret
);

$request = $client->post(
    "/api/v1/api-key-test/",
    array(
        "headers" => array(
            "X-API-Key" => $api_key,
            "X-API-Timestamp" => $current_timestamp,
            "X-Api-Signature" => $signature
        ),
        RequestOptions::JSON => array(
            "key" => "value",
            "second" => array()
        ),
    ),
);
$response = json_decode($request->getBody());

PHP using cURL

Below is an example using PHP and cURL.

$body = ["key" => "value", "second" => []];

$ch = curl_init();

$time = time();

$apiKey = "yourApiKey";
$apiSecret = "yourApiSecret";

$string_to_sign = "POST" . "\n" . "/api/v1/api-key-test/" . "\n" . hash("sha256", json_encode($body)) . "\n" . $time;
$sig = hash_hmac("sha256", $string_to_sign, $apiSecret);

$headers = [
    "X-API-Key: " . $apiKey,
    "X-API-Timestamp: " . $time,
    "X-Api-Signature: " . $sig,
    "Content-Type: application/json"
];

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "https://api.mdsi-connect.savedata.ch/api/v1/api-key-test/");
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_VERBOSE, true);
// you might need to disable SSL verification depending on your environment
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));

$ce = curl_exec($ch);

curl_close($ch);

JavaScript using axios

Below is an example using JS and axios.

const apiKey = "yourApiKey";
const apiSecret = "yourApiSecret";

const timestamp = getCurrentTimeStamp();
const signature = getHmacSignature(
    "POST",
    "/api/v1/api-key-test/",
    body,
    timestamp,
    apiSecret
);

const { data } = await axios.post(
    "https://api.mdsi-connect.savedata.ch/api/v1/api-key-test/",
    body,
    {
        headers: {
            "X-API-Key": apiKey,
            "X-API-Signature": signature,
            "X-API-Timestamp": timestamp,
        }
    }
);

API Key Test

Test API key using GET

This endpoint simplifies the first integration. You need to provide all of the required headers, but no relevant actions are done in the system. Just the API key is echoed back.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)

Responses

Test API key using POST

This endpoint simplifies the first integration. You need to provide all of the required headers, but no relevant actions are done in the system. Just the API key is echoed back. You can provide any value for the body you prefer so that you can test the signature calculation.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)

Responses

API Form Dashboards

Request the ICU dashboard

Here, the ICU dashboard is always shown. This endpoint will return a url that you should redirect the end user to.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
date
required
string <date>
username
required
string

Identification for the user who is making the changes. This value will be shown in the history of each form submission.

language
required
string
Enum: "en" "de" "fr" "it"

Initially selected language for the user. They can change it afterwards.

Responses

Request samples

Content type
application/json
{
  • "date": "2019-08-24",
  • "username": "hanspeter@protecdata.ch",
  • "language": "en"
}

Response samples

Content type
application/json
{
  • "redirect_uri": "string"
}

Fetch ICU dashboard statuses

This endpoint will return an object for the ICU indicating the statuses for each form type.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
date
required
string <date>

Responses

Request samples

Content type
application/json
{
  • "date": "2019-08-24"
}

Response samples

Content type
application/json
{
  • "icu_id": 0,
  • "date": "2019-08-24",
  • "statuses": {
    },
  • "updated_at": "2019-08-24T14:15:22Z"
}

Request the patient dashboard using INTERNAL ID

You can provide a form_type value so that the user is redirected to the correct form right away. If you leave the type empty, the dashboard is shown. This endpoint will return a url that you should redirect the end user to. The internal ID is used as a primary key here - this means that the soft connect fields will be updated when providing the same internal ID but changed age, sex, and admission date.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
internal_id
required
string non-empty

This field is used as a primary key to update age, sex, admission date if any of these have changed

required
string or integer

Age of patient - between 0-120

sex
required
string
Enum: "M" "F"
admission_date
required
string^20\d{2}\d{2}\d{2}(0[0-9]|1[0-9]|2[0-3])[0-5]...
username
required
string

Identification for the user who is making the changes. This value will be shown in the history of each form submission.

language
required
string
Enum: "en" "de" "fr" "it"

Initially selected language for the user. They can change it afterwards.

date
required
string <date>

For forms of type "once per shift" and "once per day", this date is used to determine if it has been filled

shift
required
integer
Enum: 1 2 3

For forms of type "once per shift", this field is used to determine if it has been filled

form_type
string or null
Enum: "VISIT" "ENTRY" "EXIT" "PER_SHIFT" "PER_DAY"

If you provide the type of form and there is only one form of that type available then the user will be directed to it. If no type is provided or if a number other than 1 form exists, the dashboard will be shown.

Responses

Request samples

Content type
application/json
{
  • "internal_id": "10",
  • "age": 10,
  • "sex": "M",
  • "admission_date": "202201201030",
  • "username": "hanspeter@protecdata.ch",
  • "language": "en",
  • "date": "2019-08-24",
  • "shift": 1,
  • "form_type": "ENTRY"
}

Response samples

Content type
application/json
{
  • "redirect_uri": "string"
}

Fetch patient dashboard statuses using INTERNAL ID

This endpoint will return an array of objects for each patient indicating the statuses for each form type. If no data about a patient yet exists in the system, it will be created and an object is returned. The internal ID is used as a primary key here - this means that the soft connect fields will be updated when providing the same internal ID but changed age, sex, and admission date

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
date
required
string <date>

For forms of type "once per shift" and "once per day", this date is used to determine if it has been filled

shift
required
integer or null
Enum: 1 2 3

For forms of type "once per shift", this field is used to determine if it has been filled

required
Array of objects (InternalIdFields)

Responses

Request samples

Content type
application/json
{
  • "date": "2019-08-24",
  • "shift": 1,
  • "patients": [
    ]
}

Response samples

Content type
application/json
[
  • {
    }
]

Request the patient dashboard using SOFT CONNECT

You can provide a form_type value so that the user is redirected to the correct form right away. If you leave the type empty, the dashboard is shown. This endpoint will return a url that you should redirect the end user to.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
required
string or integer

Age of patient - between 0-120

sex
required
string
Enum: "M" "F"
admission_date
required
string^20\d{2}\d{2}\d{2}(0[0-9]|1[0-9]|2[0-3])[0-5]...
username
required
string

Identification for the user who is making the changes. This value will be shown in the history of each form submission.

language
required
string
Enum: "en" "de" "fr" "it"

Initially selected language for the user. They can change it afterwards.

date
required
string <date>

For forms of type "once per shift" and "once per day", this date is used to determine if it has been filled

shift
required
integer
Enum: 1 2 3

For forms of type "once per shift", this field is used to determine if it has been filled

form_type
string or null
Enum: "VISIT" "ENTRY" "EXIT" "PER_SHIFT" "PER_DAY"

If you provide the type of form and there is only one form of that type available then the user will be directed to it. If no type is provided or if a number other than 1 form exists, the dashboard will be shown.

Responses

Request samples

Content type
application/json
{
  • "age": 10,
  • "sex": "M",
  • "admission_date": "202201201030",
  • "username": "hanspeter@protecdata.ch",
  • "language": "en",
  • "date": "2019-08-24",
  • "shift": 1,
  • "form_type": "ENTRY"
}

Response samples

Content type
application/json
{
  • "redirect_uri": "string"
}

Fetch patient dashboard statuses using SOFT CONNECT

This endpoint will return an array of objects for each patient indicating the statuses for each form type. If no data about a patient yet exists in the system, it will be created and an object is returned.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
date
required
string <date>

For forms of type "once per shift" and "once per day", this date is used to determine if it has been filled

shift
required
integer or null
Enum: 1 2 3

For forms of type "once per shift", this field is used to determine if it has been filled

required
Array of objects (SoftConnectFields)

Responses

Request samples

Content type
application/json
{
  • "date": "2019-08-24",
  • "shift": 1,
  • "patients": [
    ]
}

Response samples

Content type
application/json
[
  • {
    }
]

API Patients

Mark patients as deleted

This entrypoint allows you to mark patients as "deleted" in MDSi-connect in bulk. You can re-create a patient by sending the patient info via the /entry endpoint again. Deleted patients will not show up in dashboards, but the data will be retained.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
required
Array of objects (PatientEntryFields)

Responses

Request samples

Content type
application/json
{
  • "patients": [
    ]
}

Entry of admitted patients

This entrypoint allows you to send data about newly admitted patients into MDSi-connect in bulk. You can also use this endpoint to send data of a single patient.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
required
Array of objects (PatientEntryFields)

Responses

Request samples

Content type
application/json
{
  • "patients": [
    ]
}

Exit of admitted patients

This entrypoint allows you to send data about the exit date of patients in MDSi-connect in bulk. If a patient is not present in the system, they will be created. You can also use this endpoint to send data of a single patient.

Authorizations:
(api_key_headerapi_signature_headerapi_timestamp_header)
Request Body schema: application/json
required
required
Array of objects (PatientExitFields)

Responses

Request samples

Content type
application/json
{
  • "patients": [
    ]
}