NAV
C# Go Java JavaScript PHP Python Ruby Shell

Introduction

Welcome to the Open HealthHub Open API Hub! You can use our APIs to access the Open HealthHub Open API endpoints based on HL7 FHIR, which can interact with the Open HealthHub and SMART on FHIR applications for questionnaires, information and remote patient monitoring.

FHIR logo

Open API overview

Open HealthHub's Open API hub is a core part of our mission to empower healthcare organizations to take back control over their own healthcare pathways and remote patient monitoring. Our APIs are designed to enable teams of any shape or size to build robust and secure integrations that help them customize and get the most value out of the Open HealthHub SMART on FHIR solutions Improve Designer, Improve Mobile app and Improve Connected Health.

Open HealthHub Open API hub

Client libraries

We have language bindings in Shell, .NET, Java, JavaScript, PHP, Python and Ruby! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

For complete implementation please have a look at our FHIR Client Example Repository

Clients
Shell FHIR client Ruby FHIR client Python FHIR client PHP FHIR client JAVASCRIPT FHIR client JAVA FHIR client C# FHIR client GO FHIR client
Shell Ruby Python PHP JS Java C# Go

Get started

Welcome to the Open HealthHub Open API Hub! You can use our API's to access the Open HealthHub Open API endpoints based on FHIR, which can interact with the Open HealthHub hub for questionnaires, information and remote patient monitoring.

Step 1. Play and learn

Get familiar with our FHIR REST APIs, FHIR Client Example Repository and our example code here on this website. You can play with our sandbox with the listed credentials.

Learn more about FHIR via the following links:

Step 2. Get Credentials

To generate FHIR API credentials for the live environment:

  1. Request for an Open HealthHub API developer account.
  2. We will send your credentials.
  3. You can start using your credentials.
  4. Replace the sandbox credentials with your credentials.
  5. Replace the sandbox URL with the live URL.

For more information about authentication see our documentation

Step 3. Upload your public key

  1. Download and install the latest version of GPG command line tools for your operating system.
  2. Generate a GPG key pair.
    • in your terminal/command prompt enter gpg --expert --full-generate-key
    • choose ECC and ECC for the kind of key, Curve 25519 for the kind of curve, choose default 0 (does not expire) for keys validity, choose a user ID to match your usecase, when choosing a passphrase remember that it will need to be provided every time the private key is used.
    • use gpg --list-secret-keys --keyid-format=long to view all keys
    • for the key you want to use, find the line that looks like sec ed25519/9EB63F8FEEC4DEB8 2021-09-23 [SC] take the key ID, in this case: 9EB63F8FEEC4DEB8
    • run gpg --armor --export <KEYID> to export your key. You want to upload your public key that looks like: -----BEGIN PGP PUBLIC KEY BLOCK----- ..... -----END PGP PUBLIC KEY BLOCK-----
    • run gpg --armor --export-secret-key <KEYID> to export your private key that looks like -----BEGIN PGP PRIVATE KEY BLOCK----- ..... -----END PGP PRIVATE KEY BLOCK-----. Store this in a password manager/secret available only to your software for decrypting.
  3. Using our Binary resource upload your public key

Step 4. Get an Improve Designer account

You can design your own questionnaires and patient instructions which you can use for your app project using the Improve Designer.

  1. Register your own Improve Designer account or deliver your API key to one of your colleagues who has an Improve Designer account.
  2. Use the program credentials for your app project.

Step 5. Start your integration

Start building your own App project using our documentation and our example FHIR Client Example Repository.

Authentication

Retrieve the access token:

// Create Interceptor class that implements IClientInterceptor
@Override
public void interceptRequest(IHttpRequest theRequest) {
    String token = getToken();
    theRequest.addHeader(Constants.HEADER_AUTHORIZATION, (Constants.HEADER_AUTHORIZATION_VALPREFIX_BEARER + token));
}

private String getToken() {
    HttpRequest request = HttpRequest.newBuilder()
            .uri(new URI("https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token"))
            .POST(ofString("client_id=api-sandbox&client_secret=95810e52-4307-41f5-99a4-d873ab63b536&grant_type=client_credentials"))
            .header("Content-Type", "application/x-www-form-urlencoded")
            .build();

    String response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()).body();
    JsonObject respAsJson = JsonParser.parseString(response).getAsJsonObject();

    String token = respAsJson.getAsJsonPrimitive("access_token").getAsString();
}
const headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append('Accept', 'application/json');

const urlencoded = new URLSearchParams();
urlencoded.append('client_id', 'api-sandbox');
urlencoded.append('client_secret', '95810e52-4307-41f5-99a4-d873ab63b536');
urlencoded.append('grant_type', 'client_credentials');

const requestOptions = {
  method: 'POST',
  headers: headers,
  body: urlencoded,
  redirect: 'follow'
};

const token = await fetch('https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token', requestOptions)
  .then(response => response.json())
  .then(result => result.access_token);
$ch = curl_init('https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => 'client_id=api-sandbox&client_secret=95810e52-4307-41f5-99a4-d873ab63b536&grant_type=client_credentials',
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/x-www-form-urlencoded'
    ],
]);
$res = curl_exec($ch);
curl_close($ch);
$decoded_json = json_decode($res, true);
$token = $decoded_json['access_token'];
type TokenResponse struct {
    Access_token string
}

func authenticate() string {
    url := "https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token"
    method := "POST"

    payload := strings.NewReader("client_id=api-sandbox&client_secret=95810e52-4307-41f5-99a4-d873ab63b536&grant_type=client_credentials")

    client := &http.Client{}
    req, err := http.NewRequest(method, url, payload)

    if err != nil {
        fmt.Println(err)
        return ""
    }
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

    res, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
        return ""
    }
    defer res.Body.Close()

    data := new(TokenResponse)
    err = json.NewDecoder(res.Body).Decode(&data)
    if err != nil {
        fmt.Println(err)
        return ""
    }

    return data.Access_token
}
# The ruby fhir client takes care of retrieving the token, if configured correctly as shown below
token=$(curl --location --request POST 'https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=api-sandbox' \
--data-urlencode 'client_secret=95810e52-4307-41f5-99a4-d873ab63b536' \
--data-urlencode 'grant_type=client_credentials'  | jq -r '.access_token')
def get_token():
    url = 'https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token'
    response = requests.post(url,
                  f'client_id={api-sandbox}&client_secret={95810e52-4307-41f5-99a4-d873ab63b536}&grant_type=client_credentials',
                  headers={'Content-Type': 'application/x-www-form-urlencoded'}).json()
    return response['access_token']
