Help Center Security Webhook Security

Webhook Security

Why it matters

Your webhook address is a public URL. Anyone who finds it can send it a fake payment notification.

Checking the signature is how you tell a real call from a fake one. Do not skip it, and never treat an unverified call as proof of payment.

What Brykto sends

Every webhook carries two headers.

Header What it holds
X-Brykto-Signature sha256= followed by the signature
X-Brykto-Timestamp When the call was made

The signature is an HMAC-SHA256 of the timestamp, a full stop, and the raw request body, signed with your webhook secret.

The signed string is {timestamp}.{body}, not the body on its own.

Checking it

import hmac
import hashlib

def verify(body: bytes, signature_header: str, timestamp_header: str, secret: str) -> bool:
    signed   = timestamp_header.encode("utf-8") + b"." + body
    expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature_header)

Two things are easy to get wrong here.

  • Use the raw request body exactly as it arrived. If your framework parses the JSON and you rebuild it before checking, the signature will not match.
  • The header value includes the sha256= prefix. Compare against the whole value, or strip the prefix from both sides.

Your webhook secret

Find it under Developer Tools. Rotate it there if it may have been exposed, then update your server.

Ignoring repeats

Brykto retries a webhook up to three times, so the same event can reach you more than once.

Each order.paid event carries the transaction reference as tx_hash. Record the ones you have handled and ignore any that arrive twice, so a retry never ships the same order to your customer again.