Skip to main content
Anyone on the internet who knows your webhook URL could POST you a fake event pretending to be Boble. To protect against this, every Boble delivery is signed with a secret only you and Boble know. Before you act on any event — granting access, sending a welcome email, removing a Discord role — make sure the signature is valid.

Your signing secret

When you turn on webhooks for the first time, Boble generates a signing secret for your tenant. The same secret is used to sign deliveries for every promotion you connect a webhook to. You retrieve it from your dashboard, and you can rotate it at any time.
Treat the signing secret like a password. Never expose it in client-side code, never commit it to a public repository, and rotate it immediately if it leaks.

The Boble-Signature header

Every delivery carries a header that looks like this:
Boble-Signature: t=1717497600,v1=5257a869e7ec...
It has two parts, comma-separated:
  • t — the Unix timestamp in seconds when Boble emitted the event. Same value as the created field in the body.
  • v1 — the signature: an HMAC-SHA256 in lowercase hex over the string <t>.<rawBody>, using your signing secret.

How to verify

The verification is a three-step recipe:
1

Extract t and v1 from the header

Split the value on ,, then split each part on =.
2

Recompute the signature on your side

Compute HMAC-SHA256(secret, "<t>.<rawBody>") and hex-encode it.
3

Compare in constant time

If your recomputed signature matches v1, the event is genuine. If not, drop it and reply 401.
A few subtleties worth knowing:
  • The signature is calculated over the raw bytes of the body, exactly as Boble sent them. If your framework parses the JSON and re-serializes it before you check the signature, you’ll get a different string and verification will fail. Capture the raw body before any JSON middleware runs.
  • The comparison must be constant-time. Don’t use a regular string equality — most languages ship a dedicated function for this (crypto.timingSafeEqual, hmac.compare_digest, hash_equals, etc.).
  • It’s a good idea to also reject events whose t is more than a few minutes old (5 minutes is a reasonable default). This blocks replay attacks where someone captures a real signed body and replays it later.

Rotating the signing secret

You can regenerate the signing secret from your dashboard at any time. The moment you do:
  • The old secret stops working immediately for new events.
  • Any event Boble was about to send is signed with the new secret.
The safe rollout order is:
  1. Deploy the new secret to your receiver first.
  2. Then trigger the rotation from your dashboard.
If you want a smoother transition, keep both secrets active on your side for a few minutes and accept whichever one matches.