-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
1040 Update WH docs #2284
1040 Update WH docs #2284
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Ref. metriport/metriport-internal#1040 Signed-off-by: Rafael Leite <[email protected]>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,37 +20,39 @@ To enable this integration approach with Metriport: | |
1. Set this endpoint URL on the [Developers page](https://dash.metriport.com/developers) in the developer dashboard, | ||
or via the [Update Settings endpoint](/home/api-reference/settings/post-settings); | ||
1. This will generate a webhook key that should be used to authenticate requests on | ||
your app's endpoint (webhook). | ||
your app's endpoint (webhook) - see [authentication](#authentication) and | ||
[generating a new webhook key](#generating-a-new-webhook-key) below. | ||
|
||
General requirements for the endpoint: | ||
|
||
1. Public endpoint accessible from the internet; | ||
1. Does not do an HTTP redirect (redirects will not be followed); | ||
1. Accepts a `POST` HTTP request; | ||
1. Verifies requests by comparing the HTTP header `x-webhook-key` with the webhook key; | ||
1. Verifies requests using the HTTP header `x-metriport-signature` - see [authentication](#authentication) below; | ||
1. Accepts and responds to a [`ping` message](#the-ping-message); | ||
1. Be [idempotent](https://en.wikipedia.org/wiki/Idempotence) - it should accept being called more | ||
whan once with the same payload, with no changes to the end result. | ||
leite08 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Additionally, depending on what Metriport APIs you're using, your endpoint will need to accept and process | ||
different messages when they become available - specifically: | ||
Additionally, your endpoint will need to accept and process different messages when they become available - specifically: | ||
|
||
1. If using the Devices API - accept and process [user data messages](#devices-api-messages). | ||
1. If using the Medical API - accept and process [patient data messages](#medical-api-messages). | ||
1. [Patient data messages](#medical-api-messages). | ||
|
||
## Authentication | ||
|
||
When Metriport sends a webhook message, it includes an `x-metriport-signature` header. | ||
When Metriport sends a webhook message, it includes an `x-metriport-signature` header. | ||
|
||
- `x-metriport-signature`: This is an [HMAC](https://en.wikipedia.org/wiki/HMAC) SHA-256 hash computed using your webhook key and the body of the webhook message. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: formatting here is a bit weird - suggestion to combine the two: When Metriport sends a webhook message, it includes an |
||
|
||
At a high level, an HMAC works by taking a secret key and a message, and performing iterative hashes of the two to create | ||
a signature. That signature is compared against the signature in the header for equality. If the signatures are equal, you can trust the webhook | ||
payload is authentic and has not been tampered with. If they aren't equal, you should throw it away. | ||
At a high level, an HMAC works by taking a secret key (webhook key from the Settings page) and a message, | ||
and performing iterative hashes of the two to create a signature. That signature is compared against the | ||
signature in the header for equality. If the signatures are equal, you can trust the webhook | ||
payload is authentic and has not been tampered with. If they aren't equal, you should throw it away. | ||
|
||
You can use this header to verify that the webhook messages sent to your endpoint are from Metriport. Here's an example of how you can do this in Node.js: | ||
|
||
<Snippet file="webhook-signature-validation.mdx" /> | ||
|
||
The important thing is to make sure you use a trusted cryptography library in whatever language you choose to validate the webhook message in. | ||
The important thing is to make sure you use a trusted cryptography library in whatever language you choose to validate the webhook message in. | ||
You can also compute an HMAC in python: | ||
|
||
```python | ||
|
@@ -63,7 +65,7 @@ def compute_hmac(key, message, signature, digestmod=hashlib.sha256): | |
""" | ||
Verify the HMAC signature for a given message and key. | ||
|
||
:param key: The secret key (string). | ||
:param key: The webhook key from the Settings page (string). | ||
:param message: The message to be authenticated (string in JSON format). | ||
:param signature: The provided HMAC signature to verify against (string). | ||
:param digestmod: The hash function to use (defaults to hashlib.sha256). | ||
|
@@ -77,9 +79,9 @@ def compute_hmac(key, message, signature, digestmod=hashlib.sha256): | |
return signature == computed_signature | ||
|
||
# Example usage | ||
key = 'your_secret_key' # Your key | ||
key = 'your_secret_key' # The webhook key from the Settings page | ||
message = '{"webhookUrl": "https://api.app.com/webhook"}' # The message from the request in JSON format | ||
signature = 'the-request-signature' # The signature from the header | ||
signature = 'the-request-signature' # The signature from the header | ||
|
||
is_signature_valid = compute_hmac(key, message, signature) | ||
|
||
|
@@ -92,16 +94,18 @@ else: | |
``` | ||
|
||
<Warning> | ||
The previous method of authenticating webhooks, comparing your webhook key with the `x-webhook-key` in each request's HTTP header, is being deprecated on **December 9th, 2023**. | ||
Please update your implementation to use the `x-metriport-signature` header for authentication. | ||
The previous method of authenticating webhooks, comparing your webhook key with the | ||
`x-webhook-key` in each request's HTTP header, is being deprecated on **December 9th, 2023**. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's update the date, or take this out? we're a bit past There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missed that, removed! |
||
Please update your implementation to use the `x-metriport-signature` header for authentication. | ||
</Warning> | ||
|
||
### Generating a new webhook key | ||
|
||
If using the dashboard: simply delete your webhook URL on the [Developers page](https://dash.metriport.com/developers), save, and enter it again. | ||
If using the dashboard: simply delete your webhook URL on the [Developers page](https://dash.metriport.com/developers), | ||
save, and enter it again. | ||
|
||
If using the API: set the webhook URL to an empty string via the [Update Settings endpoint](/home/api-reference/settings/post-settings), and then set it | ||
to your desired URL making another request to the same endpoint. | ||
If using the API: set the webhook URL to an empty string via the [Update Settings endpoint](/home/api-reference/settings/post-settings), | ||
and then set it to your desired URL making another request to the same endpoint. | ||
|
||
## Format | ||
|
||
|
@@ -110,7 +114,6 @@ Webhook requests contain the relevant information on the body of the HTTP reques | |
There are several types of messages you can expect to receive: | ||
|
||
- [`ping`](#the-ping-message): validation of the webhook connection between Metriport and your app; | ||
- [Devices API messages](#devices-api-messages); | ||
- [Medical API messages](#medical-api-messages). | ||
|
||
In general, upon successful receiving of these messages, it's expected that your app responds with a `200` HTTP | ||
|
@@ -145,12 +148,6 @@ accept a `POST` request with this body... | |
You can check the [webhook mock server available on our repository](https://github.com/metriport/metriport/blob/master/utils/src/mock-webhook.ts) | ||
for a simple implementation of this message. | ||
|
||
### Devices API messages | ||
|
||
When using the Devices API, Metriport will send Webhook messages containing [new provider connections](/devices-api/more-info/webhooks#provider-connected-message) for each user, as well as [user data](/devices-api/more-info/webhooks#user-health-data-message) to your app from [our supported Providers](/devices-api/more-info/our-integrations), as soon as the data becomes available. | ||
|
||
You can see Webhook details specific to the Devices API on [this page](/devices-api/more-info/webhooks). | ||
|
||
### Medical API messages | ||
|
||
When using the Medical API, Metriport will send Webhook messages containing status updates to your app, as soon | ||
|
@@ -170,8 +167,9 @@ Example payload: | |
"meta": { | ||
"messageId": "<message-id>", | ||
"when": "<date-time-in-utc>", | ||
"type": "devices.health-data" | ||
} | ||
"type": "medical.medical.consolidated-data" | ||
}, | ||
... | ||
} | ||
``` | ||
|
||
|
@@ -191,7 +189,8 @@ The format follows: | |
</ResponseField> | ||
|
||
<ResponseField name="type" type="string" required> | ||
The type of the webhook message. If coming from the Devices API, this will be formatted `devices.<subtype>`, and if coming from the Medical API it will be formatted `medical.<subtype>`. | ||
The type of the webhook message. This can either be `ping` or one of the | ||
[Medical API types](/medical-api/more-info/webhooks#types-of-messages). | ||
</ResponseField> | ||
|
||
</Expandable> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,17 @@ You should expect to get more than one Webhook message per patient per request ( | |
To enable this integration approach with Metriport, and for some prerequesite reading to understand | ||
how the Webhook flow works, see [our Webhooks guide](/home/api-info/webhooks). | ||
|
||
### Types of Messages | ||
|
||
- `medical.document-download`: result of Document Query, containing the newly downloaded documents | ||
for the patient - see [details](#patient-document-data) below; | ||
- `medical.document-conversion`: result of converting the newly downloaded C-CDA documents into FHIR - | ||
see [details](#patient-document-data) below; | ||
- `medical.document-bulk-download-urls`: list of download urls for a patient's documents, see | ||
[details](#bulk-document-download-urls) below; | ||
- `medical.consolidated-data`: result of a Consolidated Data Query, containing the patient's data in FHIR | ||
format - see [details](#patient-consolidated-data) below. | ||
|
||
Comment on lines
+26
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while we're at it - let's add mention of these types to each endpoint page? ie for example https://docs.metriport.com/medical-api/api-reference/document/start-document-query should mention that |
||
### Passing Metadata | ||
|
||
You can pass metadata to endpoints that support webhooks, and you will receive the `meta.data` field of the webhook request. | ||
|
@@ -36,23 +47,25 @@ Below is an example payload you could send in the request body of one of those e | |
|
||
These are messages you can expect to receive in the following scenarios: | ||
|
||
1. When [queried documents](/medical-api/api-reference/document/start-document-query) have completed downloading (`type` for a patient in | ||
the message will be `document-download`, and at this point you'll be able to [download the raw files](/medical-api/api-reference/document/get-document)); | ||
1. When [queried documents](/medical-api/api-reference/document/start-document-query) have completed | ||
downloading, the message `type` will be `medical.document-download`, and at this point | ||
you'll be able to [download the raw files](/medical-api/api-reference/document/get-document); | ||
2. Then, if the downloaded documents contained C-CDA/XML files, when the conversion to FHIR has | ||
completed, the message `type` will be `medical.document-conversion`, and at this point | ||
you'll be able to query for [patient consolidated data](#patient-consolidated-data) in | ||
FHIR-compliant format. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for consistency - let's link to the endpoint they can use to do the query as per above, not the webhook message |
||
|
||
<Tip> | ||
Note that the webhooks will only contain updates for new data fetched in the current document | ||
query. | ||
</Tip> | ||
|
||
2. Then, if the downloaded documents contained C-CDA/XML files, when the conversion to FHIR has completed (`type` for a patient in the message | ||
will be `document-conversion`, and at this point you'll be able to query for [patient consolidated data](#patient-consolidated-data) in FHIR-compliant format). | ||
|
||
```json | ||
{ | ||
"meta": { | ||
"messageId": "<message-id>", | ||
"when": "<date-time-in-utc>", | ||
"type": "medical.document-bulk-download-urls" | ||
"type": "medical.document-download" | ||
"data": { | ||
youCan: "putAny", | ||
stringKeyValue: "pairsHere", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts/Qs:
200
HTTP response? Perhaps we should just tell them that, since we don't really care what they do on their end?200
right away?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, saying it should be idempotent is not the same thing as saying it should always return 200. They need to design their endpoint in a way that allows it being called multiple times w/ the same payload w/o breaking their app.
Good point, added!