Skip to the page

Webhooks

Rotate secrets

Give a webhook a new signing secret without missing a delivery. For 24 hours BlueBot signs with both the old and the new one, so you can deploy the change at your own pace.

Each webhook has its own signing secret, whsec_…, shown once when you make it. Rotate it when it may have leaked (pasted into a chat, committed to a repository, known to somebody who left), when you've lost it, or on a schedule your security policy sets.

What rotating does

  • BlueBot makes a new secret and shows it only once. The API returns it in its answer, and the dashboard shows it on screen.
  • For the next 24 hours, every delivery is signed with both secrets. webhook-signature carries two values, the new secret's first: v1,<new> v1,<old>.
  • The webhook's previous_secret_expires_at says when the old secret stops signing. After that, only the new one does.
  • Nothing else changes: the address, the events and the deliveries waiting are the same.

Because each delivery carries a signature from each secret during those 24 hours, a handler that knows only the old secret and a handler that knows only the new one both accept it. That's what makes the switch safe: you never need both secrets in your code.

Rotate without missing a delivery

Rotate

In Developers → Webhooks, open the webhook and press Rotate secret, or call the API:

POST/v1/webhooks/wh_1Hq2LxW9cVbN3mKpRtYs0a/rotate-secretRotate the signing secret →
curl -X POST "https://usebluebot.com/api/v1/webhooks/wh_1Hq2LxW9cVbN3mKpRtYs0a/rotate-secret" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY" \
  -H "Idempotency-Key: rotate-wh_1Hq2LxW9cVbN3mKpRtYs0a-2026-11"
Response200
{
  "id": "wh_1Hq2LxW9cVbN3mKpRtYs0a",
  "object": "webhook",
  "url": "https://bookings.example.com/bluebot/webhooks",
  "status": "active",
  "previous_secret_expires_at": "2026-11-14T19:41:00.000Z",
  "secret": "whsec_Q2nVb8mLx0RkT4cW9yHj3PdZs6EuAa1F"
}

Copy secret. It isn't shown again.

Put the new secret in your environment

Set BLUEBOT_WEBHOOK_SECRET to the new value wherever your handler runs: your host's environment settings, your secrets manager, every region and every worker that verifies.

Deploy

Deploy as you normally would. Servers still on the old secret and servers already on the new one both accept every delivery while the window is open, so a rolling deploy is fine.

Check it

Press Send a test event on the webhook, or call POST /v1/webhooks/{id}/test. Your handler answers 2xx with the new secret in place.

You gotIt meansDo next
last_status: 204 (or any 2xx) on the test deliveryYour handler verifies with a secret BlueBot signs with.Make sure every server has the new secret before previous_secret_expires_at.
last_status: 400Your handler rejected the signature. The new value didn't reach it, or it was pasted with a space or cut short.Copy the secret again from where you saved it. If it's lost, rotate again, but read the warning below first.

Let the old secret go

When previous_secret_expires_at passes, BlueBot signs with the new secret only. Delete the old value from anywhere you kept it.

If the secret leaked

Anybody with the old secret can sign posts your handler accepts for as long as your handler trusts that secret. BlueBot's 24-hour window only decides which secrets BlueBot signs with; your handler decides which it accepts. So after a leak, put the new secret in place as soon as you can. Once your handler verifies with the new secret only, a post signed with the leaked one fails your check, even though BlueBot still adds its own old-secret signature for the rest of the day.

Then look for posts that arrived during the leak and don't match an event BlueBot sent: every genuine event is in the event feed under its id.

Lost the secret?

BlueBot shows a secret once and can't show it again. Rotate to get a new one. If nothing verifies with the old secret any more, the 24-hour window doesn't matter to you, and you can deploy the new secret whenever it's ready; deliveries that fail meanwhile are retried for 72 hours.

Next