Verification
Every webhook request carries a set of identifying headers. Check them before trusting a payload.Headers sent with every delivery
v1: shared-secret verification
The current version uses a shared-secret model: your receiving endpoint should compare theX-Webhook-Secret header against the secret shown on the endpoint’s detail page in the CRM, and
reject the request if it doesn’t match.
hash_equals in PHP, or an equivalent) rather than ==/=== where
your language’s standard library provides one, to avoid timing attacks.
Keep the secret private
Treat the signing secret like a password. Anyone who has it can send requests to your endpoint that your code will accept as genuine. If a secret leaks, delete the endpoint and recreate it — a new secret is generated automatically.Recommended future hardening: HMAC-SHA256
A shared secret sent on every request has two limitations: it travels over the wire on every call (so it must never be logged or sent over plain HTTP), and it doesn’t protect the payload itself — nothing stops a captured request from being replayed later. A future version of this module is expected to add HMAC-SHA256 request signing, the same approach used by Stripe and GitHub:- The endpoint’s secret is used as an HMAC key, never sent on the wire itself.
- The signature is computed over
timestamp + "." + raw_body, sent in a header such asX-Webhook-Signature: t=1730000000,v1=<hex-hmac>. - Your code recomputes the HMAC locally and compares it, and rejects requests whose timestamp is too old (protecting against replay).
