ClaimsGateway Webhooks Documentation

Webhooks are a way to integrate your applications with ClaimsGateway, allowing you to receive real-time updates whenever specific events occur on ClaimsGateway.

Table of contents

  1. Webhook requests
  2. Creating a webhook
    1. Event types
    2. Record types
  3. Validating request signatures
    1. Steps to signature verification
      1. 1. Verify if timestamp is within acceptable window
      2. 2. Generate the content to be hashed
      3. 3. Generate the HMAC hash
      4. 4. Compare the generated hash with the provided signature
    2. Example

Webhook requests

When an event occurs, ClaimsGateway will send a POST request to a specified URL.

The webhook body will be a JSON resource object that relates to the event. The request headers will include:

  • A content-type header set to application/json
  • A x-request-timestamp with the time the webhook was sent
  • A x-request-signature header containing the request signature (if configured). Read on for information about validating request signatures.

Below is an example of webhook event data.

{
  "record_type": "claim",
  "action_type": "claim_approved",
  "claim": {
    "id": 1,
    "structure_id": 1,
    "status": "open",
    "notified_at": "2026-01-01",
    "assignee_id": 1,
    "vulnerable_customer": false,
    "vulnerability_reason": "",
    "data": {
      "claimed_at": "2026-01-04T00:00:00.000+11:00",
      "description": "Description of claim.",
      "address_country": "AU",
      "address_street": "Gwenda Rapid",
      "address_city": "Lake Buddy",
      "address_state": "NSW",
      "address_postcode": "2362",
      "address": "Gwenda Rapid, Lake Buddy NSW 2362, Australia"
    },
    "sections": {
      "public_liability": {
        "description": "Sint impedit et. Quia molestiae veritatis. Cupiditate quia quae.",
        "items": [
          {
            "payee": "John Smith",
            "amount": "557.00"
          }
        ]
      }
    },
    "claimant": {
      "first_name": "John",
      "last_name": "Smith",
      "email": "john.smith@mail.test",
      "address_country": "AU",
      "address_street": "5629 Irving Camp",
      "address_city": "Park Acres",
      "address_state": "NSW",
      "address_postcode": "5185",
      "address": "5629 Irving Camp, 5185 NSW Park Acres, Australia",
      "phone": "422314646",
      "phone_country": "AU",
      "phone_formatted": "+61 422 314 646",
      "phone_normalised": "+61422314646"
    },
    "client": {
      "name": "Company PTY Ltd",
      "abn": "6883915880"
    }
  },
  "item": {
    "payee": "John Smith",
    "amount": "557.00"
  },
  "document": {
    "id": 1,
    "type": "photos"
  }
}

Important note: The structure of the claim object will differ depending on your configuration. Please refer to your swagger file to check the exact structure of the response.

Creating a webhook

To create a webhook you need to access ClaimsGateway’s Webhook configuration page:

  • Main Menu “Configurations” -> “Tenant”
  • Then “Webhooks” tab.

Screenshot of Webhooks page.

  • Click “New Webhook” and complete the form
    • Name: Write the name for the webhook
    • Endpoint: the endpoint of your application where the webhook is going to post the requests
    • Actions: Select the action to which you want to be notified
    • Enable event signature: if you want to add a signature to the webhook requests

Screenshot of Webhooks creation page.

  • After saving you can see the event signature secret.

Screenshot of Webhooks details page.

Event types

action_type record_type Description
claim_creation claim The claim is created as draft
claim_submission claim The claim is submitted. If you create a claim via API as submitted, you will receive both claim_creation and claim_submission events
claim_approved claim When claim indemnity was changed to approved
claim_declined claim When claim indemnity was changed to declined
items_settled claim When all expenses have been paid or declined
item_creation item When expense was created
item_submission item When expense was submitted
item_paid item When item was paid
item_declined item When item was declined
item_settled item When item was paid or declined
document_creation document When a document was created

Record types

Depending on the event, the webhook will send a claim, item or document. Structure of each of those records is documented on your Swagger file.

The nodes item and document are only sent if the record_type is item or document, respectively. The node claim is sent in all webhook events.

Validating request signatures

ClaimsGateway provides methods to verify that requests are genuinely coming from the ClaimsGateway API by using signatures included in the request headers.

Request headers

  • x-request-timestamp : The time the webhook was sent, represented in Unix epoch time format.
  • x-request-signature : The request signature, formatted as a SHA-256 HMAC hash. It uses the generated token secret when the webhook was created in the ClaimsGateway portal.

To ensure the authenticity of a webhook request from ClaimsGateway, validate the request signature using the provided headers and your webhook’s associated signing key.

Steps to signature verification

1. Verify if timestamp is within acceptable window

Check the x-request-timestamp header to ensure the request is recent. A request older than 5 minutes may indicate a replay attack.

2. Generate the content to be hashed

You will need to build a string with the following format:

