Signatures
Check that each event is from Goomzo before you act on it.
Anyone can post to your URL, so check that each event is from Goomzo before you act on it. Every delivery is signed with your endpoint's secret.
Headers
Webhooks follow Standard Webhooks, an open spec with libraries for most languages. These headers come with each delivery:
| Header | Value |
|---|---|
webhook-id | The event's id. The same on every retry, so use it to dedupe. |
webhook-timestamp | When this attempt was sent, in Unix seconds |
webhook-signature | v1, and the signature, base64 |
Check the signature
The easiest way is a
Standard Webhooks library, with the endpoint's secret. It starts with whsec_; find it in the endpoint under
Signing secret.
import { Webhook } from 'standardwebhooks'
const wh = new Webhook(process.env.GOOMZO_WEBHOOK_SECRET)
// Throws if the signature is wrong or the timestamp is more than 5 minutes off.
const event = wh.verify(rawBody, req.headers)from standardwebhooks import Webhook
wh = Webhook(os.environ["GOOMZO_WEBHOOK_SECRET"])
event = wh.verify(raw_body, request.headers)To check it yourself:
- Base64-decode the secret after
whsec_to get the key. - Compute HMAC-SHA256 of
webhook-id, a dot,webhook-timestamp, a dot, and the body exactly as it arrived. - Base64-encode it and compare it, in constant time, with each
v1,value inwebhook-signature. - Reject it if
webhook-timestampis more than 5 minutes from now.
Rotate the secret
Open the endpoint and select Rotate secret, or call POST /v1/webhook-endpoints/{id}/rotate-secret. You
get a new secret at once, and for the next 24 hours each delivery is signed with both: webhook-signature holds
two v1, values. Standard Webhooks libraries accept either, so update your receiver any time in that window.
Send {"keepPreviousForHours": 0} to stop the old secret at once, for example if it leaked.
Next steps
Last updated