Skip to main content
Your endpoint’s URL is public, so anyone could post to it. Every request on the Webhook channel is signed with your endpoint’s signing secret. Check the signature before you act on an event, and reject the request when it does not match. Slack and Discord endpoints are not signed: Runcrate posts to Slack and Discord, never to your own server, and their webhook URL is itself the secret. See Slack and Discord.

The header

  • t is the Unix time, in seconds, when the request was signed. Every attempt is signed afresh, so a retry carries a new t.
  • v1 is the hex HMAC-SHA256 of t, 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 v1 values: t=...,v1=<signed with the new secret>,v1=<signed with the old one>. Accept the request if any v1 matches.
To verify:
  1. Read the raw body bytes. Parsing and re-serializing the JSON changes the bytes and breaks the signature.
  2. Compute HMAC-SHA256 over `${t}.${rawBody}` with your secret.
  3. Compare it with each v1 in constant time. Accept if one matches.
  4. Reject a t more than 5 minutes from your clock, so a captured request cannot be replayed later. Keep your server’s clock in sync (NTP).

Code

Frameworks that parse JSON before your handler runs (express.json(), many serverless templates) hand you an object, not the bytes. Re-serializing it rarely gives back the same bytes. Read the raw body for this route, as the examples do.

Compute a signature by hand

To check your verifier, or to post a signed event to your own server, compute a signature with openssl 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.
If your server reads the secret only when it starts, restart or reload it after you update the secret.

Best practices

Answer fast, dedupe on id, keep the secret in a secret store.

Testing locally

Run one of these verifiers on your machine and send it real events.