Skip to main content
In addition to standard SFTP data exports, the Thanx platform supports a variety of webhooks. These webhooks allow for integrating platforms to respond in realtime to changes to data resources in the Thanx platform. If you are an existing integration partner, please reach out to our team at developer.support@thanx.com to have webhooks enabled.

Requirements

Webhook endpoints must be valid HTTPS URLs. Requests will be sent as POST requests and expect the receiving servers to respond to requests within 15 seconds. By default, webhooks are not configured to retry if the receiving server responds with an error. Any missed data can be collected via bulk data transfer mechanisms.

Delivery Semantics

Webhook delivery is not exactly-once. The same event can be delivered more than once, and each individual delivery is best-effort — failed deliveries are not retried (see Requirements), so delivery of any single event is not guaranteed. Duplicates are the common case, and this is most pronounced for purchase webhooks: a single purchase is re-sent as it moves through authorization and settlement, and across payment rails, with every delivery carrying the same purchase id. Design your consumer to be idempotent: deduplicate on the payload’s stable identifier (for purchases, this is id) and treat repeat deliveries as updates rather than new events. Where a field can be refined over a resource’s lifecycle (such as a purchase amount), prefer the latest delivery. The payload carries no delivery sequence or timestamp, so treat the last delivery you receive for an id as the current state; for purchases, authorization and settlement fires are typically days apart, so arrival order tracks lifecycle order.

Query Parameters

When configuring a webhook endpoint, query parameters may be appended to the URL. These parameters will be included with every webhook request. Example: https://example.com/webhook?client_id=abc123&env=sandbox In this example, Thanx will send each POST request to the exact URL above, including the query parameters. Notes
  • Query parameters are static and defined at the time the webhook URL is registered.
  • They are not modified or validated by Thanx.
  • Sensitive values should not be placed in query parameters.
When an integration authenticates via a static query parameter (e.g. an apiKey), that value is fixed at registration and sent on every delivery. Rotating it requires re-registering the webhook URL with Thanx — a stale credential will cause your endpoint to reject deliveries (e.g. 401).

Verification

To verify the authenticity of webhook requests, each webhook request includes a X-Thanx-Signature header that can be used to verify that the webhook was initiated by the Thanx platform. The X-Thanx-Signature is a hex-encoded HMAC-SHA256 signature of the request payload, using a webhook secret that can be provided by the Thanx team. Example Verification:
require 'json'
require 'openssl'

# value of the `X-Thanx-Signature` request header
signature = '425bf0e847d0647d6e3449c7c1cddadc2095a98c954a7549da0d01c417b833fd'
# webhook secret provided by Thanx
secret = 'f19ce124-48e4-45fb-b39b-4f9e3e1d4b1b'
# webhook request payload body
payload = {
  "purchase": {
    "id": "bd4163e4ea5e168431fdc4b8d1ea061f",
    "amount": "7.76",
    "purchased_at": "2024-03-26T21:38:36.000Z",
    "event": "create",
    "user": {
      "id": "675c038cfc5f43591718936c18080855",
      "email": "example@thanx.com",
      "first_name": "Thanx",
      "last_name": "Example"
    },
    "merchant": {
      "id": "a91f75a0dc2cc54a62ad8a1a05b9fa6b",
      "name": "Thanx Restaurant"
    },
    "location": {
    },
    "order": {
    },
    "products": [
      "Latte"
    ]
  }
}.to_json

# the following will return true
OpenSSL::HMAC.hexdigest('sha256', secret, payload) == signature
import hashlib
import hmac
import json

# value of the `X-Thanx-Signature` request header
signature = '425bf0e847d0647d6e3449c7c1cddadc2095a98c954a7549da0d01c417b833fd'
# webhook secret provided by Thanx
secret = 'f19ce124-48e4-45fb-b39b-4f9e3e1d4b1b'
# webhook request payload body
payload = json.dumps({
  "purchase": {
    "id": "bd4163e4ea5e168431fdc4b8d1ea061f",
    "amount": "7.76",
    "purchased_at": "2024-03-26T21:38:36.000Z",
    "event": "create",
    "user": {
      "id": "675c038cfc5f43591718936c18080855",
      "email": "example@thanx.com",
      "first_name": "Thanx",
      "last_name": "Example"
    },
    "merchant": {
      "id": "a91f75a0dc2cc54a62ad8a1a05b9fa6b",
      "name": "Thanx Restaurant"
    },
    "location": {
    },
    "order": {
    },
    "products": [
      "Latte"
    ]
  }
}, separators=(',', ':'))

# the following will return True
signature == hmac.new(
    key=secret.encode('utf-8'),
    msg=payload.encode('utf-8'),
    digestmod=hashlib.sha256
).hexdigest()