HTTP METHOD (POST)
timestamp
webhook path
query params (if any)
request body

Example

POST
1445398140
/webhooks/claimsgateway
{"record_type":"claim","action_type":"claim_approved","claim":{}}

3. Generate the HMAC hash

Use the generated secret in ClaimsGateway and the SHA-256 hashing algorithm to generate the HMAC hash.

4. Compare the generated hash with the provided signature

Compare the generated HMAC hash with the x-request-signature header from the request. A match confirms the request’s legitimacy; otherwise, it should be considered potentially tampered with or fraudulent.

Example

Below is an example of how to confirm the signature in Ruby, Node.js and Python.

def verify_signature
  # ********* 1. VERIFY IF TIMESTAMP IS WITHIN ACCEPTABLE WINDOW *********

  # reads request header x-request-timestamp
  timestamp = request.headers['x-request-timestamp']
  # defines validity window
  timestamp_validity_window = 5.minutes

  # converts UNIX Timestamp to Ruby Time object
  time = Time.zone.at(timestamp.to_i)

  # checks if timestamp is within window
  return false if (time + timestamp_validity_window).past?

  # ********* 2. GENERATE THE CONTENT TO BE HASHED *********

  # reads request method
  method = request.method
  # reads request body
  body = request.body.read
  # Converts URL string from request to URI object
  uri = URI.parse(request.url)

  # Builds an array with all lines to sign
  lines = [
    method, # HTTP method (POST)
    timestamp, # Timestamp received in request header x-request-timestamp
    uri.path || '/', # URL path or '/' if empty
    uri.query, # adds query params
    body
  ]

  # Removes empty lines and joins everything separating by `\n`
  content_to_sign = lines.compact_blank.join("\n")

  # ********* 3. GENERATE THE HMAC HASH *********

  # Secret key from ClaimsGateway
  secret_key = '123456789'

  # reads request header x-request-signature
  signature = request.headers['x-request-signature']

  # initialize hmac object
  hmac = OpenSSL::HMAC.new(secret_key, OpenSSL::Digest.new('sha256'))

  # updates hmac object with the string to sign
  hmac.update(content_to_sign)

  generated_hash = hmac.hexdigest

  # ********* 4. COMPARE THE GENERATED HASH WITH THE PROVIDED SIGNATURE *********

  # Use secure comparison between signature received in request header and
  ActiveSupport::SecurityUtils.secure_compare(signature, generated_hash)
end
const crypto = require('crypto');

function verifySignature(req, body) {
  const timestamp = req.headers['x-request-timestamp'];
  if (!timestamp) return false;

  const timestampValidityWindow = 5 * 60 * 1000;
  const requestTime = parseInt(timestamp, 10) * 1000;

  if (Date.now() - requestTime > timestampValidityWindow) return false;

  const fullUrl = new URL(req.url, `http://myserver.com`);
  const path = fullUrl.pathname || '/';
  const query = fullUrl.search ? fullUrl.search.slice(1) : null;

  const lines = [req.method, timestamp, path, query, body];
  const contentToSign = lines.filter(Boolean).join('\n');

  const secretKey = '123456789';
  const signature = req.headers['x-request-signature'];
  if (!signature) return false;

  const generatedHash = crypto
    .createHmac('sha256', secretKey)
    .update(contentToSign)
    .digest('hex');

  const signatureBuffer = Buffer.from(signature);
  const hashBuffer = Buffer.from(generatedHash);

  if (signatureBuffer.length !== hashBuffer.length) return false;

  return crypto.timingSafeEqual(signatureBuffer, hashBuffer);
}
import hmac
import hashlib
import time
from urllib.parse import urlparse

SECRET_KEY = b'123456789'

def verify_signature(headers, method, path_and_query, body):
    # ********* 1. VERIFY TIMESTAMP *********
    timestamp = headers.get('x-request-timestamp')
    if not timestamp:
        return False

    try:
        request_time = int(timestamp)
    except ValueError:
        return False

    # 5-minute validity window (300 seconds)
    validity_window = 300
    if time.time() - request_time > validity_window:
        return False

    # ********* 2. GENERATE CONTENT TO BE HASHED *********
    parsed_url = urlparse(path_and_query)
    path = parsed_url.path or '/'
    query = parsed_url.query or None

    lines = [
        method,
        timestamp,
        path,
        query,
        body.decode('utf-8')
    ]

    # Filter out empty/None values and join with '\n' (equivalent to compact_blank)
    content_to_sign = '\n'.join([line for line in lines if line])

    # ********* 3. GENERATE HMAC HASH *********
    signature = headers.get('x-request-signature')
    if not signature:
        return False

    generated_hash = hmac.new(
        SECRET_KEY,
        content_to_sign.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    # ********* 4. COMPARE HASHES SAFELY *********
    return hmac.compare_digest(signature, generated_hash)