Agent skill

Bolt Webhooks Skill

Receive and verify Bolt checkout (boltapp.com) webhooks. Use when setting up Bolt webhook handlers, debugging X-Bolt-Hmac-Sha256 signature verification, or handling transaction events like payment, capture, credit, void, auth, pending, and rejected_irreversible.

Install this skill

npx skills add hookdeck/webhook-skills --skill bolt-webhooks


Bolt here is the Bolt checkout and payments platform (boltapp.com), not Bolt ride-hailing or bolt.new.

When to Use This Skill

  • How do I receive Bolt webhooks?
  • How do I verify the X-Bolt-Hmac-Sha256 signature on a Bolt webhook?
  • How do I handle Bolt payment, capture, credit, void, or rejected_irreversible events?
  • Why is my Bolt webhook signature verification failing?
  • Why does Bolt keep retrying my webhook?

Verification (core)

Bolt signs the raw request body with HMAC-SHA256, keyed with your Signing Secret, and sends the result base64-encoded in X-Bolt-Hmac-Sha256. There is no timestamp and no signature prefix. The Signing Secret (Merchant Dashboard > Administration > API > Signing Secret) is used as a raw string.

Node:

const crypto = require('crypto');

function verifyBoltWebhook(rawBody, hmacHeader, secret) {
  if (!hmacHeader || !secret) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
  try {
    return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(hmacHeader));
  } catch {
    return false; // different lengths
  }
}

Python:

import base64, hashlib, hmac

def verify_bolt_webhook(raw_body: bytes, hmac_header: str, secret: str) -> bool:
    if not hmac_header or not secret:
        return False
    expected = base64.b64encode(
        hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
    ).decode()
    return hmac.compare_digest(expected.encode(), hmac_header.encode())

Bolt does not offer an SDK for receiving or verifying webhooks, so verification is manual in every framework. Verify before parsing JSON: re-serializing changes the bytes and breaks the signature.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

The event type is the top-level type field of the JSON body (the object is transaction; credit_card_deleted has object credit_card).

EventDescription
pendingPre-authorization succeeded; transaction is in fraud review
failed_paymentPayment failed pre-authorization
paymentAutomatic capture: passed authorization and fraud review, funds captured
authManual capture: authorized, ready for you to capture
captureA manual capture succeeded
creditA refund or credit was processed
voidThe transaction was voided
rejected_irreversibleFailed fraud review; cannot be appealed
rejected_reversibleFlagged in fraud review; a re-review can be requested
risk_insightsRisk information from the fraud review is available
newsletter_subscriptionCustomer subscribed to the merchant's newsletter
credit_card_deletedCustomer removed a saved card (beta, custom cart)

Subscription events (subscription_created, subscription_renewed, ...) exist too. Acknowledge event types you don't process. See references/overview.md.

Handler Notes

  • Respond HTTP 200 with a valid JSON body (e.g. {"received": true}). Any other status, or a non-JSON body, makes Bolt retry with exponential back-off.
  • Payloads carry no event ID, so make handlers idempotent on type + data.id / data.reference.
  • Webhooks are separate from Merchant Callbacks and are configured under Administration > Developers > Webhooks.

Environment Variables

BOLT_SIGNING_SECRET=your_signing_secret   # Merchant Dashboard > Administration > API > Signing Secret

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 bolt --path /webhooks/bolt

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 2, 2026

View on GitHub →