const string authUrl =
        "https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token";
IEnumerable<KeyValuePair<string, string>> auth = new[]
{
        new KeyValuePair<string, string>("client_id", "api-sandbox"),
        new KeyValuePair<string, string>("client_secret", "95810e52-4307-41f5-99a4-d873ab63b536"),
        new KeyValuePair<string, string>("grant_type", "client_credentials")
};
HttpClient http = new HttpClient();
HttpContent content = new FormUrlEncodedContent(auth);
HttpResponseMessage response = http.PostAsync(authUrl, content).Result;
return JsonConvert.DeserializeObject<Dictionary<string, string>>(
        response.Content.ReadAsStringAsync().Result);

The client_id and client_secret need to be replaced with the id and secret for your client

Adding the API key and token to the request:

client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
client.additional_headers = {'X-API-KEY': 'ad880601-b7e6-4d86-901d-b6fca96fc725'}
client.set_oauth2_auth('client_id', 'client_secret', 'https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/auth', 'https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token')
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4',
    authorization=f'Bearer {get_token()}',
    extra_headers={'x-api-key': 'ad880601-b7e6-4d86-901d-b6fca96fc725'}
)
# With shell, you can just pass the correct header with each request
curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4" \
  -H "X-API-KEY: ad880601-b7e6-4d86-901d-b6fca96fc725" \
  -H "Authorization: Bearer $token"
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const requestObj = typeof request === 'string' ? {url: request} : request;
const parameterSeparator = requestObj.url.indexOf('?') === -1 ? '?' : '&';
requestObj.url = `${requestObj.url}${parameterSeparator}apikey=ad880601-b7e6-4d86-901d-b6fca96fc725`;
requestObj.headers = {
  Authorization: `Bearer ${this.token}`
};
client.request(requestObj);
$ch = curl_init('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => array(
        'X-API-Key: ' . self::API_KEY
    ),
    CURLOPT_XOAUTH2_BEARER => $token,
    CURLOPT_HTTPAUTH => CURLAUTH_BEARER
]);
req := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4", nil)
req.Header.Add("X-API-KEY", "ad880601-b7e6-4d86-901d-b6fca96fc725")
token := authenticate()
req.Header.Add("Authorization", "Bearer " + token)
@Interceptor
public class ApiKeyInterceptor {
    @Hook(Pointcut.CLIENT_REQUEST)
    public void addApiKey(IHttpRequest request) {
        request.addHeader("X-API-KEY", "ad880601-b7e6-4d86-901d-b6fca96fc725");
    }
}

FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");
client.registerInterceptor(new ApiKeyInterceptor());
client.registerInterceptor(new AuthorizationInterceptor());
public class ApiKeyMessageHandler : HttpClientHandler
{
                protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
                {
                        request.Headers.Add("x-api-key", "ad880601-b7e6-4d86-901d-b6fca96fc725");
                        request.Headers.Add("Authorization", $"Bearer {_token}");
                        return await base.SendAsync(request, cancellationToken);
                }
}

var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4", settings, new ApiKeyMessageHandler());

Make sure to replace api-key with your personal API key.

Improve uses API keys and OpenID Connect to allow access to the API. You can register a new Improve API key at our developer portal.

Improve expects the API key and OpenID Connect access token to be included in all API requests to the server in a header, or as request parameter on the url.

For the sandbox environment this looks as follows:

FHIR 4 API Reference

Overview

FHIR Resource Open HealthHub entity
PlanDefinition Program
Questionnaire Module
CarePlan Activated Program
Questionnaire Reference + Status Module + Participant Task
QuestionnaireResponse Module Response

Entity Mapping FHIR

Binary (Upload Key)

Upload public key

String publicKey = Base64.getEncoder().encodeToString(IOUtils.resourceToByteArray("sandbox.pub"));

HttpRequest request = HttpRequest.newBuilder()
        .uri(new URI("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary"))
        .POST(ofString(publicKey))
        .header("Content-Type", "text/plain")
        .header("Authorization", "Bearer " + getToken())
        .header("x-api-key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
        .build();

HttpResponse<Void> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.discarding());
HttpClient http = new HttpClient();

var publicKey = File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "sandbox.pub"));
var encodedKey = System.Convert.ToBase64String(publicKey);
var content = new ByteArrayContent(Encoding.ASCII.GetBytes(encodedKey));
content.Headers.ContentType = MediaTypeHeaderValue.Parse("text/plain");

http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthenticationUtil.authenticate());
http.DefaultRequestHeaders.Add("x-api-key", "ad880601-b7e6-4d86-901d-b6fca96fc725");

HttpResponseMessage response = http.PostAsync("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary", content).Result;
Console.Out.WriteLine(response.StatusCode);
publicKey, err := ioutil.ReadFile("sandbox.pub")
if err != nil {
  return 0, err
}

pubKeysBytes := []byte(publicKey)
encodedBytes := []byte(b64.StdEncoding.EncodeToString(pubKeysBytes))

