Skip to content

✏️ Creating Webhooks

Webhooks are managed via the following endpoint, using your partner credentials:

https://public.api.barestho.com/api/v1/partnership/webhooks/

Refer to the API reference for the full list of available operations (create, retrieve, delete).

Each request sent to your webhook is signed by Barestho using an
HMAC-SHA256 computed from your auth_token,
the request payload, and the current time as a timestamp.
The auth_token is defined by the partner when creating the webhook.

The following headers are included with each request:

HeaderContent
X-Webhook-SignatureHMAC-SHA256 signature
X-Webhook-TimestampRequest timestamp (Unix)

To validate that a request originates from Barestho, reconstruct the signature as follows:

  1. Retrieve the timestamp from X-Webhook-Timestamp
  2. Serialize the request body to JSON without spaces, with keys sorted alphabetically
  3. Build the message to sign: {timestamp}.{serialized_json}
  4. Compute an HMAC-SHA256 of this message using your auth_token as the key
  5. Prefix the result with sha256= and compare it to X-Webhook-Signature
hmac.py
import hmac
import hashlib
import json
import time
SIGNATURE_PREFIX = "sha256="
TIMESTAMP_TOLERANCE_SECONDS = 300
def sign_payload(secret: str, payload: dict, timestamp: int):
"""
Sign a webhook payload using HMAC-SHA256.
:param secret: The shared secret key
:param payload: The payload as a dict
:param timestamp: The Unix timestamp of the request
:return: The hex signature prefixed with 'sha256='
"""
dumped_payload = json.dumps(
payload,
separators=(',', ':'),
sort_keys=True,
)
message = f"{timestamp}.{dumped_payload}"
signature = hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return f"{SIGNATURE_PREFIX}{signature}"
def verify_signature(secret: str, payload: dict, signature_header, timestamp_header):
"""
Verify a webhook signature and reject stale requests.
:param secret: The shared secret key
:param payload: The raw payload as a dict
:param signature_header: The value of X-Webhook-Signature
:param timestamp_header: The value of X-Webhook-Timestamp
:raises ValueError: If the timestamp is stale or the signature is invalid
"""
request_time = int(timestamp_header)
if abs(time.time() - request_time) > TIMESTAMP_TOLERANCE_SECONDS:
raise ValueError("Request timestamp is too old")
expected = sign_payload(secret, payload, timestamp_header)
if not hmac.compare_digest(expected, signature_header):
raise ValueError("Invalid signature")