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:

HeaderValue
webhook-idThe event's id. The same on every retry, so use it to dedupe.
webhook-timestampWhen this attempt was sent, in Unix seconds
webhook-signaturev1, 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.

Node.js
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)
Python
from standardwebhooks import Webhook

wh = Webhook(os.environ["GOOMZO_WEBHOOK_SECRET"])
event = wh.verify(raw_body, request.headers)

To check it yourself:

  1. Base64-decode the secret after whsec_ to get the key.
  2. Compute HMAC-SHA256 of webhook-id, a dot, webhook-timestamp, a dot, and the body exactly as it arrived.
  3. Base64-encode it and compare it, in constant time, with each v1, value in webhook-signature.
  4. Reject it if webhook-timestamp is 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