req, err := http.NewRequest("POST", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary", bytes.NewBuffer(encodedBytes))
if err != nil {
  return 0, err
}

token := client.Authenticate()
req.Header.Add("Authorization", "Bearer "+token)
req.Header.Add("Content-Type", "text/plain")
req.Header.Add("X-API-KEY", "ad880601-b7e6-4d86-901d-b6fca96fc725")

resp, err := http.DefaultClient.Do(req)
const client = new Client();
const token = await client.getToken();
const request = {
  method: 'POST',
  headers: {
    'Content-Type': 'text/plain',
    'Authorization': `Bearer ${token}`,
    'X-API-KEY': 'ad880601-b7e6-4d86-901d-b6fca96fc725'
  },
  body: btoa(publicKey)
};
const promise = fetch('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary', request);
return promise.then(resp => {
  return {responseStatus: resp.status, request};
});
$client = new FhirClient();

$keyASCII = file_get_contents(dirname(__FILE__) . 'sandbox.pub');

$encodedPubKey = base64_encode($keyASCII);
return $client->createRaw("Binary", $encodedPubKey, 'text/plain');
with open('sandbox.pub') as file:
    key = file.read()
key_bytes = key.encode('ascii')
base64_bytes = base64.b64encode(key_bytes)
encoded_key = base64_bytes.decode('ascii')
token = get_token()
resp = requests.post(url='https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary',
                     data=encoded_key,
                     headers={'Content-Type': 'text/plain', 'Authorization': f'Bearer {token}', 'X-API-KEY': 'ad880601-b7e6-4d86-901d-b6fca96fc725'})
return resp
uri = URI('https://auth.openhealthhub.com/auth/realms/OpenHealthHub/protocol/openid-connect/token')
params = { 'client_id' => 'api-sandbox',
           'client_secret' => '95810e52-4307-41f5-99a4-d873ab63b536',
           'grant_type' => 'client_credentials' }
res = Net::HTTP.post_form(uri, params)
token = JSON.parse(res.body)['access_token']

file = open('sandbox.pub')
key = file.read
encoded_key = Base64.strict_encode64(key)

uri = URI('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary')
Net::HTTP.post(uri, encoded_key, { 'Content-Type' => 'text/plain',
                                   'X-API-KEY' => 'ad880601-b7e6-4d86-901d-b6fca96fc725',
                                   'Authorization' => 'Bearer ' + token })
base64Key=$(base64 -w 0 sandbox.pub)
curl -X POST "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary" -H "Content-Type: text/plain" -H "X-API-KEY: ad880601-b7e6-4d86-901d-b6fca96fc725" -H "Authorization: Bearer $token" --data-raw "$base64Key"

This endpoint can be used to upload your public key to Open HealthHub. This key will be used to encrypt patient data so you/your application can decrypt the patient data using your private key.

HTTP Request

POST https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Binary

The body of the post should be the base64 encoded version of the public key.

Request headers

Header Value Description
Content-Type text/plain the Content-Type should be text/plain

CarePlan

Create CarePlan

curl -X POST "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan" -H "Content-Type: application/json" --data-binary "@careplan.json"
CarePlan carePlan = new CarePlan();
carePlan.setPeriod(new Period().setStart(Date.from(Instant.now())));

Patient patient = new Patient();
patient.setId("patient");
HumanName humanName = new HumanName();
humanName.setText("Test Patient");
patient.setName(Collections.singletonList(humanName));

ContactPoint email = new ContactPoint();
email.setSystem(ContactPoint.ContactPointSystem.EMAIL);
email.setValue("test@patient.ohh");
patient.addTelecom(email);

Identifier programPatientId = new Identifier();
programPatientId.setSystem("urn:oid:2.16.840.1.113883.2.4.99");
programPatientId.setValue("1234");
patient.addIdentifier(programPatientId);

carePlan.addContained(patient);
carePlan.setSubject(new Reference("#patient"));
carePlan.setInstantiatesCanonical(List.of(new CanonicalType("PlanDefinition/cca2eaf3-03a9-46c0-88c6-e0287917cea6")));

return client.create().resource(carePlan).execute();
var patient = new Patient
{
    Id = "patient",
    Identifier = new List<Identifier>
    {
        new Identifier
        {
            System = "urn:oid:2.16.840.1.113883.2.4.99",
            Value = "1234"
        }
    },
    Name = new List<HumanName> {new HumanName {Text = "Test Patient"}},
    Telecom = new List<ContactPoint>
    {
        new ContactPoint
        {
            System = ContactPoint.ContactPointSystem.Email,
            Value = "test@patient.ohh"
        }
    }
};
var carePlan = new CarePlan
{
    Period = new Period(FhirDateTime.Now(), null),
    Contained = new List<Resource>{patient},
    Subject = new ResourceReference("#patient"),
    InstantiatesCanonical = new List<string> { "PlanDefinition/cca2eaf3-03a9-46c0-88c6-e0287917cea6" }
};

var findPlan = client.Read<CarePlan>("CarePlan/aax4cxe5-03a9-46c0-88c6-e0287917cea6");

Console.Out.WriteLine(findPlan.InstantiatesCanonical.First());

var plan = client.Create(carePlan);

Console.Out.WriteLine(plan.InstantiatesCanonical.First());
}

var c = client.Create(carePlan);
const client = new Client();
return client.create({
    resourceType: "CarePlan",
    contained: [
      {
        resourceType: "Patient",
        id: "patient",
        identifier: [
          {
            system: "urn:oid:2.16.840.1.113883.2.4.99",
            value: "1234"
          }
        ],
        name: [
          {
            text: "Test Patient"
          }
        ],
        telecom: [
          {
            system: "email",
            value: "test@patient.ohh"
          }
        ]
      }
    ],
    instantiatesCanonical: [
      "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition/cca2eaf3-03a9-46c0-88c6-e0287917cea6"
    ],
    status: "active",
    intent: "proposal",
    subject: {
      reference: "#patient"
    },
    period: {
      start: "2021-03-16T13:32:37.430+01:00"
    }
  }
);
}
FhirClient.new

careplan = FHIR::CarePlan.new
careplan.period = FHIR::Period.new
careplan.period.start = Date.new(2021, 7, 9)

