As a value- and/or risk-based care delivery provider, you strive to manage a patient’s continuum of care, within and outside of your organization. This involves timely interventions to events of interest, such as:

  • When a patient is admitted, discharged, or transferred (ADT) from an inpatient encounter
  • When the patient is prescribed a medication or picks up a medication
  • When a patient has a new lab result

Rather than requiring you to make API calls against the Zus FHIR Store to discover whether a data-generating event occurred, Zus can securely send a message to your URL.

What is a webhook?

A webhook is a user-defined HTTPS callback. When you build your application using Zus APIs, you may want to receive inbound HTTPS requests from Zus. These request payloads contain information about data creation and update activity in the Zus FHIR Store - for instance, when a patient picks up a prescription for a new medication.

👍

If you would like to configure a ZusHook for your account, please submit a support ticket to us here.

Getting Started with ZusHooks

To begin setting up your ZusHook, you will need to specify a webhook URL, filter criteria, and authentication method.

Webhook URL

To react to data and develop your own trigger-action workflow automations, you will need to tell Zus what URL to securely send these requests to and acknowledge that the request was received by your application with a standard HTTP 200 OK response.

Filter

ZusHooks will send an outbound HTTP request to your webhook URL when a FHIR resource is created or updated that matches the specified filter criteria. Filters are constructed as JQ queries and can be used to alert for resources that, for example:

  • Contain / do not contain a particular value such as a patient identifier system
  • Pertain to a set of Zus Universal Patient IDs
  • Have been updated to a completed status (e.g. Appointments, Encounters)
  • Come from a given data type such as ADT or Medication Alerts

It's also recommended that the customer implement a queue on their endpoint.

Authentication

Basic (username/password) and OAuth client credentials authentication (https) methods are supported.

OAuth values

  • webhookUrl
  • clientId
  • clientSecret
  • tokenUrl
  • audience

When secured with OAuth, Zus will send the entire FHIR resource in the response body:

{
    “message_id”: “123”,
    “source”: “com.zushealth.zushooks”,
    “time”: “2023-07-17T13:48:32Z”,
    “resourceId”: “Encounter/c020d8eb-ea59-492b-862f-c27f9556f628”,
    “ownerId”: “builder/abc",
    “resourceIncluded”: true,
    “resource”: {
      "meta": {
        "extension": [
            {
                "url": "https://zusapi.com/created-at",
                "valueInstant": "2023-07-17T13:48:29.991+00:00"
            }
        ],
        "versionId": "1",
        "lastUpdated": "2023-07-17T13:48:53.526+00:00",
        "tag": [
            {
                "system": "https://zusapi.com/accesscontrol/owner",
                "code": "builder/844ad945-13d1-4c18-9f03-8753df1579b3"
            },
            {
                "system": "https://zusapi.com/thirdparty/source",
                "code": "Sample ADT Network"
            }
        ]
    },
    "extension": [
        {
            "url": "https://zusapi.com/fhir/identifier/universal-id",
            "valueString": "75bf769f-d309-4243-a98d-0d12e339b468"
        }
    ],
    "identifier": [
        {
            "use": "official",
            "type": {
                "coding": [
                    {
                        "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
                        "code": "VN",
                        "display": "visit number"
                    }
                ],
                "text": "visit number"
            },
            "value": "185423165"
        }
    ],
    "status": "finished",
    "class": {
        "system": "http://terminology.hl7.org/CodeSystem/class-coding",
        "code": "E",
        "display": "Emergency"
    },
    "subject": {
        "reference": "Patient/f61b4027-d758-4686-ab96-e0e1d1bedc71",
        "type": "Patient",
        "display": "Grace Martin"
    },
    "participant": [
        {
            "individual": {
                "reference": "Practitioner/7927e01e-c251-45c5-8d1b-fb135afba689",
                "type": "Practitioner",
                "display": "VICTOR HOBBS"
            }
        }
    ],
    "period": {
        "start": "2023-07-11T01:09:00Z",
        "end": "2023-07-12T04:30:00Z"
    },
    "hospitalization": {
        "admitSource": {
            "text": "Home"
        },
        "dischargeDisposition": {
            "text": "Against Medical Advice"
        }
    },
    "location": [
        {
            "location": {
                "reference": "Location/9433cbaf-4cea-46d0-a45c-afc2c9f9503f",
                "type": "Location",
                "display": "Sample Medical Center"
            }
        }
    ],
    "serviceProvider": {
        "reference": "Organization/87984080-e35f-4a00-8295-bdfacfc368a8",
        "type": "Organization",
        "display": "Sample Medical Center"
    }
}

📘

Zus sends the full FHIR resource as part of the message for FHIR resources smaller than 1 MB.

For resources larger than 1MB, Builders can query the Zus FHIR store with the delivered resourceID.

For basic authentication schemes, Zus will send the FHIR resource ID and associated Universal Patient ID. You will need to query the Zus FHIR store to retrieve the full details of the resource:

{
  "message_id":"456",
  "source":"com.zushealth.zushooks",
  "time":"2023-07-17T13:48:32Z",
  "resourceId":"Encounter/c020d8eb-ea59-492b-862f-c27f9556f628",
  "ownerId":"builder/abc",
  "resourceIncluded":true,
  "resource":{"UPID":"75bf769f-d309-4243-a98d-0d12e339b468"}
}

The message payload also includes:

  • Message ID. There is no guarantee that a message will only be sent once.
  • Source. Supported sources are "com.zushealth.zushooks"
  • Time of message creation.
  • Resource ID.
  • Owner ID of the resource.
  • resourceIncluded. This will be set to false if using basic authentication or the resource size is greater than 1 MB.

Retry Behavior

Presently, if a customer's webserver returns any non-2xx HTTP response, the message will mark as failed and be returned to the input queue. Messages will be retried every 30 minutes until they succeed or have failed 5 times. Messages which fail 5 times are put on a dead-letter queue and can be manually redriven by Zus engineers within the 14 day message retention period.

Zushooks delivery is at-least once. If you have multiple Zushooks filters which match the same resource, automatic retries will cause duplicate messages on all matching filters. For example: a resource matches filterA, causing a successful message to webserverA, and that same resource matches filterB, which causes a failed message to webserverB. That resource will be retried in 30 minutes, so webserverB may see this as a duplicate. Your integration should keep track of already-seen messages, such that repeat delivery will not cause an issue.

Testing ZusHooks on your local development environment

ZusHooks require a publicly accessible URL. To approach this, you can deploy your application to a development or test server that is reachable from the public internet or use an HTTP tunneling tool like ngrok to set up a public URL for your application that maps to an application server running locally on your computer.

For convenience to quickly demonstrate proof of life, we will test with no authentication using webhook.site, which instantly provides a unique, random URL that allows us to receive webhooks without needing an internet-facing web server.

❗️

This testing should only be conducted on synthetic data.