The header
tis the Unix time, in seconds, when the request was signed. Every attempt is signed afresh, so a retry carries a newt.v1is the hex HMAC-SHA256 oft, a full stop and the raw request body, keyed with the whole secret string,whsec_included.- For 24 hours after you rotate the secret, the header carries two
v1values:t=...,v1=<signed with the new secret>,v1=<signed with the old one>. Accept the request if anyv1matches.
- Read the raw body bytes. Parsing and re-serializing the JSON changes the bytes and breaks the signature.
- Compute HMAC-SHA256 over
`${t}.${rawBody}`with your secret. - Compare it with each
v1in constant time. Accept if one matches. - Reject a
tmore than 5 minutes from your clock, so a captured request cannot be replayed later. Keep your server’s clock in sync (NTP).
Code
Compute a signature by hand
To check your verifier, or to post a signed event to your own server, compute a signature withopenssl and send it with curl. Save an event body to event.json first (copy one from events).
--data-binary sends the file unchanged; -d would strip its newlines and the signature would no longer match.
Rotating the secret
Rotate the secret when it may have leaked, when someone who had it leaves, or on a schedule.1
Rotate it in Runcrate
In Settings > Webhooks, open the endpoint’s menu and select Rotate secret, then confirm. Only owners and admins can.
2
Copy the new secret
It is shown once, under Copy the new signing secret now. Store it where your server reads its secrets.
3
Deploy it within 24 hours
For the next 24 hours every request is signed with both secrets, so your server keeps working while it still holds the old one and as soon as it holds the new one. After 24 hours the old secret stops being used.