patient = FHIR::Patient.new
patient.id = 'patient'
patient_identifier = FHIR::Identifier.new
patient_identifier.system = 'urn:oid:2.16.840.1.113883.2.4.99'
patient_identifier.value = '1234'
patient.identifier = [patient_identifier]
patient.name = 'Test Patient'
patient_email = FHIR::ContactPoint.new
patient_email.system = 'email'
patient_email.value = 'test@patient.ohh'
patient.telecom = [patient_email]
careplan.contained = [patient]

careplan.instantiatesCanonical = 'PlanDefinition/cca2eaf3-03a9-46c0-88c6-e0287917cea6'

patient_reference = FHIR::Reference.new
patient_reference.reference = '#patient'
careplan.subject = patient_reference

FHIR::CarePlan.create(careplan)
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

with open('careplan.json', 'r') as file:
        careplan_json = json.load(file)

create_response = await client.execute ('CarePlan', method='post', data=careplan_json)
print(create_response)
file, err := os.Open("careplan/careplan.json")
if err != nil {
    return nil, err
}

req, err := http.NewRequest("POST", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan", file)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)
$client = new FhirClient();
$careplan = new FHIRCarePlan();

$patient = new FHIRPatient();
$patientName = new FHIRHumanName();
$patientName->setText('Test Patient');
$patientEmail = new FHIRContactPoint();
$telecomType = new FHIRContactPointSystem();
$telecomType->setValue('email');
$patientEmail->setSystem($telecomType)->setValue('test@patient.ohh');
$patientIdentifier = new FHIRIdentifier();
$patientIdentifier->setSystem('urn:oid:2.16.840.1.113883.2.4.99')->setValue('1234');
$patient->setId('patient')->addName($patientName)->addTelecom($patientEmail)->addIdentifier($patientIdentifier);
$careplan->addContained($patient);
$careplan->setSubject(new FHIRReference(['#patient']));

$careplan->setInstantiatesCanonical([new FHIRCanonical('PlanDefinition/cca2eaf3-03a9-46c0-88c6-e0287917cea6')]);
$period = new FHIRPeriod();
$period->setStart([new FHIRInstant('2021-03-16T13:32:37.430+01:00')]);
$careplan->setPeriod($period);

$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($careplan)
]);
$res = curl_exec($ch);
curl_close($ch);
$createdCarePlan = new FHIRCarePlan(json_decode($res, true));

Get CarePlan

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan/3"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");
CarePlan carePlan = client.read(
    .resource(CarePlan.class)
    .withId(3L)
    .execute();
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");
var app = client.Read<CarePlan>("CarePlan/1");

Console.Out.WriteLine(app.Description);
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const carePlan = await client.request('CarePlan/1');
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

FHIR::CarePlan.read(3)
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

carePlan = await client.resource('CarePlan').execute('1', 'GET')
req, err := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan/1", nil)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)

unmarshaller, err := jsonformat.NewUnmarshaller(config.TimeZone, config.FhirVersion)
if err != nil {
    return nil, err
}

bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
    return nil, err
}

unmarshal, err := unmarshaller.Unmarshal(bytes)
if err != nil {
    return nil, err
}

unmarshal.(*r4pb.ContainedResource).GetCarePlan()
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan/4?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$carePlan = new FHIRCarePlan(json_decode($res, true));

When a care plan is scheduled by a doctor via Open HealthHub the patient will be notified by email. They will receive a link and pin code to access the program. When a care plan is scheduled through a connected EHR system the link (with pin code) will be given in the CarePlan response and can then be integrated where needed in the requesting application. The link will be specified in an extension with url http://openhealthhub.com/fhir/StructureDefinition/improve-program-link.

The APIs can be used to connect Open HealthHub to, for example, your EHR or scheduling software.

This endpoint retrieves an CarePlan by id or creates based on an CarePlan request.

Creating a CarePlan

For creating new CarePlan the following fields are required (on top of the FHIR requirements):

Field Description Example Value
subject.actor There should be exactly on patient subject
subject.actor.name.text The patient subject should have a name specified in the text field, other properties will be ignored. If no name present the email address of the patient will be used Test Patient
subject.actor.telecom The patient subject should have a telecom element of type 'email', if multiple email addresses are found the first one will be used test@patient.ohh
subject.actor.identifier.value The patient should have a patient number, this should be identifiable by the system creating the care plan. 564372819
subject.actor.identifier.system The patient identifier, this should be the system of the healthcare institution. urn:oid:2.16.840.1.113883.2.4.99
instantiatesCanonical The instantiatesCanonical should hold a canonical URI according to FHIR specifications, Open Health Hub is a bit more lenient and accepts a reference to the PlanDefinition (i.e. Improve Program) for which the patient is invited PlanDefinition/cca2eaf3-03a9-46c0-88c6-e0287917cea6
period.start A start date should be entered when the patient can start the program. When retrieving a care plan we will always fill the end date. If no end date is known, the start date will be used. 2021-03-16T13:32:37.430+01:00

HTTP Request

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan/<ID>

POST https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/CarePlan

URL Parameters

Parameter Description
ID The ID of the CarePlan to retrieve

VitalSigns

Get Observation

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Observation/1"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

Observation observation = client.read()
      .resource(Observation.class)
      .withId(1)
      .execute()
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");
var obs = client.Read<Observation>("Observation/1");
Console.Out.WriteLine(obs.Category[0].Text);
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const observation = await client.request("Observation/1");
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

FHIR::Observation.read(1)
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

obs = await client.resource('Observation').execute('1', 'GET')

print(obs.resource_type, obs.id)
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Observation/4?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$observation = new FHIRObservation(json_decode($res, true));

Search Observation

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Observation?identifier=identifier&device-name=devicename"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

Bundle observationBundle = client.search()
      .forResource(Observation.class)
      .where(Patient.IDENTIFIER.exactly().identifier("1234"))
      .where(Device.DEVICE_NAME.matches().value("deviceName"))
      .returnBundle(Bundle.class)
      .execute();
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

