Download OpenAPI specification:Download
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.
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.
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:
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.
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.
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);
}
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!
To successfully authenticate, you need to provide the following headers with every request.
See below for descriptions of each required header.
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.
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.
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:
ICU-based forms have the following types:
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.
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.
The so-called soft connect patient identificator includes the following fields:
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.
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.
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());
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);
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,
}
}
);
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.
Here, the ICU dashboard is always shown. This endpoint will return a url that you should redirect the end user to.
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. |
{- "date": "2019-08-24",
- "username": "hanspeter@protecdata.ch",
- "language": "en"
}
{- "redirect_uri": "string"
}
This endpoint will return an object for the ICU indicating the statuses for each form type.
date required | string <date> |
{- "date": "2019-08-24"
}
{- "icu_id": 0,
- "date": "2019-08-24",
- "statuses": {
- "ICU": "NO_FORM",
- "ICU_PER_DAY": "NO_FORM"
}, - "updated_at": "2019-08-24T14:15:22Z"
}
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.
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. |
{- "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"
}
{- "redirect_uri": "string"
}
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
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) |
{- "date": "2019-08-24",
- "shift": 1,
- "patients": [
- {
- "internal_id": "10",
- "age": 10,
- "sex": "M",
- "admission_date": "202201201030"
}
]
}
[- {
- "icu_id": 0,
- "mdsi_id": "string",
- "internal_id": "string",
- "admission_date": "2019-08-24",
- "date": "2019-08-24",
- "shift": 1,
- "sex": "M",
- "age": "string",
- "statuses": {
- "VISIT": "NO_FORM",
- "ENTRY": "NO_FORM",
- "EXIT": "NO_FORM",
- "PER_SHIFT": "NO_FORM",
- "PER_DAY": "NO_FORM"
}, - "updated_at": "2019-08-24T14:15:22Z",
- "marked_as_done_at": "2019-08-24T14:15:22Z"
}
]
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.
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. |
{- "age": 10,
- "sex": "M",
- "admission_date": "202201201030",
- "username": "hanspeter@protecdata.ch",
- "language": "en",
- "date": "2019-08-24",
- "shift": 1,
- "form_type": "ENTRY"
}
{- "redirect_uri": "string"
}
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.
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) |
{- "date": "2019-08-24",
- "shift": 1,
- "patients": [
- {
- "age": 10,
- "sex": "M",
- "admission_date": "202201201030"
}
]
}
[- {
- "icu_id": 0,
- "mdsi_id": "string",
- "internal_id": "string",
- "admission_date": "2019-08-24",
- "date": "2019-08-24",
- "shift": 1,
- "sex": "M",
- "age": "string",
- "statuses": {
- "VISIT": "NO_FORM",
- "ENTRY": "NO_FORM",
- "EXIT": "NO_FORM",
- "PER_SHIFT": "NO_FORM",
- "PER_DAY": "NO_FORM"
}, - "updated_at": "2019-08-24T14:15:22Z",
- "marked_as_done_at": "2019-08-24T14:15:22Z"
}
]
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.
required | Array of objects (PatientEntryFields) |
{- "patients": [
- {
- "age": 10,
- "sex": "M",
- "admission_date": "202201201030",
- "internal_id": "10",
- "mdsi_id": "10"
}
]
}
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.
required | Array of objects (PatientEntryFields) |
{- "patients": [
- {
- "age": 10,
- "sex": "M",
- "admission_date": "202201201030",
- "internal_id": "10",
- "mdsi_id": "10"
}
]
}
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.
required | Array of objects (PatientExitFields) |
{- "patients": [
- {
- "age": 10,
- "sex": "M",
- "admission_date": "202201201030",
- "exit_date": "202201201030",
- "internal_id": "10",
- "mdsi_id": "10"
}
]
}