Set up Webhooks
Webhooks allow the NexHealth Synchronizer API to send messages to you when certain events occur.
Registering an Endpoint
The first step to begin consuming NexHealth webhooks is to register a URL with us. Do this by making a POST request to /webhook_endpoints
curl --request POST \
--url 'https://nexhealth.info/webhook_endpoints' \
--header 'Accept: application/vnd.Nexhealth+json; version=2' \
--header 'Content-Type: application/json' \
--data '
{
"target_url": "https://yoururl.com/hooks"
}
'
Since our webhook messages may contain PHI, you must register a valid URL using HTTPS.
Upon successful registration, we will respond with the id of the new registered endpoint. Since your server will be listening on a public endpoint, the registered endpoint includes a secret key that will allow you to cryptographically verify the authenticity of the messages you receive.
Creating a Subscription
Once your server is listening to HTTPS requests on the registered route, you need to define what kind of messages you are interested in. This is called subscribing. Subscriptions are always defined as a dependent sub-resource to the endpoint that you registered previously. See the request below as an example of how to create your first subscription.
curl --request POST \
--url 'https://nexhealth.info/webhook_endpoints/id/webhook_subscriptions?subdomain=test' \
--header 'Accept: application/vnd.Nexhealth+json; version=2' \
--header 'Content-Type: application/json' \
--data '
{
"resource_type": "appointment",
"event": "appointment_insertion"
}
'
Notice, you are required to provide a subdomain to scope which institution's events you are subscribing to. The other required fields are the resource_type
and the event
. See the WebhookSubscriptions resource page for more details on these parameters.
Event Messages
Now that you have registered a URL and created a subscription with that URL, we will begin sending you messages anytime an event you subscribed to occurs within the subscribed subdomain. Hooks are sent via POST requests to the registered URL with the following headers:
Header | value |
---|---|
timestamp | ISO8601 Timestamp of sending the message |
signature | HMAC signature using SHA256 to encode the message with the endpoint secret_key |
content-type | application/json |
The JSON payload is structured in the following manner using a completed appointment insertion as an example:
{
"resource_type": "appointment",
"event_name": "appointment_insertion.complete",
"event_time": "2021-12-07T05:47:21.214+00:00",
"data": {
"appointment": {
// Fields shown are meant as a non-comprehensive example.
// Reference https://docs.nexhealth.com/reference/webhook-subscriptions for current details
"id": 1136829,
"patient_id": 55476535,
"provider_id": 415858,
"start_time": "2021-12-01T09:30:00.000Z",
"confirmed": true,
"patient_missed": false,
"created_at": "2021-12-05T16:30:53.471Z",
"updated_at": "2021-12-05T16:30:53.488Z",
"note": "Caleb Orozco has some concerns they want to discuss about their condition",
"end_time": "2021-12-01T09:45:00.000Z",
"unavailable": false,
"cancelled": false,
"timezone": "America/New_York",
"institution_id": 1,
"appointment_type_id": 1112,
"checkin_at": "2021-12-01T09:32:00.000Z",
"foreign_id": null,
"foreign_id_type": "nex",
"location_id": 75,
"last_sync_time": null,
"patient_confirmed": true,
"created_by_user_id": null,
"is_guardian": false,
"insurance_choice_type": null,
"insurance_plan_id": null,
"insurance_carrier_id": null,
"patient_confirmed_at": "2021-12-05T16:30:53.501Z",
"cancelled_at": null,
"is_new_clients_patient": null,
"confirmed_at": null,
"sooner_if_possible": false,
"operatory_id": 3,
"checked_out": false,
"checked_out_at": null,
"referrer": null,
"patient_name": "Caleb Orozco",
"provider_name": "Dr. Crawford",
"operatory_name": "Room 3",
"is_past": true,
"status": "paid",
"is_past_patient": true,
"insurance_description": null,
"created_at_with_tz": "2021-12-05T11:30:53.471-05:00",
"start_time_with_tz": "2021-12-01T04:30:00.000-05:00",
"end_time_with_tz": "2021-12-01T04:45:00.000-05:00",
"timezone_offset": "-05:00"
}
},
"delivery_errors": [
{
"code": "500",
"timestamp": "2021-12-06T05:53:44.844Z",
"message": "500 Internal Server Error",
"attempts": 1
}
]
}
Checking the webhook signature
NexHealth will always sign webhook events sent to your configured endpoints by including a signature in the signature
header, as well as a timestamp in the timestamp
header.
In order to verify the signature of the webhook request, you'll need to retrieve the webhook secret_key
given to you when you first created your webhook endpoint. If you don't have this, you'll need to recreate your webhook endpoint.
Step 1: Encode the payload for use in the signed_payload
string
signed_payload
string- Using any library of your choice, base64 encode the payload body that was sent to you in the webhook event
Step 2: Recreate the signed_payload
string by concatenating
signed_payload
string by concatenating- The timestamp directly from the
timestamp
header - The character
.
- The base64 encoded payload from Step 1
Step 3: Determine the expected_signature
expected_signature
- Compute an HMAC with the SHA256 hash function using the webhook endpoint's
secret_key
as the key, and thesigned_payload
string from Step 2 as the message
Step 4: Compare the signatures
- Compare the
expected_signature
from Step 3 to thesignature
header
Examples
import json
import hashlib
import hmac
import base64
def generate_signature(payload: dict, timestamp: str, secret_key: str):
# Separators are used to remove whitespaces;
# ensure_ascii=False will save the string as UTF-8 instead of the escaped sequence
payload_str = json.dumps(payload, separators=(',', ':'), ensure_ascii=False)
b64encoded = base64.b64encode(payload_str.encode())
# When concatenating don't forget to decode everything first
message = f"{timestamp}.{b64encoded.decode()}"
return hmac.new(secret_key.encode(), message.encode(), hashlib.sha256).hexdigest()
Webhook RetriesIf your application does not respond with a 2xx status code we will retry using the following schedule. If we continue to receive errors for 48 hours we will update the webhook endpoint "active" field to false and stop sending webhooks.
Retry # Total Time 1 30 sec 2 1.5 min 3 3.5 min 4 10 min 5 30 min 6 2 hours 7 5 hours 8 10 hours 9 24 hours 10 48 hours
Updated 11 months ago