The signing scheme each source uses, why timing-safe comparison matters, and what happens when a signature doesn't match.
A webhook URL is, by itself, just a bearer secret — anyone who learns it (a leaked log line, a misconfigured proxy, a browser history sync) can POST anything they want to it. Without verification, a receiving endpoint has no way to distinguish a genuine event from a forged one: a fabricated refund, a fake "deployment succeeded" ping, an invented support ticket. Signature verification closes that gap by requiring every request to prove it was generated by someone who holds a secret only the real source and ZestyGlue know — the URL alone is no longer enough.
Most providers sign with HMAC — a cryptographic hash of the request body keyed with a shared secret, sent alongside the request in a header. ZestyGlue verifies whichever scheme the source actually uses, rather than forcing every source into one shape:
| Scheme | Sources |
|---|---|
| HMAC-SHA256 (body) | Stripe, Shopify, WooCommerce, Lemon Squeezy, Paddle, Ghost, Tally, Typeform, Calendly, Cal.com, DocuSign, Twitch, GitHub, Sentry |
| HMAC-SHA256 (notification URL + body) | Square — the one source here that signs over more than just the request body |
| HMAC-SHA1 | Vercel — verified against Vercel's own docs; it's the one source that isn't SHA256 |
| HMAC-MD5 | Patreon — also verified against Patreon's docs; unusual, but genuinely what they use |
| Static token | GitLab, Ko-fi, Supabase — a plain shared secret compared directly, rather than a computed signature |
| None available | Any Webhook, PayPal, Gumroad, Jotform, UptimeRobot, Docker Hub — these rely on the webhook URL itself being unguessable |
Where a source offers no signature scheme at all, the only real mitigation is that the URL is a long, random, effectively unguessable path — the same reason you wouldn't publish a webhook URL publicly even for a source that does support signing.
Computing the right signature is only half the job — how it's compared against the one
the source sent matters too. A naive comparison (Python's == on two
strings) exits as soon as it hits the first mismatched character, which means a mismatch on
character 1 returns fractionally faster than a mismatch on character 30. Given enough requests
and precise enough timing measurement, that difference is a real, if slow, side channel an
attacker could use to guess a signature byte by byte. Every signature check in ZestyGlue uses
hmac.compare_digest, which takes the same amount of time regardless of
where — or whether — the strings differ, closing that channel off entirely.
The event is logged as rejected in your connector's event log and
dropped — nothing is forwarded to your destination. Critically, the source still receives a
normal 200 OK response. Returning an error here would let an attacker
probing a webhook URL learn, from the response alone, that signature verification exists and is
failing — information they could use to iterate toward a valid signature or simply confirm the
endpoint is worth attacking further. A flat 200 gives no such feedback, at the cost of the source
never being told a specific delivery was rejected — which is also why every connector's own event
log, not the source's dashboard, is the right place to check when something isn't arriving. See
the troubleshooting section of the docs for what each
event-log status actually means.
Signing secrets, API tokens, and destination webhook URLs are encrypted at rest with Fernet symmetric encryption before they ever touch disk, decrypted only in memory for the moment a connector fires, and never logged in plaintext anywhere. Once you save a connector's credentials, the dashboard never displays the real value again — only a masked placeholder — so even a compromised session on your own account can't be used to exfiltrate a secret you already entered.
Questions about a specific source's scheme, or think we've got a detail wrong? Get in touch — this page describes the actual verification code running in production, not a general description of how HMAC works, so we'd genuinely want to know if it drifts out of sync.