> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thanx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

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
<a href="mailto:developer.support@thanx.com">[developer.support@thanx.com](mailto:developer.support@thanx.com)</a>
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](#requirements)), so delivery of any single event
is not guaranteed. Duplicates are the common case, and this is most pronounced
for [purchase](/webhooks/purchases) 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](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.

<Note>
  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`).
</Note>

## 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:

<CodeGroup>
  ```ruby Ruby theme={null}
  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
  ```

  ```python Python theme={null}
  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()
  ```
</CodeGroup>
