Webhooks
Webhooks allow partners to be notified when important events happen in XCover. When one of those events are triggered, we will send an HTTP POST payload to the webhook's configured URL. Webhooks can be used to send a customer notification, initiate a policy renewal process or perform any custom logic. To establish webhooks, partners can provide to their CSE: 1. Listener URL 2. Authentication Key & Secret 3. Support URL 4. Requested events (CSE may propose or suggest these as part of solutioning).
We will provide an HTTP signature generated on our end in Authorization
header and the api key itself in X-Api-Key
header. We will base the signature on your provided key pair. You can use the same HMAC
based algorithm for signature verification, if required. Please use the information from the signature header to check which hash algorithm is used in order to validate the request. Currently, webhook requests are signed using sha256
algorithm.
In case of multiple failures with the webhook notification, where the partner supplied endpoint does not respond with a 200 OK, we will try the webhook for up to 3 times.
Authentication
Your webhook API endpoint should implement HMAC authentication to verify that the API request was sent and signed by XCover.
import base64
import hashlib
import hmac
from urllib.parse import unquote
# You will provide your assigned Client Solution Engineer with an API key and secret
# they will configure the XCover platform
api_key = "--your-api-key--"
secret = "--your-secret--"
# This is just an example, you would obtain this from your server library
# E.g. a Flask server
# from flask import Flask, request
# app = Flask(__name__)
# @app.post('/my-xcover-webhook')
# def xcover_webhook():
#. request_headers = request.headers
# return do_everything_below()
request_headers = {
'X-Api-Key': '--api-key--',
'Authorization': '--signature--',
'Date': 'Thu, 27 Feb 2025 05:01:47 GMT'
}
# Extract signature from the request headers
auth_header = request_headers.get('Authorization', '')
if not auth_header.startswith('Signature '):
raise ValueError("Invalid Authorization header")
auth_parts = dict(
part.split('=', 1) for part in
auth_header.replace('Signature ', '').split(',')
)
received_signature = unquote(auth_parts.get('signature', '').strip('"'))
# Compute the expected signature
date_header = request_headers.get('Date')
if not date_header:
raise ValueError("Missing Date header")
raw = f"date: {date_header}"
expected_hash = hmac.new(
secret.encode("utf-8"),
raw.encode("utf-8"),
hashlib.sha256
).digest()
expected_signature = base64.b64encode(expected_hash).decode('utf-8')
is_valid = hmac.compare_digest(expected_signature, received_signature)
BOOKING_CREATED
Description: The payload is sent in a webhook to the Partner whenever the Booking is Created through an API Request.
{
'event': 'BOOKING_CREATED',
'payload': {
'id': '8AMKH-KQ8NR-INS',
'status': 'CONFIRMED',
'currency': 'USD',
'total_price': 68.71,
'total_price_formatted': 'US$68.71',
'partner_transaction_id': null,
'quotes': [
{
'id': '8bfb48ab-6947-4f34-a932-c4dcede958f6',
'policy_start_date': '2023-10-08T00:00:49.751227+00:00',
'policy_end_date': '2023-10-13T00:00:49.751247+00:00',
'status': 'CONFIRMED',
'price': 68.71,
'price_formatted': 'US$68.71',
'policy': {
'policy_type': 'travel_medical',
'policy_name': 'PolicyVersion 0',
'policy_version': 'e94df011-ee0b-44a1-bdb1-1dd05cb67fc7'
},
'duration': '5 00:00:00.000020',
'total_renewed_times': 0
}
]
}
}
BOOKING_UPDATED
Description: The payload is sent to the Partner whenever the Booking is Updated through an API Request
{
'event': 'BOOKING_UPDATED',
'payload': {
'id': 'TKBZN-XGPAX-INS',
'status': 'CONFIRMED',
'currency': 'USD',
'total_price': 68.71,
'total_price_formatted': 'US$68.71',
'partner_transaction_id': null,
'quotes': [
{
'id': '4d3b7919-9372-4c54-bdaf-05f770ffc2fc',
'policy_start_date': '2023-10-08T01:09:19.649112+00:00',
'policy_end_date': '2023-10-13T01:09:19.649133+00:00',
'status': 'CONFIRMED',
'price': 68.71,
'price_formatted': 'US$68.71',
'policy': {
'policy_type': 'travel_medical',
'policy_name': 'PolicyVersion 0',
'policy_version': '1637bc92-0299-4624-8f38-c9b9e15d379e'
},
'duration': '5 00:00:00.000021',
'total_renewed_times': 0
}
]
}
}
BOOKING_CANCELLED
Description: The payload is sent to the Partner whenever the Booking is cancelled through an API Request
{
'event': 'BOOKING_CANCELLED',
'payload': {
'id': 'HMZZ8-3YYHZ-INS',
'status': 'CANCELLED',
'currency': 'USD',
'total_price': 0.0,
'total_price_formatted': 'US$0.00',
'partner_transaction_id': null,
'quotes': [
{
'id': 'e0925616-824d-4216-bd18-450fb669c017',
'policy_start_date': '2023-10-08T01:19:41.766661+00:00',
'policy_end_date': '2023-10-13T01:19:41.766719+00:00',
'status': 'CANCELLED',
'price': 68.71,
'price_formatted': 'US$68.71',
'policy': {
'policy_type': 'travel_medical',
'policy_name': 'PolicyVersion 0',
'policy_version': 'dd5aef78-5ce5-4f19-a455-968780b89ea2'
},
'duration': '5 00:00:00.000058',
'total_renewed_times': 0,
'refund_value': 68.71
}
]
}
}
RENEWAL_CREATED
Description: Used to notify partner that the policy has been renewed.
Example:
{
"event": "RENEWAL_CREATED",
"payload": {
"id": "daSJp-fwdoj-REN",
"package_id": "GKDK9-CECQU-INS",
"quote_id": "c8ddb161-e3d9-455b-9621-96df90f17acb",
"status": "ACTIVE",
"start_date": "2019-02-25T13:00:00Z",
"notification_date": "2018-11-22T00:21:41Z",
"due_date": "2019-02-25T13:00:00Z",
"expiry_date": "2018-11-21T22:56:01Z",
"cancelled_on": null,
"paid_on": "2019-02-25T13:00:00Z",
"created_at": "2018-11-21T04:15:12.175468Z"
}
}
RENEWAL_NOTIFICATION
Description: Used to notify partner about approaching renewal.
Example:
{
"event": "RENEWAL_NOTIFICATION",
"payload": {
"id": "daSJp-fwdoj-REN",
"package_id": "GKDK9-CECQU-INS",
"quote_id": "c8ddb161-e3d9-455b-9621-96df90f17acb",
"status": "ACTIVE",
"start_date": "2019-02-25T13:00:00Z",
"notification_date": "2018-11-22T00:21:41Z",
"due_date": "2019-02-25T13:00:00Z",
"expiry_date": "2018-11-21T22:56:01Z",
"cancelled_on": null,
"paid_on": null,
"created_at": "2018-11-21T04:15:12.175468Z"
}
}
RENEWAL_DUE
Description: Sent on the renewal due date, the customer policy will be active until the end of the grace period (expiry_date
).
Example:
{
"event": "RENEWAL_DUE",
"payload": {
"id": "daSJp-fwdoj-REN",
"package_id": "GKDK9-CECQU-INS",
"quote_id": "c8ddb161-e3d9-455b-9621-96df90f17acb",
"status": "DUE",
"start_date": "2019-02-25T13:00:00Z",
"notification_date": "2018-11-22T00:21:41Z",
"due_date": "2019-02-25T13:00:00Z",
"expiry_date": "2018-11-21T22:56:01Z",
"cancelled_on": null,
"paid_on": null,
"created_at": "2018-11-21T04:15:12.175468Z"
}
}
RENEWAL_EXPIRED
Description: Sent on the renewal expiry date, the customer policy is no longer renewable after this event is triggered.
Example:
{
"event": "RENEWAL_DUE",
"payload": {
"id": "daSJp-fwdoj-REN",
"package_id": "GKDK9-CECQU-INS",
"quote_id": "c8ddb161-e3d9-455b-9621-96df90f17acb",
"status": "DUE",
"start_date": "2019-02-25T13:00:00Z",
"notification_date": "2018-11-22T00:21:41Z",
"due_date": "2019-02-25T13:00:00Z",
"expiry_date": "2018-11-21T22:56:01Z",
"cancelled_on": null,
"paid_on": null,
"created_at": "2018-11-21T04:15:12.175468Z"
}
}
Last updated
Was this helpful?