Webhook API
What is a webhook?
A webhook is a lightweight, event-driven communication that automatically sends data between applications via HTTP.
— https://www.redhat.com/en/topics/automation/what-is-a-webhook
What problem do they solve?
Configure webhooks to receive notifications via HTTPS when certain events occur within Keelvar's systems.
A simple way to explain the purpose of webhooks is to analogise it to push notifications on a mobile phone. Say you are expecting a message from a friend. You can either:
- Every five minutes take your phone out of your pocket, unlock your phone, open your Messages app and check if you have received a message
- Leave your phone in your pocket, wait for your phone to make a notification sound, unlock your phone and open your Messages app only when you know you have received a message
The first situation is equivalent to you constantly polling an endpoint on the internet and checking to see if the data it returns has been updated. The sooner you want to know when an update has occurred, the more frequently you need to poll that endpoint. This consumes needless compute resources for both the polling server and the polled server and in extreme cases can lead to system degradation or rate limits being imposed.
Creating a webhook
Contact support@keelvar.com
and make a whitelist request for the domain of the downstream system that you
want your configured webhook to send HTTPS notifications to. Also ensure you have a valid
Keelvar API key.
Next, use the Webhook API as documented in the OpenAPI specification to create a new webhook. When creating a webhook you must choose at minimum:
- a name for ease of identification
- the URL of the receiving endpoint on the downstream system
- only HTTPS protocol is supported
- the receiving endpoint must be configured to accept
POST
requests
- which event within Keelvar's system the webhook should subscribe to
- e.g. Export API's event feed updated
Note: when a webhook is created successfully you will receive a JSON response which includes a field
called signing_key
.
The signing key is a secret
You must save signing_key
to a secure location now as you will only be shown this secret once.
More info on the usage of signing_key
is found in Using X-Signature
to verify a webhook's authenticity
import requests
import json
api_key = "YOUR_API_KEY"
headers = {"Authorization": f"Bearer {api_key}"}
url = "https://my.keelvar.app/api/webhooks"
payload = {
"name": "Basic Webhook",
"description": "Demonstration of the Keelvar Webhook API",
"signing_alg": "HMAC_SHA256",
"signing_key": "46178bda07c9661d4f8f1b324be1426af516e0a30ffd51a88cd45e47c019cbab",
"triggers": ["SOURCING_EVENTS_FEED_UPDATED"],
"url": "https://test.url.ie",
}
response = requests.post(url, headers=headers, json=payload)
# Show the Webhook creation result
print(json.dumps(response.json())
#> {
#> "description": "Demonstration of the Keelvar Webhook API.",
#> "name": "Basic Webhook",
#> "signing_alg": "HMAC_SHA256",
#> "signing_key": "46178bda07c9661d4f8f1b324be1426af516e0a30ffd51a88cd45e47c019cbab",
#> "triggers": "[SOURCING_EVENTS_FEED_UPDATED]",
#> "url": "https://test.url.ie",
#> "uuid": "4a55a230-307b-4bab-ac53-e95186b88968"
#> }
curl -X POST https://my.keelvar.app/api/webhooks \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_API_KEY>' \
-d '{
"name": "Basic Webhook",
"description": "Demonstration of the Keelvar Webhook API",
"signing_alg": "HMAC_SHA256",
"signing_key": "46178bda07c9661d4f8f1b324be1426af516e0a30ffd51a88cd45e47c019cbab",
"triggers": "[SOURCING_EVENTS_FEED_UPDATED]",
"url": "https://test.url.ie",
}'
#> {'description':'Demonstration of the Keelvar Webhook API.','name':'Basic Webhook','signing_alg':'HMAC_SHA256','signing_key': '46178bda07c9661d4f8f1b324be1426af516e0a30ffd51a88cd45e47c019cbab','triggers':'[SOURCING_EVENTS_FEED_UPDATED]','url':'https://test.url.ie','uuid':'4a55a230-307b-4bab-ac53-e95186b88968'}
Managing created webhooks
Facilities for retrieving the details of and deleting webhooks registered to your organisation are documented in the OpenAPI specification.
Examples
Get all details for a specific webhook.
import requests
import json
api_key = "YOUR_API_KEY"
headers = {"Authorization": f"Bearer {api_key}"}
url = "https://my.keelvar.app/api/webhooks/4a55a230-307b-4bab-ac53-e95186b88968"
response = requests.get(url, headers=headers)
# Show the Webhook GET result
print(json.dumps(response.json()))
#> {
#> "description": "Demonstration of the Keelvar Webhook API.",
#> "name": "Basic Webhook",
#> "signing_alg": "HMAC_SHA256",
#> "triggers": "[SOURCING_EVENTS_FEED_UPDATED]",
#> "url": "https://test.url.ie",
#> "uuid": "4a55a230-307b-4bab-ac53-e95186b88968"
#> }
curl -X GET https://my.keelvar.app/api/webhooks/4a55a230-307b-4bab-ac53-e95186b88968 \
-H 'accept: application/json' \
-H 'Authorization: Bearer <YOUR_API_KEY>' \
#> {'description':'Demonstration of the Keelvar Webhook API.','name':'Basic Webhook','signing_alg':'HMAC_SHA256','triggers':'[SOURCING_EVENTS_FEED_UPDATED]','url':'https://test.url.ie','uuid':'4a55a230-307b-4bab-ac53-e95186b88968'}
Delete a specific webhook.
Webhook secret rotation
Webhook signing keys are secrets which are stored both by you and by Keelvar. The signing keys are stored securely by Keelvar and are only decrypted when signing a webhook notification.
Signing keys do not automatically expire and users of the Webhook API are responsible for securely storing their copy of the signing key. To rotate a signing key for a given webhook, users can:
- create a new webhook using the same settings as the webhook to be deleted
- swap your copy of the old signing key with your copy of the new signing key in your downstream system
- delete the webhook corresponding to this signing key using the Webhook API
Following these steps will result in no missed or duplicate notifications as your downstream system should be configured to only accept notifications from the:
- old webhook prior to swapping the key
- new webhook after swapping the key
Testing webhooks
You can manually schedule sending a notification for a given webhook as outlined in the OpenAPI specification.
Provided the domain of the webhook's url
is whitelisted, you will shortly receive a test webhook notification at
your configured url
. This test notification's X-Signature
header will be created using based on the test notification's
request body which looks like the following:
Example snippets for scheduling a test notification are as follow:
import requests
import json
api_key = "YOUR_API_KEY"
headers = {"Authorization": f"Bearer {api_key}"}
url = "https://my.keelvar.app/api/webhooks/4a55a230-307b-4bab-ac53-e95186b88968/test"
response = requests.post(url, headers=headers)
# Show the Webhook GET result
print(json.dumps(response.json()))
#> {
#> "detail": "Webhook test notification to https://test.url.ie scheduled.",
#> }
Webhook security
Confidentiality
Keelvar will only send webhook requests over the HTTPS protocol to HTTPS enabled servers. Data contained in webhook notifications will be encrypted using your web server's public key prior to being sent over the public internet. Only your web server will be able to decrypt this data using its corresponding private key. Any third-party which intercepts this message in-transit will be unable to decipher its content.
Authenticity and integrity
Each webhook HTTPS request sent by Keelvar to your downstream systems will include a header called X-Signature
. This header includes
a Message Authentication Code which - in conjunction with a webhook's secret signing_key
, signing_alg
and the webhook's message
content - can be used to verify that an incoming webhook was created by Keelvar and that the bytes content of that message have
not been altered during transit.
Using X-Signature
to verify a webhook's authenticity
The following Python code snippet creates a signature using hmac-sha256
algorithm, a HTTP request body and a secret which is known
only to you and Keelvar (generated at time of webhook creation). Equivalent implementations in other programming languages should
align closely with this snippet using those languages' respective standard cryptographic libraries.
Currently only the hmac-sha256
signing algorithm is supported by the Webhook API for creating Message Authentication Codes,
but other algorithms may be added in the future.
import hashlib
import hmac
# Sample data for a request received by your downstream system
request_body = '{"triggers":"[LATEST_BIDS_FEED_UPDATED]","timestamp":"2021-09-11T03:43:37.151935Z","payload":{"sourcing_request":"310a8662-1bad-42ef-94cd-eeaf50f254fc","sourcing_event_name":"Test Event"}}'
request_X_Signature_header = "hmac-sha256=cbece13eb06622b3b032568f24d6c4aea11ed5e2cb40823ecf0723404a123f12"
# The secret singing_key generated when the webhook was first created
shared_secret_signing_key="d6d20aeae3e567a77bb43646115f32493c3edf8a0c1ad4de9ffa496a43edac3e"
# Recreate the signature using your copy of the secret signing_key and the request body
buffer = bytes(request_body, "utf-8")
signature = hmac.new(shared_secret_signing_key.encode("utf-8"), msg=buffer, digestmod=hashlib.sha256).hexdigest()
# Verify the signature you calculated matches the signature in the request header
assert f"hmac-sha256={signature}" == request_headers.get("X-Signature")
Dates, Times and Timezones
All dates and times throughout the entire API for both input and output are ISO 8601 standard and use UTC as the timezone. The following examples highlighting 1 second before midnight:
Zero offset UTC timezone
2030-12-31T23:59:59Z
2030-12-31T23:59:59+00:00
2030-12-31T23:59:59.000000Z