var obs = client.Read<Observation>("Observation/1");
Console.Out.WriteLine(obs.Category[0].Text);
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const bundle = await client.request("Observation?identifier=1234&device-name=testDevice");
const observations = bundle.entry.map(entry => entry.resource);
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

 params = {
  'identifier': '1234',
  'device-name': 'testDevice'
}
FHIR::Observation.search(params)
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

observations = await client.resources('Observation').search(identifier='identifier', device_name='devicename').fetch()

for o in observations:
    print(o.as_json())
req, err := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Observation/1", nil)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Observation?device-name=deviceName&identifier=1234?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$bundle = new FHIRBundle(json_decode($res, true));

Having accurate patient’s vital signs available whenever and wherever needed is crucial for medical professionals to provide the best care possible. Vital signs acquired via Improve Connected Health can be monitored on Improve Dashboard, but also added to, for example, the patient’s EHR file.

The APIs can be used to (automatically) add vital signs retrieved via Open HealthHub in any of the supported formats to your EHR system(s), or to integrate or create a different dashboard.

HTTP Request

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Observation/<ID>

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Observation/search?identifier=<PATIENT-NUMBER>&device-name=<DEVICE-NAME>

URL Parameters

Parameter Description
ID The ID of the Observation to retrieve
PATIENT-NUMBER the patient-number to retrieve measurements Observations for
DEVICE-NAME name of the device that the measurements were made with

PlanDefinition

Get PlanDefinition

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

PlanDefinition planDefinition = client.read()
    .resource(PlanDefinition.class)
    .withId("57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca")
    .execute();
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/", settings);
var qr = client.Read<PlanDefinition>("PlanDefinition/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca");
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const resp = await client.request("PlanDefinition/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca");
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

FHIR::PlanDefinition.read('57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca')
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

response = await client.resource('PlanDefinition').execute('57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca', 'GET')

print(response)
req, err := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca", nil)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)

unmarshaller, err := jsonformat.NewUnmarshaller(config.TimeZone, config.FhirVersion)
if err != nil {
    return nil, err
}

bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
    return nil, err
}

unmarshal, err := unmarshaller.Unmarshal(bytes)
if err != nil {
    return nil, err
}

unmarshal.(*r4pb.ContainedResource).GetPlanDefinition()
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$planDefinition = new FHIRPlanDefinition(json_decode($res, true));

Search PlanDefinition

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition?definition=Questionnaire/97f680b9-e397-4298-8c53-de62a284c806"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

Bundle bundle = client.search()
    .forResource(PlanDefinition.class)
    .where(PlanDefinition.DEFINITION.hasId("Questionnaire/97f680b9-e397-4298-8c53-de62a284c806"))
    .returnBundle(Bundle.class)
    .execute();
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");
var qr = client.Search<PlanDefinition>(new []{"definition=Questionnaire/97f680b9-e397-4298-8c53-de62a284c806"});

qr.Entry.ForEach(component => Console.WriteLine(component.FullUrl));
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const bundle = await client.request("PlanDefinition?definition=Questionnaire/97f680b9-e397-4298-8c53-de62a284c806");
const responses = bundle.entry.map(entry => entry.resource);
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

params = {
  'definition': 'Questionnaire/97f680b9-e397-4298-8c53-de62a284c806'
}
FHIR::PlanDefinition.search(params)
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

response = await client.resources('PlanDefinition').search(definition='Questionnaire/97f680b9-e397-4298-8c53-de62a284c806').fetch()

print(response)
req, err := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition?definition=Questionnaire/97f680b9-e397-4298-8c53-de62a284c806", nil)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition?definition=Questionnaire/97f680b9-e397-4298-8c53-de62a284c806?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$bundle = new FHIRBundle(json_decode($res, true));

The program as created in Improve Designer.

This endpoint can be used to retrieve a PlanDefinition by id, or search using a small set of parameters.

HTTP Request

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition/<ID>

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/PlanDefinition?definition=Questionnaire/<MODULE_UUID>&publisher=<PUBLISHER_NAME>

URL Parameters

Parameter Description
ID UUID of the PlanDefinition to retrieve
MODULE_UUID (optional) UUID of the Questionnaire that is part of this PlanDefinition
PUBLISHER_NAME (optional) name of the user that published the PlanDefinition

Questionnaire

Get Questionnaire

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Questionnaire/781dc338-a932-4c0e-8c02-8d419fad4264"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

Questionnaire questionnaire = client.read()
      .resource(Questionnaire.class)
      .withId("781dc338-a932-4c0e-8c02-8d419fad4264")
      .execute();
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");
var questionnaire = client.Read<Questionnaire>("Questionnaire/781dc338-a932-4c0e-8c02-8d419fad4264");

Console.Out.WriteLine(questionnaire.Description);
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const questionnaire = client.request("Questionnaire/781dc338-a932-4c0e-8c02-8d419fad4264");
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

FHIR::Questionnaire.read("781dc338-a932-4c0e-8c02-8d419fad4264")
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

questionnaire = await client.resource('Questionnaire').execute('781dc338-a932-4c0e-8c02-8d419fad4264', 'GET')


print(questionnaire.description)
req, err := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Questionnaire/781dc338-a932-4c0e-8c02-8d419fad4264", nil)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)

unmarshaller, err := jsonformat.NewUnmarshaller(config.TimeZone, config.FhirVersion)
if err != nil {
    return nil, err
}

bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
    return nil, err
}

unmarshal, err := unmarshaller.Unmarshal(bytes)
if err != nil {
    return nil, err
}

unmarshal.(*r4pb.ContainedResource).GetQuestionnaire()
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Questionnaire/781dc338-a932-4c0e-8c02-8d419fad4264?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$questionnaire = new FHIRQuestionnaire(json_decode($res, true));

Patients interact with healthcare professionals through questionnaires on Improve Mobile App. A Questionnaire can either be selected from Improve Library or custom made in Improve Designer. Improve Designer allows the user to specify custom codings for questions. These codings can be used to map data in this resource to existing data. The unfilled-out questionnaire is made available in FHIR format through this end-point. Generally the Questionnaire item components retrieved through this endpoint correspond to questions created in the Improve Designer. An exception to this is the readonly text items that are generated for the formulas defined in the Improve Designer. These are made so that even if a result slide is not defined (where the formula is mentioned as a sub item), the formula will still be in the questionnaire.

