Skip to content
catchook

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.