Implementation Guide

Overview

To embed a CTW component into your application, there are three main concepts to consider:

  • Install the component library and integrate it into your user interface (UI).
  • Verify that your users are authenticated and authorized to access the Zus Aggregated Profile (ZAP).
  • Set up the "data loop" between Zus and your own data backend.

Embed UI Components

The CTW component library is an open-source project that is easy to integrate into your existing application using React. While it is most compatible with React applications, these components can also be used with other frameworks. To get started, follow the instructions in the GitHub readme to install the library.

You can find an overview of all the components in the library, demos and examples, and technical documentation on how the components work in our Storybook documentation.

Auth

All CTW components communicate with the Zus Operational Data Store (ODS), a fully-featured FHIR server that provides data persistence for the Zus Aggregated Profile (ZAP). In order to access the Zus ODS, you must have a builder account. If you are an existing Zus customer, this was set up for you when you signed up. If you are interested in using Zus, you can request a sandbox account.

Authorization for all Zus services is handled using OAuth 2.0. There are several ways you can set up access in your application. If you're just getting started with the CTW components and want to try them out in the sandbox environment, the easiest option is to request a token through Postman (or any other HTTP client) and hardcode it in the authToken prop in CTWProvider. Please note that this is not a long-term solution and you should not commit the code to any source control. Additionally, these tokens are only valid for one hour, so this is not a proper solution for ongoing use.

Use Zus as the Identity Provider

Zus offers the ability to manage users and app clients through our auth API endpoints. If you are considering using Zus as an identity provider, you can find more information on access controls in our documentation.

OIDC Identity Token Exchange

If your application uses OIDC (OpenID Connect) compliant identity tokens, you can set up a "token exchange" with Zus to allow for a seamless user experience without requiring a "double login." With some configuration, you can use the Zus token exchange endpoint to provide an identity token and receive a Zus access token for the user. This allows you to continue using your own authentication solution while integrating with Zus.

User Proxy Token

Another option is to use the Zus user proxy token endpoint to retrieve a Zus access token for a user in your builder via a machine-to-machine connection. This option requires additional setup by Zus, so please contact us if you are interested in this approach.

Cross Builder Grants

If you are building a system with a need to further separate your own customers (such as an EHR), it is recommended to set up a Zus builder account for each of your customers. This allows patients and users to be scoped to each customer you serve with the necessary boundaries between them. However, you may want to work with Zus to determine the appropriate level of granularity for your customers. To allow your application to have access to all of these customers, you must set up a single builder for your organization that has "cross builder grants" to each of your customer builders. This ensures that your customers are properly separated and that your application has the necessary access to all of them. Check out these docs for more information on cross builder grants.

The "Data Loop"

While it is possible to build an application using the CTW component library on top of an empty instance of the Zus Operational Data Store (ODS), the full potential of the Zus Aggregated Profile (ZAP) can be realized once you have back-filled your patient population and set up automated synchronization between your data store and Zus.

To implement this data loop, you should use a process authorized by a machine-to-machine app client. You can back-fill your data using a manual script and then automate the process using similar code running on events in your backend. The implementation of this data loop will depend on the architecture of your data store and your visibility into data changes within your system.

Patients

To synchronize patients between your system and Zus, it is important to choose a unique identifier in your patient data store that can be used to identify the patient in Zus. The best option is to use the primary key or unique identifier for your patient records. You should also choose a system URL for this identifier, such as http://yourwebsite.com/patient-id. This identifier and system URL pair will be added to the identifier property on your patient FHIR resources in Zus and will be used as the patientId and systemURL passed to the PatientProvider component, which provides patient context to all components within it.

To send a patient FHIR resource to Zus, you should include as much demographic information as possible (such as address, telecom, etc.) to increase the chances of finding data on the network. The payload should include the following information:

payload = {
  "resourceType": "Patient",
  "active": true, // should be true if you are actively engaged in a treatment relationship with the patient
  "name": [
    {
      "use": "official",
      "family": "Smith",
      "given": ["John"],
    }
  ],
  "identifier": [
    {
      "system": "http://yourwebsite.com/patient-id",
      "value": "123456789",
    }
  ],
  "birthDate": "1987-11-15",
  "gender": "male"
    "address": [
    {
      "line": ["123 Main St"],
      "city": "New York",
      "state": "NY",
      "postalCode": "10001",
      "country": "US"
    }
  ],
  "telecom": [
    {
      "system": "phone",
      "value": "555-555-5555"
    },
    {
      "system": "email",
      "value": "[email protected]"
    }
  ]
}

The above payload should be sent as a PUT to https://{environment}.zusapi.com/fhir/Patient?identifier=http://yourwebsite.com/patient-id|123456789 (note how the identifier matches that of the payload). This will create the patient in Zus if they do not already exist (returning a status code of 201) or update the patient if they do already exist (returning a 200).

Additional Clinical Data (Optional)

While patient records are most critical to sync with Zus, some of the components work best when they can distinguish records that exist in your application and records coming from outside sources, such as a workflow to reconcile outside medications with those already documented in your system. In order to facilitate this it is important to sync all relevant clinical data between your system and Zus. This will be implemented similar to the patient example above using any FHIR resources, but notably Condition and MedicationStatement. Again, it is important to put some identifier on these records to map them back to your system. While the patient endpoint above offers the ability to perform upserts with a single call, all other FHIR resources must be managed with separate creates and updates. Deletes must be handled separately for all resources, including patients.

Fetching History

The above synchronization process only handles writing data to Zus, but does not cover fetching patient history from 3rd party network such as CommonWell, Surescripts, Carequality, and more. These can be triggered by calling the endpoints described in Patient History Retrieval.

Users

In addition to synchronizing clinical data, you also need to make sure that your user store is synchronized with Zus. To access Zus data, a user must have a Zus access token (see above for guidance on how to set this up). Once you have a way to obtain a Zus access token for each user, you must ensure that there are users in Zus with the appropriate roles and permissions for every user in your application that will be accessing Zus data.

If you have clinician users then you may want to create Practitioner resources for each user in ODS and link them to the created users. This will ensure proper Provenance is recorded for all resources generated by those users.

Back-fill vs Full Automation

When initially using Zus, you should perform a complete back-fill of all patients, clinical data, and users. This can typically be done using a one-time script. After the back-fill, it is important to have fully automated synchronization with as low latency as possible. The implementation of this data loop may depend on the architecture of your data store and your visibility into data changes in your system.

Getting Data Written to Zus

Some Zus components allow users to write data to Zus, such as when reconciling external conditions or medications and adding them to your system. In these cases, it is advisable to use ZusHooks, which allow you to subscribe to any updates in the ODS via webhooks. This can provide real-time integration between data written through Zus components and your application, making the experience seamless for end-users.