Overview
A webhook is a simple event-notification system: When an event occurs in SAIVA, such as daily risk report containing daily risk predictions is ready for delivery to a consumer, a payload of JSON data containing information representing a report is sent via POST to a specified endpoint URL over secure HTTP.
Creating a webhook
In order to set up the integration, navigate to the Integrations
module of MySAIVA located at app.saiva.ai:
Note: You must be an Org Administrator in order to access this function
Choose Webhooks and click on Connect
to enter the setup screen.
The next screen contains a summary of all your current webhooks and will initially show an empty state:
Click on Create Webhook
to define your first webhook and enter all the required configuration fields:
-
Webhook Name: Your unique webhook name.
- This value will appear in the payload's web_hook_name property.
- Endpoint URL: Your endpoint URL. Must start with https://
- Report Type: Select the report type from the dropdown
- Quality Measure (QM): Select the predicted quality measure from the dropdown
- Facility list: Select All Facilities or specific facility names to include in the payload
Click Create
to create & save your webhook. A new screen will now show the Secret value associated with this webhook:
Copy the secret value by clicking the Copy
button and use it in your code to validate the signature of the incoming event. See the Authentication section for more information about how this signature should be used in your code.
Testing your webhook
You can test your webhook immediately after saving or from the Edit Webhook screen:
Start your webhook endpoint server. You can download some samples for node.js and Python from the saiva-api repository.
Note: Make sure you include the copied secret value in your code!
Click on Test
to initiate a test ping to the endpoint with the signature header based on the current secret and a sample, one-record payload. If the test succeeds, your webhook is complete! Next, implement your payload-specific logic in the code.
Each delivery will also include a saiva-event-id header property indicating a unique id associated with this delivery.
After your webhook has been created, from this screen, you can also:
- Update your webhook configuration: Modify the desired fields and click
Save
to save your new configuration. - Regenerate the webhook secret from this screen by clicking the
Re-generate
button. Remember to update your code with the new secret value!
When you're ready to go live, make sure you set the Enable
switch so that your webhook can be invoked whenever a new report is available (see the Service Level Agreement section).
Defining and managing multiple webhooks
You can define additional webhooks as long as settings do not overlap and by abiding the following rules:
- Each webhook must have a unique name, regardless of the configuration
- You cannot define multiple webhooks with the exact same QM, list of facilities and Report Type
- You may use the same endpoint for multiple webhooks. Make sure however, that the payload doesn't create any code logic conflicts and that your code supports multiple secrets (refer to the Authentication section in this page).
A duplicate webhook error will be shown upon save if uniqueness is violated.
You can manage your list of active webhooks from the Webhooks integration screen:
- Click on
Edit
(pencil) to show the Edit Webhook screen - Use the Enabled/Disabled switch to set the active state of your webhook
- Use the
Delete
(trashcan) button to delete your webhook
If one or more of your webhooks returned an error upon creation or after all possible retries have been exhausted (see: Retry Policy) it will be disabled and a corresponding message will reflect its disabled state:
Developing webhooks locally
In order to develop your webhook locally, you will need to use a Reverse Proxy so that you can tunnel traffic from a publicly accessible endpoint to your development box. There are a number of tools (free and paid) on the market that generate a public facing, secure HTTP URL. If you need some recommendations, please contact support@saiva.ai.
Service Level Agreement
For daily_risk_report, webhook will be invoked once a day by 8:00 am Eastern Standard Time. SLA is successful only if the webhook call is successful.
Authentication
SAIVA uses a digital signature which is generated using the secret key entered when creating a webhook and the body of the webhook’s request. This data is contained within the signature header property. SAIVA uses the following in order to compute the signature:
HMAC-SHA256 = {Base64(HmacSHA256(body_bytes, CLIENT_SECRET))}
To verify the request came from SAIVA, compute שמ HMAC digest using your secret key and the body and compare it to the signature portion (after the space) contained in the header. If they match, you can be sure the webhook was sent from SAIVA. Otherwise, make sure your code returns an unspecific error immediately without invoking any additional logic.
This signature header property contains the SHA algorithm used to generate the signature, a space, and the signature itself.
signature: sha256 HMAC-SHA256
e.g. signature: sha256 37d2725109df92747ffcee59833a1d1262d74b9703fa1234c789407218b4a4ef
Important: Implementation of signature verification is not mandatory for the webhook to function correctly, but is highly recommended in order to make sure your connected application is protected from denial-of-service and that it will not accidentally leak any information to unauthorized callers.
On the receiving end, this is how you verify the received HMAC
TypeScript example:
import crypto from 'crypto';
...
const verifyHmac = (
receivedHmacHeader: string,
receivedBody: any,
secret: string
): boolean => {
const hmacParts = receivedHmacHeader.split(' ')
const receivedHmac = hmacParts[1]
const hash = crypto
.createHmac('sha256', secret)
.update(receivedBody)
.digest('hex')
return receivedHmac == hash
}
JavaScript example:
const crypto = require('crypto');
...
const verifyHmac = (
receivedHmacHeader,
receivedBody,
secret
) => {
const hmacParts = receivedHmacHeader.split(' ')
const receivedHmac = hmacParts[1]
const hash = crypto
.createHmac('sha256', secret)
.update(receivedBody)
.digest('hex')
return receivedHmac == hash
}
Python example:
import hashlib, hmac
...
def verifyHmac(receivedHmacHeader, receivedBody, secret): hmacParts = receivedHmacHeader.split(' ') receivedHmac = hmacParts[1] hash = hmac.new(bytes(secret, 'UTF-8'), receivedBody, hashlib.sha256).hexdigest() return receivedHmac == hash
Note: If you are using the same endpoint to service multiple webhooks please make sure you add support in your code for verification of multiple signatures containing each webhook secret
Payload Description
Overview
Payload will vary depending on the selection of Report Type. SAIVA plans to add additional report types in the future and will publish their payload schema on the saiva-api Github repository.
Testing and pinging
During the webhook configuration process, when enable/disable changes or whenever the webhook is tested from the UI, SAIVA AI will issue special requests denoted in the type property as either "ping" or "test" to indicate the webhook's functionality & integrity is being tested. In both cases, the consuming party should return 200 Ok, otherwise the webhook will be disabled.
Ping & Test Examples:
{
"version": "v1",
"type": "ping"
}
{
"version": "v1",
"type": "test",
...
// Additional payload properties follow
}
Report Type: daily_risk_report
Example:
The payload will contain an array of report objects FacilityRiskReport, one per facility
{
"version": "v1",
"web_hook_name": "Sample RTH Daily Report For 10 Facilities", // From user webhook setting
"report_type": "daily_risk_report",
"report_date": "02/13/2023",
"client": "Sample",
"payload_length": 10,
"payload": [
{
// 1st facility risk report as FacilitRiskReport object
},
...
{
// 10th facility risk report FacilityRiskReport object
}
]
}
Schema:
See DailyRiskReport.json schema on GitHub.
Retry policy
In the event of a failed webhook request (due to timeout, a non HTTP 200 response, or network issues), SAIVA will attempt a maximum of 5 retries according to the following algorithm:
RETRY_DELAY_MINUTES = [1, 15, 60, 120, 240]
sidekiq_retry_in do |index, _exception|
seconds_delay = (RETRY_DELAY_MINUTES[index] || RETRY_DELAY_MINUTES.last) * 60
offset = rand(30) * (index + 1)
seconds_delay + offset
end
This formula increases the amount of time between each retry, while assigning a random number of seconds to avoid consistent failures from overload or contention.
SAIVA will attempt 5 retries over the course of 7 hours. When all attempts have been exhausted, the webhook will be disabled and an error message will be sent to the webhook owner via email.
The table below outlines the estimated wait time for each retry request, assuming that rand(30) always returns 0:
Retry number | Next retry in | Total waiting time |
---|---|---|
1 | 1m | 0h 1m |
2 | 15m | 0h 16m |
3 | 60m | 1h 16m |
4 | 120m | 3h 16m |
5 | 240m | 7h 16m |
Email notifications to the registered email address of the owner of the webhook will be sent in the following events:
- Whenever the first attempts fails (warning)
- Whenever the last attempt fails (fatal error, webhook disabled)
Code Samples
You can find an example node.js webhook implementation for node.js and Python in the saiva-api Github repository.
Get Help
For questions and support issues please contact support@saiva.ai
Comments
0 comments
Please sign in to leave a comment.