This endpoint retrieves a Questionnaire by id.

HTTP Request

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Questionnaire/<ID>

URL Parameters

Parameter Description
ID The ID of the Questionnaire to retrieve

QuestionnaireResponse

Get QuestionnaireResponse

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

QuestionnaireResponse questionnaireResponse = client.read()
    .resource(QuestionnaireResponse.class)
    .withId("57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca")
    .execute();
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/", settings);
var qr = client.Read<QuestionnaireResponse>("QuestionnaireResponse/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca");
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const resp = await client.request("QuestionnaireResponse/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca");
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

FHIR::QuestionnaireResponse.read('57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca')
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

response = await client.resource('QuestionnaireResponse').execute('57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca', 'GET')

print(response)
req, err := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca", nil)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)

unmarshaller, err := jsonformat.NewUnmarshaller(config.TimeZone, config.FhirVersion)
if err != nil {
    return nil, err
}

bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
    return nil, err
}

unmarshal, err := unmarshaller.Unmarshal(bytes)
if err != nil {
    return nil, err
}

unmarshal.(*r4pb.ContainedResource).GetQuestionnaireResponse()
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse/57a1f708-d9cf-4d8c-9f25-b5a450e7f0ca?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$questionnaireResponse = new FHIRQuestionnaireResponse(json_decode($res, true));

Search QuestionnaireResponse

curl "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse?patient.identifier=6226217e&based-on=97f680b9-e397-4298-8c53-de62a284c806"
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

Bundle bundle = client.search()
    .forResource(QuestionnaireResponse.class)
    .where(QuestionnaireResponse.BASED_ON.hasId("97f680b9-e397-4298-8c53-de62a284c806"))
    .and(QuestionnaireResponse.PATIENT.hasChainedProperty(Patient.IDENTIFIER.exactly().identifier("6226217e")))
    .returnBundle(Bundle.class)
    .execute();
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");
var qr = client.Search<QuestionnaireResponse>(new []{"patient.identifier=6226217e", "based-on=97f680b9-e397-4298-8c53-de62a284c806"});

qr.Entry.ForEach(component => Console.WriteLine(component.FullUrl));
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const bundle = await client.request("QuestionnaireResponse?based-on=PlanDefinition/97f680b9-e397-4298-8c53-de62a284c806&patient.identifier=6226217e");
const responses = bundle.entry.map(entry => entry.resource);
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

params = {
  'based-on': 'PlanDefinition/97f680b9-e397-4298-8c53-de62a284c806',
  'patient.identifier': '6226217e'
}
FHIR::QuestionnaireResponse.search(params)
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

response = await client.resources('QuestionnaireResponse').search(based_on='PlanDefinition/97f680b9-e397-4298-8c53-de62a284c806', patient__identifier='6226217e').fetch()

print(response)
req, err := http.NewRequest("GET", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse?based-on=PlanDefinition/97f680b9-e397-4298-8c53-de62a284c806&identifier=6226217e", nil)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)
$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse?based-on=PlanDefinition/97f680b9-e397-4298-8c53-de62a284c806&patient.identifier=6226217e?_format=json';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
]);
$res = curl_exec($ch);
curl_close($ch);
$bundle = new FHIRBundle(json_decode($res, true));

The patient’s filled out questionnaire is immediately visible for the medical professional on Improve applications.

The APIs can be used to add responses to, for example, the patient’s EHR file, or can be used to integrate or create a different dashboard.

This endpoint can be used to retrieve a QuestionnaireResponse by id, or search using a small set of parameters. Note when using the search endpoint a Bundle containing the QuestionnaireResponses that match the search criteria will be returned.

The answers of the questionnaire will be encrypted using OpenPGP. Details on decrypting the response can be found in the Decrypting QuestionnaireResponse section.

HTTP Request

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse/<ID>

GET https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/QuestionnaireResponse?based-on.instantiates-canonical=PlanDefinition/<PROGRAM_UUID>&questionnaire=Questionnaire/<MODULE_UUID>&patient.identifier=<PATIENT_ID>&_since=<SINCE_DATE>

URL Parameters

Parameter Description
ID UUID of the QuestionnaireResponse to retrieve
PROGRAM_UUID (optional) UUID of the program this Questionnaire response belongs to
MODULE_UUID (optional) UUID of the Questionnaire that was answered
PATIENT_ID (optional) the id of the patient that answered the questionnaire in FHIR Token format
SINCE_DATE (optional) QuestionnaireResponses that were entered after this date in ISO 8601 format

Decrypting QuestionnaireResponse

The decrypted JSON with all answers

{
    "date_1": [
        {
            "value": "1981-07-29",
            "codes": []
        }
    ],
    "photo_1": [
        {
            "value": "base64EncodedImage",
            "codes": []
        }
    ],
    "hslider_1": [
        {
            "value": "5.5",
            "codes": []
        }
    ],
    "vslider_1": [
        {
            "value": "-9",
            "codes": []
        }
    ],
    "formula_1": [
        {
            "value": "-3.5",
            "codes": []
        }
    ],
    "number_1": [
        {
            "value": "42",
            "codes": []
        }
    ],
    "mchoice_1": [
        {
            "value": "Raspberry",
            "codes": [{
                "code": "7658-8",
                "system": "https://fhir.loinc.org/CodeSystem/loinc",
                "display": "Raspberry BasoBnd Ab Qn",
                "version": "2.71"
            }]
        }
    ],
    "mselect_1": [
        {
            "value": "Laptop",
            "codes": []
        },
        {
            "value": "Smartphone",
            "codes": []
        }
    ],
    "boolean_1": [
        {
            "value": "1",
            "codes": []
        }
    ],
    "open_2": [
        {
            "value": "It's the best questionnaire I've ever seen!",
            "codes": []
        }
    ]
}

