Webhooks
Payment webhook verification
Webhooks are signed, so a handler should trust the signature rather than the source address. Verify before you parse.
POST /your/webhook/endpoint
x-kollect-signature: <signature>
# Verify before trusting the body:
# 1. read the raw request body, unparsed
# 2. compute HMAC-SHA256 over it with your webhook secret
# 3. compare in constant time against x-kollect-signature
# 4. reject on mismatch, and only then parse the JSON Why are payment webhooks signed at all?
Because a webhook endpoint is a public URL, and anything can post to it. A signature is what distinguishes a real settlement notification from a forged one. Without verifying it, a handler is trusting whoever found the address.
Why compare the signature in constant time?
A normal string comparison returns as soon as it finds a difference, and the time it takes leaks how much of the signature was correct. Enough attempts and that leak reconstructs a valid one. A constant-time compare removes the signal.
What should my handler do if verification fails?
Reject the request and stop, before parsing the body. Do not log the payload as trusted, do not act on it, and do not return a success status. An unverified webhook is untrusted input that happens to look like your own data.
The interface
- Signature header
- Signed via x-kollect-signature; kollect-server interface only.
- Request authentication
- API key + secret, HMAC-SHA256 signature, mandatory idempotency key.
- Payment creation
- POST /sdk/server/create-payment → hosted paymentUrl
- Sandbox
- Sepolia (sandbox only)
Exact payload shapes and event names live in the developer docs, which is the authority. This page covers verification, which is the part that is easy to get subtly wrong.
Webhook questions
Every answer here is mirrored verbatim in this page's FAQ structured data, so nothing is hidden behind a click.
The wider API surface is on the developers page.
How do I verify a Kollect webhook signature?
Every webhook is signed and carries the signature in the x-kollect-signature header. Compute an HMAC-SHA256 over the raw, unparsed request body using your webhook secret, compare it against that header in constant time, and reject the request on mismatch. Parse the JSON only after the signature checks out.
Why verify against the raw body rather than the parsed JSON?
Because re-serialising parsed JSON does not reliably reproduce the original bytes. Key order, whitespace and number formatting can all change, and any of those breaks the hash. Capture the raw body before your framework parses it.
What stops a webhook being replayed?
Idempotency keys are mandatory on the API, so the same logical operation applied twice is not counted twice. Treat your webhook handler the same way: key on the event identifier and make the handler safe to run more than once, because at-least-once delivery means duplicates are normal rather than exceptional.
Can I test webhooks before taking live payments?
Yes. The sandbox runs on Sepolia (sandbox only) and issues real invoices with working payment links using test tokens, so your handler can be exercised end to end before a single live payment.