Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kash.bot/llms.txt

Use this file to discover all available pages before exploring further.

The webhook_secret is what your endpoint uses to verify incoming webhook signatures. Rotate it on the same triggers as any sensitive credential: a team change, a leaked log line, or just routine hygiene. The rotation flow has a built-in 7-day overlap window so deliveries in flight are never dropped. Both the previous and new secret are valid during the window — your verifier just needs to accept either.

Rotating

curl -X POST https://api.kash.bot/v1/auth/api-keys/me/webhook-secret/rotate \
  -H "X-API-Key: $KASH_API_KEY"
Response (200 OK):
{
  "webhookSecret": {
    "secret":                "whsec_<new-base64url-secret>",
    "rotatedAt":             "2026-05-02T12:00:00.000Z",
    "previousRetainedUntil": "2026-05-09T12:00:00.000Z"
  },
  "data": { /* same as webhookSecret */ },
  "_meta": {
    "requestId": "...",
    "timestamp": "...",
    "message":   "Webhook secret rotated. Previous secret remains valid for signature verification until 2026-05-09T12:00:00.000Z."
  }
}
The new secret plaintext is shown once — capture it and roll it out to your endpoint. Required scope: webhooks:manage. The key must already have a webhook URL + secret configured at issuance time — rotation only swaps the secret on a configured webhook, it does not provision one.

Rotation cooldown (60 seconds)

Calling rotate again within 60 seconds of a successful rotation returns 429 WEBHOOK_SECRET_ROTATION_COOLDOWN with a Retry-After header counting down to the cooldown’s expiry.
HTTP/1.1 429 Too Many Requests
Retry-After: 38
Content-Type: application/problem+json

{
  "code":   "WEBHOOK_SECRET_ROTATION_COOLDOWN",
  "title":  "Rotation cooldown active",
  "status": 429,
  "detail": "A rotation completed within the last 60s. If you did not capture the new secret from that response, contact support — re-rotating now would overwrite the rollback slot with a secret you never received. Retry after the cooldown expires only if you actually want a new secret."
}
This guard exists because rotation is destructive. The cap protects you from a retry-storm scenario: your HTTP client times out on the rotate POST, retries automatically, and the second rotation overwrites the previous-secret slot with a brand-new secret you never received in the lost first response. Rolling back from there would restore a secret no verifier was ever configured for. If you genuinely lost the new secret from the previous response, do NOT retry yourself. Contact support — operations can roll back via the operator path (described below) which preserves the rollback chain. Hammering rotate just makes the situation harder to recover from.

What happens during the 7-day window

While now < previousRetainedUntil:
  • The webhook delivery worker dual-signs every delivery. The X-Kash-Signature header carries TWO v1= entries — one with the new secret, one with the old.
  • Your verifier should accept signatures matching either secret. Both reference implementations and the SDK already do this.
Sample signature header during the window:
X-Kash-Signature: t=1730000000000,v1=<new-hmac>,v1=<old-hmac>
After previousRetainedUntil, dual-signing stops and only the new secret is used.
  1. Rotate the secret. POST /v1/auth/api-keys/me/webhook-secret/rotate. Capture the new plaintext.
  2. Update your endpoint to know about both secrets. Most setups use a KASH_WEBHOOK_SECRET env var — add KASH_WEBHOOK_SECRET_PREVIOUS and have your verifier try the new one first, then the previous. Or use a verifier (like the SDK’s constructEvent) that already accepts multiple v1= entries.
  3. Deploy. Your endpoint now accepts signatures under either secret.
  4. Wait for in-flight deliveries to drain. Conservatively, a few minutes. The 7-day window gives you generous headroom.
  5. (After 7 days, optional) Remove KASH_WEBHOOK_SECRET_PREVIOUS from your config. After previousRetainedUntil, the old secret no longer signs anything; carrying it in your verifier just adds dead code.
If your verifier is the SDK or one of the reference implementations, step 2 collapses to “set the new secret as the only secret” — the dual-v1= parsing is already in there.

What can go wrong

”I rotated and immediately replaced the secret in my verifier”

If your verifier only knows the new secret, deliveries signed during the rotation overlap (with both new and old signatures) still verify because at least one v1= entry matches. You’re fine — but if you’d swapped to the OLD secret only, you’d lose deliveries. The safe rule: during overlap, accept either secret. Never accept only the old one after rotation.

”I lost the new secret”

Two cases:
  • You captured the previous response and just didn’t store the new secret. Rotate again. Each rotate creates a fresh secret and re-starts the 7-day window. Your previously-captured new-secret becomes the rotation’s “previous” — both still valid during the new window. Wait at least 60s between rotations to avoid the cooldown gate.
  • The previous rotation response was lost (network timeout, dropped response). Do NOT just retry rotate yourself — that would silently overwrite the rollback slot with a secret no one ever held. Contact [email protected]; operations can either roll back to the prior known-good secret (preserving your verifier) or perform an operator-path rotation that captures the new secret onto a support channel rather than into the void.

”Rotation broke my verifier and I need to roll back”

Within 7 days of a rotation, operations can roll back to the prior secret via the kash-admin api-keys rollback-webhook-secret operator command. This atomically swaps webhook_secret_previous back into webhook_secret, so your endpoint immediately starts receiving deliveries signed with the secret your verifier knows. To trigger a rollback, email [email protected] with:
  • The API key id (UUID — visible in the webapp Settings → API Keys page)
  • A short description of what broke (so the operator captures it in the audit row)
  • Confirmation that the prior secret you remember is still configured on your endpoint
The operator confirms the now-active secret with you on the support channel. After 7 days the rollback path closes — beyond that, the only recovery is to rotate forward to a fresh secret and update your verifier.

”My endpoint started rejecting signatures after the window expired”

Check that you’re verifying with the current secret and that you didn’t accidentally remove the v1= parsing. After previousRetainedUntil, the old secret stops signing; if your verifier was relying on the old secret being valid, it’ll start failing. GET /v1/account/usage will surface the failure rate via webhooks.7d.failed going up.

Inspecting current state

There’s no API to read the secret back — once issued, the plaintext lives only in your config. To check rotation timestamps, the webapp’s Settings → API Keys → [key] → Webhook page shows rotatedAt and previousRetainedUntil.

URL changes

If you change webhook_url (different domain or path), the API does a one-time validation to confirm the new URL responds 2xx to a probe before queuing real deliveries. To prevent abuse, URL changes also have a 1-hour cooldown between consecutive changes. The API will warn you if the old URL has unprocessed deliveries pending; redirect them with manual redelivery once the new URL is live.

Going dark

If you want to temporarily pause webhook delivery without changing your URL or rotating the secret, the cleanest path is to revoke and re-issue the key (no deliveries are queued for a key with no webhook_url). For short pauses, just respond 429 with a long Retry-After from your endpoint — the breaker opens and we back off.

Next

Verifying Signatures

The verification recipe that handles the dual-v1= overlap window.

Webhooks Overview

Back to the webhooks overview.