The private key used to decrypt the examples from the sandbox

-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.10.10
Comment: https://openpgpjs.org

xYYEYQeklhYJKwYBBAHaRw8BAQdA7zkG2GrTERRlKqZRhbHEYARkzWjXt4Bj
RBx9PFEfwwb+CQMIwF4UP4mQC0bgQkxklm3EDi6SFHw1kfMzQ+gqpfaepGau
EPjpSqLN48gZJMznHK4UBe0xzu67OP6Y9UyT8SL2dj/gSVcku1MZvPZCEP2Y
Is0uU2FuZGJveCBBUEkyIDxlZGR5K3NhbmRib3gyQG9wZW5oZWFsdGhodWIu
Y29tPsKPBBAWCgAgBQJhB6SWBgsJBwgDAgQVCAoCBBYCAQACGQECGwMCHgEA
IQkQAyVBKg8H7ZgWIQSmxMhcFAXUQBd0uOYDJUEqDwftmLCiAQDVYGQ9eI+J
WL0PYwgrY7StV590qZar9S1dJvmGfkcGAgD/e/EBG7C5+Kd5KO2uCaS2q42B
HKzBUGVRYBjFQ8/SUQDHiwRhB6SWEgorBgEEAZdVAQUBAQdAum8OjYljCvYq
GCKW84Fub4/e/fp3l8dE/oZsrwArMwUDAQgH/gkDCJRAihNl5syQ4C/UKaHt
gM94KCfG48hvgIiOFrBp7ZBkNOl30H0r/mOLD6+BrvMFLHUG3H+OTIwsjDbc
Q5sHAE9tPVDXA98luvBQC/KNu2HCeAQYFggACQUCYQeklgIbDAAhCRADJUEq
DwftmBYhBKbEyFwUBdRAF3S45gMlQSoPB+2Y9IYBAMCPkUQO9r7Ot3nff45i
vThPkehhDk+MDb4zWW3xXVe7AP0ZXv6yBNGlsm2xFyGtVQH30nckhyBeKGcT
CEykt3SCBQ==
=X1eg
-----END PGP PRIVATE KEY BLOCK-----

Load private key:

gpg --import "../../sandbox.key"
privateKey = PGPainless.readKeyRing().secretKeyRing(ClientPerAnswerApplication.class.getResourceAsStream(PRIVATE_KEY_FILE));
keyRingProtector = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.fromPassword(PRIVATE_KEY_PASSPHRASE), privateKey);
var privateKey = File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "sandbox.key"));
var pgp = new Openpgp();
pgp.Keys.Add(new Key(privateKey));
pgp.Keys[0].Passphrase = PgpPassphrase;
const keys = await openpgp.key.readArmored(PRIVATE_KEY_FILE);
const key = keys.keys[0];
await key.decrypt(PRIVATE_KEY_PASSPHRASE);
this.decryptedPrivateKey = key;
GPGME::Key.import(File.open('../../sandbox.key'))
gpg = gnupg.GPG()
with open('../../sandbox.key') as file:
    gpg.import_keys(file.read(), passphrase='api-sandbox')
privateKey, err := ioutil.ReadFile("../sandbox.key")
$keyASCII = file_get_contents(dirname(__FILE__) . '/../../../../sandbox.key');
$gnupg = new gnupg();
$importKeyResult = $gnupg->import($keyASCII);
$fingerPrint = $importKeyResult['fingerprint'];
$gnupg -> adddecryptkey($fingerPrint, 'api-sandbox');
return $gnupg;

Determine if QuestionnaireResponse is encrypted

jq -r '.extension[] | select(.url="http://openhealthhub.com/StructureDefinition/encryptedAnswers") | .valueString'
boolean isEncryptedResponse(QuestionnaireResponse questionnaireResponse) {
    return questionnaireResponse.getMeta()
        .getProfile()
        .stream()
        .anyMatch(profile -> "http://openhealthhub.com/StructureDefinition/EncryptedQuestionnaireResponse".equals(profile.getValue()));
}
questionnaireResponse.Meta.Profile.Any(profile =>
                "http://openhealthhub.com/StructureDefinition/EncryptedQuestionnaireResponse".Equals(profile));
isEncryptedResponse(resp) {
  return resp.meta.profile.some(profile => profile === 'http://openhealthhub.com/StructureDefinition/EncryptedQuestionnaireResponse');
}
response.meta.profile.any? { |profile| profile == 'http://openhealthhub.com/StructureDefinition/EncryptedQuestionnaireResponse' }
encryptedProfile = 'http://openhealthhub.com/StructureDefinition/EncryptedQuestionnaireResponse'


def is_encrypted(resource: Resource):
    encProfile = list(filter(lambda p: encryptedProfile == p, resource.meta.profile))
    return len(encProfile) > 0

func isEncrypted(qr *questionnaire_response_go_proto.QuestionnaireResponse) bool {
    for _, p := range qr.Meta.Profile {
        if p.GetValue() == encryptedProfileUrl {
            return true
        }
    }
    return false
}
foreach ($resp->getMeta()->getProfile() as $profile) {
    if ($profile->getValue() == 'http://openhealthhub.com/StructureDefinition/EncryptedQuestionnaireResponse') {
        return true;
    }
}

return false;

Decrypt a String value

gpg -d
String decryptValue(String value, String extensionUrl) {
    DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
            .onInputStream(new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)))
            .decryptWith(keyRingProtector, privateKey)
            .doNotVerify()
            .build();
    byte[] bytes = decryptionStream.readAllBytes();
    decryptionStream.close();

    return new String(bytes, StandardCharsets.UTF_8);
}
pgp.Decrypt();
var decrypted = pgp.OutputMessage;
async decryptValue(value) {
  const messageObj = await openpgp.message.readArmored(value);

  const result = await openpgp.decrypt({message: messageObj, privateKeys: [this.decryptedPrivateKey]});
  return result.data;
}
crypto = GPGME::Crypto.new
crypto.decrypt(value, password: private_key_passphrase).to_s
dec = gpg.decrypt(encryptedAnswers[0].valueString)
print(dec)
message, err := helper.DecryptMessageArmored(string(privateKey), []byte("api-sandbox"), armored)
$gnupg->decrypt($value);

