Gareth Wilson Gareth Wilson

Guide to Bump.sh Webhooks: Features and Best Practices

Published


Bump.sh hosts API reference documentation generated from OpenAPI and AsyncAPI definitions, and tracks how those definitions change from one deployment to the next. Its webhook does one job: when a deployment changes the structure of an API, Bump.sh posts the change to your URL. Teams use it to tell API consumers about a new version, to flag breaking changes in chat, or to start regenerating SDKs in CI.

Because there is only one kind of notification, this is a short guide. It covers what the body contains, where to find the secret token, how to verify X-Bump-Signature-256, and the inconsistency in Bump.sh's documentation of that header.

What are Bump.sh webhooks?

Webhooks are set up per documentation in Bump.sh. Each documentation can send to several URLs, and each webhook has its own secret token. Bump.sh sends a JSON POST to every URL whenever it notices that a new deployment has changed the API's structure.

There is no event name in the request. Every delivery is a structure change, so your receiver does not need to route by type.

Bump.sh webhook features

FeatureDetails
ConfigurationDocumentation's Integrations tab in the dashboard
TriggerA deployment that changes the API's structure
Signaturehex HMAC-SHA256
Signature headerx-bump-signature-256
Header formatsha256=<hex> per the help page
Signed contentThe raw request body
SecretA secret token per webhook, on its confirmation screen
BodyJSON with api and diff
Delivery historyRecent deliveries, the last 10 notifications
Retries and timeoutNot documented

Events and payloads

NotificationTriggered WhenCommon Use Cases
API documentation structure changeA deployment changes the structure of a documentationPost the change to chat, open a ticket, trigger SDK or client regeneration, notify API consumers

Bump.sh's API reference describes the body as a schema rather than a complete example, so the samples library has no sample for it. The body has two objects:

  • api describes the documentation: id, name, description, slug, humanUrl, tags, version and properties. url still appears but is deprecated. Each entry in properties has a type, such as OpenAPI or AsyncAPI, which describes the definition format, not the event.
  • diff describes the change: id, title, public_url, breaking, details, previous_version_url and current_version_url.

diff.breaking is the field most receivers act on, and diff.public_url links to Bump.sh's human-readable changelog entry. After verifying the signature, still check the type of each field before reading it, since you have no full example to test against.

Setting up Bump.sh webhooks

  1. Open the documentation in the Bump.sh dashboard and select the Integrations tab.
  2. Add a webhook with your payload URL, for example https://your-app.example.com/webhooks/bump-sh. Repeat for any other URLs.
  3. Bump.sh shows a confirmation screen where you can change the payload URL and copy the secret token. Store the token on your server, for example as BUMP_WEBHOOK_SECRET. Use it as plain text, without decoding it.
  4. In the webhook's Recent deliveries section, use Test Webhook to send a test notification.

To remove a webhook later, select it in the Integrations section and confirm in the Danger zone.

If you route through Hookdeck, create a source with the Bump.sh source type, paste the secret token into its verification settings, and add the Hookdeck source URL as the payload URL. During development, npx hookdeck-cli listen 3000 bump-sh --path /webhooks/bump-sh forwards notifications to a local server with the Hookdeck CLI.

Securing Bump.sh webhooks

Bump.sh's help page verifies the request by computing "sha256=" + hexdigest(HMAC-SHA256(secret, body)) and comparing it with the X-Bump-Signature-256 header. The verifier below strips the sha256= prefix if present, checks the remainder is 64 hex characters, and compares it with the HMAC of the raw body in constant time. It also accepts a bare hex value, which is what Bump.sh's API reference shows. The full steps are in the Bump.sh webhooks help page and the verification reference in the Bump.sh skill.

const crypto = require('node:crypto');

