Security2026-03-246 min

Every webhook we fire is signed, so the receiver can prove it's us

A webhook is an open door unless the receiver can verify who knocked. Our event dispatch signs every payload with HMAC and refuses to call private networks. Here's the threat model.

A webhook URL is a public mailbox. Anyone who learns the address can post a forged event through it — a fake lead, fake budget, fake urgency — and a naive receiver will trust all of it. An unauthenticated webhook isn't delivery; it's a suggestion.

Our event dispatch signs every payload. Each outbound delivery is hashed with HMAC-SHA256 using a per-endpoint secret, and the result rides along in an `X-Webhook-Signature` header. The receiver recomputes the hash with the shared secret and rejects anything that doesn't match. That single check turns 'a request claiming to be a lead' into 'a request provably from us.' It also makes payloads tamper-evident in transit: change one character of the body and the signature breaks.

Authentication is only half the threat model. A dispatcher that will POST anywhere is a server-side request forgery tool waiting to be pointed inward. So before we send, we validate the destination and refuse private and loopback ranges — 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local, and friends. Combined with a hard request timeout, a misconfigured or malicious endpoint can't turn our dispatcher into a probe against an internal network or a hung connection.

What we refuse to do: send unsigned payloads, retry blindly into a black hole, or let an endpoint definition smuggle in an internal address. Signing without SSRF guards is a locked front door next to an open window; we close both.

The principle: a lead handoff should be verifiable at the receiving end and unable to attack anything on the way out. If your CRM can't tell our leads from a stranger's, the integration is decorative. Cryptographic proof plus a strict egress policy is what makes machine-to-machine delivery something you can actually rely on at 2 a.m.

Let's talk