All answers in our QuestionnaireResponse are encrypted, so they can't be read unless you have the correct private key to decrypt the responses. The answers are encrypted in 2 separate ways.

Every answer is encrypted in the item -> answer field, using an extension with the prefix http://openhealthhub.com/fhir/StructureDefinition/encrypted- followed by the type of the answer (e.g. decimalType, stringType , attachment).

Because decrypting all answers one-by-one can have a big impact on performance, we also emit all answers in an encrypted JSON object. This is emitted in an extension at the top level of the QuestionnaireResponse with url http://openhealthhub.com/StructureDefinition/encryptedAnswers.

All answers are emitted as a JSON object with a value property that contains the actual value of the answer. If the answer has codes linked to it (as entered in Improve Designer), they will be put in a codes array (see the mchoice_1 answer on the left). Most questionnaire items will have a one-to-one match between their linkids and the ids for the answer value objects. For result questionnaire items result_<result_index>_formula_<formula_index> will be created to indicate first which item (result_<result_index>) and second (formula_<formula_index>) which result sub-item the answer is for. Finally all formula results will also be emitted as formula_<formula_id>.

For decrypting, the private key shown on the right can be used (passphrase: api-sandbox).

Some helper functions are displayed here to help you load the private key, determine if a QuestionnaireResponse is encrypted, and decrypt a value.

For a complete example please have a look at the FHIR QuestionnaireResponse Client Examples:

Language
C#
Go
Java
JavaScript
PHP
Python
Ruby
shell

Subscription

Create Subscription

curl  -X POST 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Subscription' \
      -H 'Content-Type: application/json' \
      -D '{
        "resourceType" : "Subscription",
        "status": "requested",
        "criteria": "Appointment?name=test",
        "channel" : {
          "type" : "rest-hook",
          "endpoint" : "https://your-webhook/endpoint",
          "header": ["Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"]
      }
  }'
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent()
        .setType(Subscription.SubscriptionChannelType.RESTHOOK)
        .setHeader(List.of(new StringType("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")))
        .setEndpoint("https://your-webhook/endpoint");
Subscription subscription = new Subscription()
        .setCriteria("Appointment?name=test")
        .setStatus(Subscription.SubscriptionStatus.REQUESTED)
        .setChannel(channel);

FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

Subscription subscription = client.create()
      .resource(subscription)
      .execute();
// we use nuget package Hl7.Fhir.R4
var client = new FhirClient("https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4");

var resource = new Subscription
{
    Status = Subscription.SubscriptionStatus.Requested,
    Criteria = "QuestionnaireResponse",
    Channel = new Subscription.ChannelComponent()
    {
        Type = Subscription.SubscriptionChannelType.RestHook,
        Header = new List<string>{"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"}
    }
};
var subscription = client.Create(resource);

Console.Out.WriteLine(subscription.Id);
const client = FHIR.client('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4');
const subscription = client.create({
  resourceType: 'Subscription',
  criteria: 'Appointment?name=test',
  status: 'requested',
  channel: {
    type: 'rest-hook',
    endpoint: 'https://your-webhook/endpoint',
    header: ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8']
  }
});
client = FHIR::Client.new('https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4')
FHIR::Model.client = client

subscription = FHIR::Subscription.new

subscription.criteria = 'Appointment?name=test'
subscription.status = 'requested'
subscription.channel = FHIR::Subscription::Channel.new
subscription.channel.type = 'rest-hook'
subscription.channel.endpoint = 'https://your-webhook/endpoint'
subscription.channel.header = ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8']

FHIR::Subscription.create(subscription)
client = AsyncFHIRClient(
    'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4'
)

with open('subscription.json', 'r') as file:
        subJson  = json.load(file)

createResponse = await client.execute('Subscription', method='post', data=subJson)

print(createResponse)
file, err := os.Open("subscription/subscription.json")
if err != nil {
    return nil, err
}

req, err := http.NewRequest("POST", "https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Subscription", file)
if err != nil {
    return nil, err
}
req.Header.Add("X-API-Key", "ad880601-b7e6-4d86-901d-b6fca96fc725")
if method == "POST" {
    req.Header.Add("Content-Type", "application/json")
}

response, err := http.DefaultClient.Do(req)
$subscription = new FHIRSubscription();
$status = new FHIRSubscriptionStatus(['requested']);
$subscription->setStatus($status);
$channel = new FHIRSubscriptionChannel();
$channelType = new FHIRSubscriptionChannelType();
$channelType->setValue('rest-hook')
$channel->setEndpoint('https://your-webhook/endpoint')->setType($channelType->setValue('rest-hook'))->addHeader(['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8']);
$subscription->setCriteria('Appointment?name=test')->setChannel($channel);

$url = 'https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Subscription';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($subscription)
]);
$res = curl_exec($ch);
curl_close($ch);
$createdSubscription = new FHIRSubscription(json_decode($res, true));

The endpoint can be used to query and create Subscriptions that trigger a webhook request when a Resource that matches the criteria has been updated. The content of the webhook will be empty, so a query with the same criteria needs to be executed to get the actual data.

HTTP Request

POST https://api.openhealthhub.com/OpenHealthhub/fhir-sandbox/4/Subscription

Subscription fields

Field Example value Description
channel.type rest-hook the only allowed allowed channel type
channel.headers ["header1:value1", "header2:value2"] custom headers to put on the webhook request
channel.endpoint http://yourserver:8080/yourPostEndpoint the endpoint that the webhook should post to
status requested the only allowed status
payload has to be empty
criteria QuestionnaireResponse?based-on=21bf742e-5220-4b17-8eb8-241d20028de1 for more info see subscription criteria and search