When webhook signature verification fails, check the bytes before you blame the secret. The sender signs the exact request it sent, and between its servers and your handler a framework can re-serialise the JSON, a proxy can rewrite the URL, or your code can read a timestamp in the wrong unit. Paste what your endpoint received, and the tool recomputes the signature, shows the string it signed and says whether the two agree.
It runs on your browser's own Web Crypto sign method. Nothing you paste is sent or saved, but a test secret is still the better habit.
Why the signature does not match
The body changed. Many frameworks parse JSON before your handler sees it, and re-serialising it changes the hash. Read the raw bytes first.
The URL is not the one the sender called. Behind a load balancer your app may see http or an internal host. HubSpot signs the public URL.
Encoded characters. HubSpot v3 decodes a set of URL-encoded characters before signing.
The timestamp unit. HubSpot sends milliseconds, while most apps that sign a timestamp send seconds.
The wrong scheme or encoding. v3 is an HMAC in base64, while v1 and v2 are plain hashes in hex.
If all five check out, look at the secret last. HubSpot signs with the app's client secret, and an access token gives a clean, confident mismatch.
Verify in your own receiver
Compare signatures in constant time, with hmac.compare_digest in Python or crypto.timingSafeEqual in Node. Check the timestamp after the signature, against the window the sender documents. Accept more than one secret while you rotate, and deduplicate on the event's ID, since a retry is the same event.
Each scheme the tool checks cites the app maker's own docs. HubSpot's rest on HubSpot's guide to validating requests.
What to do next
If the signature matches here but fails in your receiver, your code's copy of the request is the problem: build the same signed string the tool shows.
For HubSpot, you can skip the receiver. In Oneprofile, turn on live changes for a table imported from HubSpot, and each delivery's signature is checked before it changes anything.
