Concepts
Signature verification
The verification methods an endpoint can use, and exactly what each one checks.
Every request to an endpoint is verified before anything else happens. A
request that fails gets 401, is not recorded, and is never delivered.
The method is set per endpoint.
hmac_sha256 and hmac_sha1
The common case, and what Stripe, GitHub and Shopify all use in some form. Catchook handles two header shapes:
Timestamped CSV — Stripe’s format:
Stripe-Signature: t=1756454651,v1=5257a869e7ecebeda32affa62cdca3fa...
The signed string is ${t}.${body}. The timestamp is part of what is signed,
which is what makes replaying a captured request detectable.
Single value — GitHub’s format, and most others:
X-Hub-Signature-256: sha256=d57c68ca6f92289e6987922ff26938930f6e66a2...
A bare hex digest with no prefix works too. The signed string is the raw body.
Comparison is constant-time in both cases.
Provider quirks beyond these two shapes are not covered yet — Twilio’s base64 encoding being the notable one.
bearer_token
The request must carry Authorization: Bearer <token> matching the token
configured on the endpoint. Appropriate for internal services posting to you,
not for third-party providers.
basic_auth
HTTP Basic credentials, checked against the configured username and password. Some older enterprise systems only offer this.
jwt
The request carries a JWT, and Catchook validates its signature and standard claims. Use when the sender already issues signed tokens.
ip_allowlist
The request’s source address must be in a configured list. This is the weakest option here: it proves where a request came from, not that the payload is intact, and provider IP ranges change without notice. Prefer it as a second factor alongside an HMAC rather than on its own.
none
No verification. Every request is accepted.
There is one good reason to use this: a provider that offers no signing at all, on an endpoint whose id has not been shared. Anyone who learns the URL can post anything to it, and it will be delivered to your destinations as genuine. Treat it as temporary.
What verification does not cover
Verification proves the request was signed by someone holding the secret. It
says nothing about whether the event is one you have already handled —
providers legitimately re-send, and Catchook itself retries. Idempotency is
still your handler’s job; catchook-event-id is the key to use.