Hookbase
LoginGet Started Free
Back to Blog
Product Update

Hookbase CLI v2.4 — Scaffold, Filter, and Sign in One Release

The new hookbase init command scaffolds a working webhook handler in any of five frameworks. hookbase listen filters tunnel traffic before it reaches localhost. And hookbase trigger now signs payloads using the live provider catalog. Three additions, one upgrade.

Hookbase Team
May 2, 2026
5 min read

The CLI is the most direct surface developers touch when working with Hookbase. v2.4 ships three additions that compound: scaffold a handler, run a filtered tunnel against it, and trigger signed test events into it. Same workflow, three less papercuts.

npm install -g @hookbase/[email protected]

1. hookbase init — scaffold a working webhook handler

Starting a webhook receiver from scratch involves a handful of mistakes everyone makes once: parsing the body before verifying the signature, comparing signatures with === instead of timing-safe equality, mixing up the timestamp tolerance for Stripe vs Slack. We just generate the right code for you.

hookbase init --framework express --provider stripe --dir ./my-handler

Five frameworks supported:

  • Express — Node, raw-body parser via express.raw
  • Fastify — Node, custom string content-type parser to preserve raw bytes
  • Hono — Node/Bun/edge runtimes
  • Next.js — App Router route handler at app/webhook/route.ts
  • Cloudflare Worker — uses Web Crypto so no Node modules

Five providers supported (signature verifier specific to each):

  • Stripe — t=<ts>.v1=<sig> with 5-minute timestamp tolerance
  • GitHub — sha256=<hex>
  • Shopify — base64-encoded HMAC-SHA256
  • Slack — v0=<hex> over v0:timestamp:body
  • Custom — generic HMAC-SHA256

The generated handler reads the raw body (which signature verification requires), runs the provider-correct verifier inline, parses JSON only after verification passes, and returns 401 on bad signatures. Zero runtime dependencies beyond the framework itself.

my-handler/
├── index.js          # Working handler with Stripe verifier inline
├── package.json
├── .env.example
└── README.md
cd my-handler && npm install && npm run dev

That's a runnable receiver. Point a Hookbase tunnel at it and you're handling real Stripe events.

2. hookbase listen — filter at the tunnel boundary

hookbase listen <port> is a top-level alias for tunnels start. The reason it exists isn't the rename — it's the new filter flags.

A common dev setup: one tunnel, multiple sources, lots of noise. Maybe you're debugging a Stripe charge.succeeded flow but a chatty internal source keeps blasting unrelated events into your handler logs. Until now, you had to filter inside your handler.

# Only forward charge.* events
hookbase listen 3000 --filter-event 'charge.*'

# Multiple filters AND together
hookbase listen 3000 --filter-source stripe-prod --filter-event 'charge.succeeded'

# Arbitrary JSONata over the parsed body
hookbase listen 3000 --filter-expr 'amount > 1000'

Three filter dimensions, all repeatable and stackable:

  • --filter-source <slug> — match x-hookbase-source header or path prefix
  • --filter-event <pattern> — glob (* and ?) against detected event type. Detection sniffs x-github-event, x-shopify-topic, the Stripe body's .type, and generic body fields like event and event_type.
  • --filter-expr <jsonata> — JSONata expression evaluated against the parsed body. Truthy = pass.

Filtered requests come back from the tunnel with status 204 (configurable via --filter-skip-status) and an x-hookbase-skip-reason header explaining why. Your localhost never sees them. The CLI logs each skip:

POST /webhook → skipped (event "customer.subscription.created" did not match [charge.*])

JSONata is lazy-loaded — only when --filter-expr is used — so it doesn't bloat startup for the 90% of users who just want event-type filtering.

3. hookbase trigger — catalog-driven and signed by default

trigger already existed but shipped only five hardcoded sample payloads, sent unsigned. The "stripe-signature" header it sent was the literal string t=1234567890,v1=test. Any source with strict signature verification rejected it. We've reworked it from the ground up.

hookbase trigger --provider stripe --event charge.succeeded
ℹ Triggering webhook to tf-demo-stripe...
✓ Webhook delivered (200)
Signed with stripe-signature header
ℹ Event ID: 66db31af-38a9-49c7-9281-e577e67f993b

What changed:

  • Catalog-driven payloads — pulls from the same provider catalog the dashboard uses. Ten-plus providers, dozens of event types per provider, all with realistic sample bodies. No more "github_push", "stripe_payment", "shopify_order" generic placeholders — pick the actual event type.
  • Signed by default — the API computes a real HMAC signature using the source's actual signing_secret and the provider's correct algorithm. Your verification path runs end-to-end. Opt out with --no-sign if you specifically want to test rejection.
  • --print — output the would-be payload without sending. Useful for piping into other tools or just inspecting what a Stripe charge.succeeded looks like.
  • Better errors — typo an event type and you get the list of valid ones for that provider.

The signing logic on the server reuses the same computeSignature() utility that the live ingest path uses to verify signatures. They cannot drift — guarded by 63 round-trip unit tests across every signed-provider catalog entry.

How They Compose

These three commands are designed to chain. A common workflow:

# 1. Scaffold a Stripe handler
hookbase init --framework express --provider stripe --dir ./demo
cd demo && npm install && npm run dev &

# 2. Tunnel to it, but only forward charge events
hookbase listen 3000 --filter-event 'charge.*' &

# 3. Send a real signed Stripe event
hookbase trigger --provider stripe --event charge.succeeded

Three commands, three terminals — and you've exercised the full path: signed payload → ingest signature verification → tunnel filter → localhost handler signature verification → 200 OK. If anything is broken anywhere in the chain, you find out in seconds.

Backward Compatibility

Nothing breaks. tunnel, tunnels start, the legacy --event <template> shorthand on trigger, and every existing flag still work. The new flags are additive.

Get the Update

npm install -g @hookbase/[email protected]
hookbase --version

Or if you're using the GitHub Action: HookbaseApp/setup-tunnel@v1 already pins to the latest CLI by default.

Read the docs · Sign up free

product-updateclideveloper-toolstunnelssignature-verificationscaffolding

Related Articles

Product Update

Test Real Webhooks in CI With Three Lines of YAML

The new hookbase/setup-tunnel GitHub Action exposes a localhost port via a public Hookbase tunnel during CI runs. Receive real webhooks from Stripe, GitHub, Shopify, or any provider against ephemeral test environments — without managing tunnel lifecycle by hand.

Tutorial

Shopify Webhook Signature Verification, Explained

Shopify HMAC verification trips up almost every first-time integrator. Here is exactly how the signature is computed, what goes wrong, and a working implementation in Node, Python, Go, and Ruby.

Reference

Webhook Retries: What Every Provider Does Differently

Stripe retries for 3 days. GitHub gives up after one failure. Shopify retries 19 times. Knowing the rules for each provider is the difference between losing events and not. A reference table plus what it means for your handler.

Ready to Try Hookbase?

Start receiving, transforming, and routing webhooks in minutes.

Get Started Free
Hookbase

Reliable webhook infrastructure for modern teams. Built on Cloudflare's global edge network.

Product

  • Features
  • Pricing
  • Use Cases
  • Integrations
  • ngrok Alternative

Resources

  • Documentation
  • API Reference
  • CLI Guide
  • Blog
  • FAQ

Free Tools

  • All Tools
  • Webhook Bin
  • HMAC Calculator
  • JSONata Playground
  • Cron Builder
  • Payload Formatter
  • Local Testing

Legal

  • Privacy Policy
  • Terms of Service
  • Contact
  • Status

© 2026 Hookbase. All rights reserved.