Webhook Security

Each webhook has two secret families: one for publishing into the public ingest endpoint and one for verifying outbound deliveries. Hooksbase also keeps a secret-version ledger so you can rotate safely without breaking producers or consumers instantly.

Secret types

  • Name
    Ingest secret
    Type
    whsec_...
    Description

    Used only for calls into /v1/ingest/{publicId}.

  • Name
    Signing secret
    Type
    sws_...
    Description

    Used only to verify the signature Hooksbase attaches to outbound delivery requests.

  • Name
    Secret versions
    Description

    Hooksbase stores lifecycle metadata for both secret families and exposes the non-secret version history through /secret-versions.

Auth model

Project API keys manage webhook security surfaces. The dashboard mirrors the same controls with extra product affordances such as reveal and audit-log views.

Related Public API routes:

  • POST /v1/webhooks/{id}/rotate-ingest-secret
  • POST /v1/webhooks/{id}/rotate-signing-secret
  • GET /v1/webhooks/{id}/secret-versions
  • POST /v1/webhooks/{id}/test-delivery

Rotation behavior

Rotation is overlap-aware:

  • the new version becomes current immediately
  • the previous current version becomes overlapping
  • older versions become retired

During overlap:

  • both ingest secrets are accepted for public ingest
  • outbound signatures can include both the current and overlapping signing secret

Only create and rotate responses return raw secret material. List and get routes never do.

Verifying outbound signatures

Hooksbase signs the exact raw body bytes it sends to your destination. The signing input is:

{deliveryId}.{unixTimestampSeconds}.<raw body bytes>

The HMAC is SHA-256, base64-encoded, and emitted as v1,<base64>. Headers on the outbound request:

  • webhook-id — the delivery ID (also used as the signing input)
  • webhook-timestamp — Unix timestamp in seconds
  • webhook-signature — one or more v1,... values joined by spaces during a signing-secret overlap window

Use the SDK helper when possible:

Verify an outbound delivery

import { verifyHooksbaseWebhook } from '@hooksbase/sdk'

verifyHooksbaseWebhook({
  headers: request.headers,
  rawBody: await request.arrayBuffer(),
  signingSecret: [
    process.env.HOOKSBASE_CURRENT_SIGNING_SECRET!,
    process.env.HOOKSBASE_PREVIOUS_SIGNING_SECRET!,
  ],
})

The helper enforces a 5-minute timestamp tolerance by default (override with toleranceSeconds) and checks every v1,... candidate signature against every provided secret, so rotation overlap is handled automatically. It throws if no signature matches or the timestamp is too far out of tolerance.

Dashboard-only views

Some security views exist only in the dashboard and are not part of the Public API:

  • reveal the current signing secret for a webhook
  • inspect the secret audit log
  • rotate secrets from the UI
  • run a synchronous test delivery without creating delivery history

Test delivery is also exposed on the Public API at POST /v1/webhooks/{id}/test-delivery. It is intentionally narrower than public ingest:

  • it evaluates saved route rules against the probe payload, empty request headers, and derived provider metadata, falling back to the default destination when no rule matches
  • it applies the saved transform, selected destination config, timeout, and usable signing secrets
  • it does not create deliveries, emit analytics events, or raise operator incidents
  • archived webhooks return 409, but paused webhooks are still testable
  • repeated probes are rate-limited so the endpoint is not a general-purpose synchronous traffic path

Common mistakes

  • Verifying against parsed JSON instead of raw bytes.
  • Forgetting that both signatures can be valid during an overlap window.
  • Expecting list or get routes to ever return the raw secret again after creation.
  • Treating test delivery as a full public-ingest routing test. Use public ingest when you need producer auth, provider verification, real request headers, and route evaluation end to end.

Was this page helpful?