function verifySignature(rawBody, headers, secret) {
  if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret) return false;
  const signature = headers['x-bump-signature-256'];
  if (typeof signature !== 'string') return false;
  const key = Buffer.from(secret, 'utf8');
  const message = rawBody;
  const candidates = [signature.startsWith('sha256=') ? signature.slice(7) : signature];
  const expected = crypto.createHmac('sha256', key).update(message).digest();
  return candidates.some((candidate) => {
    if (!/^[0-9a-fA-F]{64}$/.test(candidate)) return false;
    const received = Buffer.from(candidate, 'hex');
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

With the signature checked, the handler reads diff and reacts to breaking changes:

const express = require('express');

const app = express();

app.post('/webhooks/bump-sh', express.raw({ type: '*/*' }), async (req, res) => {
  const secret = process.env.BUMP_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500);
  if (!verifySignature(req.body, req.headers, secret)) return res.sendStatus(401);

  let parsed;
  try {
    parsed = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return res.sendStatus(400);
  const { api, diff } = parsed;
  try {
    if (!diff || typeof diff.id !== 'string') return res.sendStatus(200);

    if (diff.breaking === true) {
      await alertApiConsumers(api && api.name, diff.title, diff.public_url);
    }
    await recordApiChange(diff.id, api && api.slug, diff.current_version_url);
  } catch {
    return res.sendStatus(503);
  }

  res.sendStatus(200);
});

alertApiConsumers and recordApiChange stand in for your own notification and storage code.

Bump.sh signs no timestamp, so the signature never expires. Hookdeck's Bump.sh source checks the same HMAC on x-bump-signature-256, answers Bump.sh with 200, and marks a request that fails the check as failed verification instead of returning 401.

Make Bump.sh webhooks production-ready. Hookdeck Event Gateway verifies X-Bump-Signature-256, keeps a history of every structure change well beyond Bump.sh's last 10, and delivers each one to as many destinations as you need.

Bump.sh webhook limitations and pain points

Two spellings of the signature header

The Problem: A verifier built from one page of Bump.sh's documentation rejects real requests.

Why It Happens: The help page's Ruby example reads the header Rack exposes as HTTP_X_BUMP_SIGNATURE_256, which is X-Bump-Signature-256 on the wire, and expects a sha256= prefix. The API reference names the header X_BUMP_SIGNATURE_256 and shows a bare hex example. Many proxies drop headers with underscores in their names.

Workarounds:

  • Read X-Bump-Signature-256, and accept the digest with or without the sha256= prefix, as the sample above does.
  • Send a test from Recent deliveries and look at the actual header before you go live.

How Hookdeck Can Help: Hookdeck records the exact headers of every request, so you can see which form Bump.sh sent without adding logging to your service.

No documented retries

The Problem: If your endpoint is down during a deployment, you may never hear about that change.

Why It Happens: Bump.sh documents no retry policy, timeout or expected response, and the dashboard keeps only the last 10 deliveries.

Workarounds:

  • After an outage, compare your records with the documentation's changelog in Bump.sh.
  • Return 200 quickly and do the slow work, such as SDK regeneration, afterwards.

How Hookdeck Can Help: Hookdeck stores each notification as soon as Bump.sh sends it and retries your service until it succeeds, with the full history available for replay.

No delivery ID

The Problem: You can't tell a second delivery of the same change from a new one using headers alone.

Why It Happens: Bump.sh documents no delivery ID or timestamp header.

Workarounds:

  • Use diff.id as the idempotency key. It identifies the change itself.

How Hookdeck Can Help: Hookdeck's deduplication can drop requests with a diff.id it has already seen.

Best practices

Verify the raw bytes

Compute the HMAC over the body exactly as received. In Express, use express.raw({ type: '*/*' }). In Next.js, use await request.arrayBuffer(), and in FastAPI, await request.body().

Use diff.breaking to decide urgency

Most structure changes are additive. Send breaking ones to people who need to act, and log the rest.

Keep the secret per webhook

Each URL has its own secret token. If several services receive Bump.sh notifications, give each one its own webhook and secret rather than sharing one.

Deduplicate on diff.id

Store diff.id with the work it triggered so that a repeated notification does nothing. See our guide to webhook idempotency.

Conclusion

Bump.sh sends a single kind of webhook, a structure change with api and diff objects, signed with a hex HMAC-SHA256 of the body. Accept the signature with or without its sha256= prefix, key your processing on diff.id, and plan for the fact that Bump.sh documents neither retries nor a delivery ID.

Hookdeck Event Gateway verifies each Bump.sh notification and keeps it until your service has handled it.

Get started with Hookdeck for free and handle Bump.sh webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.