npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@better-webhook/cli

v3.4.1

Published

Modern CLI for developing, capturing, and replaying webhooks locally with dashboard UI.

Readme

@better-webhook/cli

npm npm monthly

A modern CLI tool for webhook development, testing, and debugging. Capture incoming webhooks, replay them against your local server, manage reusable templates, and generate provider-specific signatures automatically.

Features

  • 🎣 Capture — Start a local server to capture incoming webhooks from any provider
  • 🔄 Replay — Replay captured webhooks to any endpoint with full header preservation
  • 📋 Templates — Download and run curated webhook templates from the community
  • 🔐 Signatures — Automatic signature generation for Stripe, GitHub, Shopify, Slack, and more
  • 🌐 WebSocket — Real-time capture notifications via WebSocket for dashboard integration
  • 🎯 Provider Detection — Automatically identifies webhook providers from headers

Supported Providers

Signature Generation Support

The CLI can automatically generate signatures for the following providers when you provide a --secret:

| Provider | Signature Algorithm | Auto-Detection | | ------------ | ------------------------------- | -------------- | | Stripe | HMAC-SHA256 (t={ts},v1={sig}) | ✅ | | GitHub | HMAC-SHA256 (sha256={sig}) | ✅ | | Shopify | HMAC-SHA256 (Base64) | ✅ | | Slack | HMAC-SHA256 (v0={sig}) | ✅ | | Twilio | HMAC-SHA1 (Base64) | ✅ | | SendGrid | HMAC-SHA256 (Base64) | ✅ | | Linear | HMAC-SHA256 (Hex) | ✅ | | Clerk (Svix) | HMAC-SHA256 (v1,{sig}) | ✅ | | Ragie | HMAC-SHA256 (Hex) | ✅ |

Detection Only (No Signature Generation)

These providers are auto-detected from headers but signature generation is not yet implemented:

| Provider | Notes | | -------- | ----------------------------------------------------- | | Discord | Detected from headers; uses Ed25519 (not implemented) | | Custom | Generic detection; no signature generation |

Installation

# NPM
npm install -g @better-webhook/cli

# Yarn
yarn global add @better-webhook/cli

# PNPM
pnpm add -g @better-webhook/cli

# Or use with npx (no installation required)
npx @better-webhook/cli --help

Verify Installation

better-webhook --version

Quick Start

1. Capture Webhooks

Start a local server to capture incoming webhooks:

# Start capture server on default port 3001
better-webhook capture

# Use a custom port
better-webhook capture --port 4000

Point your webhook provider (Stripe, GitHub, etc.) to http://localhost:3001 or use a tunneling service like ngrok.

2. View & Manage Captures

# List captured webhooks
better-webhook captures list

# Show detailed information about a capture
better-webhook captures show abc123

# Search captures
better-webhook captures search "github"

# Delete a capture
better-webhook captures delete abc123

3. Replay Webhooks

Replay a captured webhook to your local development server:

# Interactive mode (select capture and enter URL)
better-webhook replay

# Direct replay
better-webhook replay abc123 http://localhost:3000/api/webhooks/github

4. Use Templates

Download and run curated webhook templates:

# List available templates
better-webhook templates list

# Download a template
better-webhook templates download github-push

# Run a template against your endpoint
better-webhook run github-push --url http://localhost:3000/webhooks/github

Commands Reference

better-webhook capture

Start a server to capture incoming webhooks. All captured webhooks are saved to ~/.better-webhook/captures/.

better-webhook capture [options]

| Option | Description | Default | | ------------------- | ----------------- | --------- | | -p, --port <port> | Port to listen on | 3001 | | -h, --host <host> | Host to bind to | 0.0.0.0 |

Features:

  • Automatically detects webhook provider from headers
  • Saves full request including headers, body, query params
  • WebSocket server for real-time notifications
  • Returns capture ID in response for easy reference

Example:

better-webhook capture --port 4000 --host localhost

better-webhook captures (alias: c)

Manage captured webhooks.

captures list (alias: ls)

List captured webhooks, sorted by most recent first.

better-webhook captures list [options]

| Option | Description | Default | | --------------------------- | ----------------------------------------- | ------- | | -l, --limit <limit> | Maximum captures to show | 20 | | -p, --provider <provider> | Filter by provider (stripe, github, etc.) | — |

captures show <captureId>

Show detailed information about a specific capture.

better-webhook captures show <captureId> [options]

| Option | Description | | ------------ | ---------------------- | | -b, --body | Show full body content |

Arguments:

  • <captureId> — Full or partial capture ID

captures search <query>

Search captures by ID, path, method, provider, or filename.

better-webhook captures search <query>

captures delete (alias: rm)

Delete a specific captured webhook.

better-webhook captures delete <captureId> [options]

| Option | Description | | ------------- | ------------------------ | | -f, --force | Skip confirmation prompt |

captures clean (alias: remove-all)

Remove all captured webhooks.

better-webhook captures clean [options]

| Option | Description | | ------------- | ------------------------ | | -f, --force | Skip confirmation prompt |


better-webhook templates (alias: t)

Manage webhook templates. Templates are fetched from the better-webhook repository.

templates list (alias: ls)

List available remote templates from the repository.

better-webhook templates list [options]

| Option | Description | | --------------------------- | -------------------------------------- | | -p, --provider <provider> | Filter by provider | | -r, --refresh | Force refresh the template index cache |

templates download (alias: get)

Download a template to local storage (~/.better-webhook/templates/).

better-webhook templates download [templateId] [options]

| Option | Description | | ----------- | -------------------------------- | | -a, --all | Download all available templates |

If no templateId is provided, shows an interactive selection menu.

templates local

List downloaded local templates.

better-webhook templates local [options]

| Option | Description | | --------------------------- | ------------------ | | -p, --provider <provider> | Filter by provider |

templates search <query>

Search templates by name, provider, or event type.

better-webhook templates search <query>

templates cache

Manage the template index cache.

better-webhook templates cache [options]

| Option | Description | | ------------- | ------------------------ | | -c, --clear | Clear the template cache |

templates clean (alias: remove-all)

Remove all downloaded templates.

better-webhook templates clean [options]

| Option | Description | | ------------- | ------------------------ | | -f, --force | Skip confirmation prompt |


better-webhook run

Run a webhook template against a target URL. Automatically generates provider-specific signatures when a secret is provided.

better-webhook run [templateId] [options]

| Option | Description | Required | | ----------------------- | ------------------------------------------ | -------- | | -u, --url <url> | Target URL to send the webhook to | ✅ | | -s, --secret <secret> | Secret for signature generation | — | | -H, --header <header> | Add custom header (format: key:value) | — | | -v, --verbose | Show detailed request/response information | — |

Arguments:

  • [templateId] — Template ID to run (interactive selection if omitted)

Signature Generation:

When you provide a secret (--secret), the CLI automatically generates the correct signature header based on the template's provider. You can also use environment variables (see Environment Variables).

Example:

# Run with inline secret
better-webhook run github-push \
  --url http://localhost:3000/api/webhooks/github \
  --secret "your-webhook-secret"

# Run with custom headers
better-webhook run github-push \
  --url http://localhost:3000/api/webhooks/github \
  --secret "$GITHUB_WEBHOOK_SECRET" \
  --header "X-Custom-Header:value" \
  --verbose

better-webhook replay

Replay a captured webhook to a target URL. Preserves original headers (except connection-related ones) and allows overrides.

better-webhook replay [captureId] [targetUrl] [options]

| Option | Description | | ----------------------- | -------------------------------------------- | | -m, --method <method> | Override HTTP method | | -H, --header <header> | Add or override header (format: key:value) | | -v, --verbose | Show detailed request/response information |

Arguments:

  • [captureId] — Capture ID to replay (interactive selection if omitted)
  • [targetUrl] — Target URL (prompts if omitted, defaults to original path on localhost:3000)

Example:

# Interactive mode
better-webhook replay

# Direct replay with options
better-webhook replay abc123 http://localhost:3000/webhooks \
  --method POST \
  --header "X-Debug:true" \
  --verbose

better-webhook dashboard

Start the local dashboard UI + API + WebSocket server.

By default, this command starts:

  • Dashboard UI: http://localhost:4000/
  • API: http://localhost:4000/api/*
  • WebSocket: ws://localhost:4000/ws
  • Capture server (in-process): http://localhost:3001/*
better-webhook dashboard [options]

| Option | Description | Default | | ----------------------- | --------------------------- | ----------- | | -p, --port <port> | Dashboard server port | 4000 | | -h, --host <host> | Dashboard server host | localhost | | --capture-port <port> | Capture server port | 3001 | | --capture-host <host> | Capture server host | 0.0.0.0 | | --no-capture | Do not start capture server | — | | --captures-dir <dir> | Override captures directory | — | | --templates-dir <dir> | Override templates base dir | — |

Security note: Keep the dashboard bound to localhost unless you trust your network. The API includes endpoints that can send HTTP requests to arbitrary URLs (run/replay).


Environment Variables

The CLI automatically reads webhook secrets from environment variables based on the provider:

| Provider | Environment Variable | | -------- | ------------------------- | | Stripe | STRIPE_WEBHOOK_SECRET | | GitHub | GITHUB_WEBHOOK_SECRET | | Shopify | SHOPIFY_WEBHOOK_SECRET | | Twilio | TWILIO_WEBHOOK_SECRET | | Slack | SLACK_WEBHOOK_SECRET | | Linear | LINEAR_WEBHOOK_SECRET | | Clerk | CLERK_WEBHOOK_SECRET | | SendGrid | SENDGRID_WEBHOOK_SECRET | | Ragie | RAGIE_WEBHOOK_SECRET | | Discord | DISCORD_WEBHOOK_SECRET | | Custom | WEBHOOK_SECRET |

Usage:

export GITHUB_WEBHOOK_SECRET="your-secret-here"
better-webhook run github-push --url http://localhost:3000/webhooks/github
# Secret is automatically used for signature generation

Storage Locations

All CLI data is stored in ~/.better-webhook/:

~/.better-webhook/
├── captures/                    # Captured webhook requests
│   ├── 2024-01-15_10-30-00_abc12345.json
│   └── 2024-01-15_11-00-00_def67890.json
├── templates/                   # Downloaded templates
│   ├── github/
│   │   ├── github-push.json
│   │   └── github-pull_request.json
│   └── stripe/
│       └── stripe-invoice.json
└── templates-cache.json         # Template index cache (1 hour TTL)

Webhook Template Format

Templates follow this JSON schema:

{
  "url": "https://api.example.com/webhook",
  "method": "POST",
  "headers": [
    { "key": "Content-Type", "value": "application/json" },
    { "key": "X-Custom-Header", "value": "custom-value" }
  ],
  "body": {
    "event": "user.created",
    "data": {
      "id": "12345",
      "email": "[email protected]"
    }
  },
  "provider": "custom",
  "event": "user.created",
  "description": "Triggered when a new user is created"
}

| Field | Type | Required | Description | | ------------- | ------ | -------- | --------------------------------------------------- | | url | string | — | Default target URL (can be overridden with --url) | | method | string | — | HTTP method (default: POST) | | headers | array | — | Array of { key, value } header objects | | body | any | — | Request payload (object or string) | | provider | string | — | Provider name for signature generation | | event | string | — | Event type identifier | | description | string | — | Human-readable description |


Captured Webhook Format

Captured webhooks are stored with full request details:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "method": "POST",
  "url": "/webhooks/github?action=opened",
  "path": "/webhooks/github",
  "headers": {
    "content-type": "application/json",
    "x-github-event": "push",
    "x-hub-signature-256": "sha256=..."
  },
  "body": { "...parsed JSON..." },
  "rawBody": "{...original string...}",
  "query": { "action": "opened" },
  "provider": "github",
  "contentType": "application/json",
  "contentLength": 1234
}

Use Cases

Local Development

Test your webhook endpoints during development:

# Terminal 1: Start your app
npm run dev

# Terminal 2: Capture webhooks
better-webhook capture --port 4000

# Configure your webhook provider to send to http://localhost:4000
# (use ngrok for external providers: ngrok http 4000)

# Terminal 3: Replay captured webhooks to your app
better-webhook replay abc123 http://localhost:3000/api/webhooks

Debugging Webhook Issues

# Capture the problematic webhook
better-webhook capture

# Inspect the full request
better-webhook captures show abc123 --body

# Replay to your local server with verbose output
better-webhook replay abc123 http://localhost:3000/webhooks --verbose

Testing Signature Verification

# Run a template with your production secret
better-webhook run stripe-invoice.payment_succeeded \
  --url http://localhost:3000/api/webhooks/stripe \
  --secret "whsec_your_stripe_secret" \
  --verbose

CI/CD Integration

# Test webhook endpoints in your pipeline
better-webhook templates download github-push
better-webhook run github-push \
  --url "$TEST_ENDPOINT" \
  --secret "$GITHUB_WEBHOOK_SECRET"

# Check exit code
if [ $? -eq 0 ]; then
  echo "Webhook test passed"
fi

WebSocket API

The capture server exposes a WebSocket endpoint on the same port for real-time notifications:

const ws = new WebSocket("ws://localhost:3001");

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);

  switch (message.type) {
    case "capture":
      console.log("New capture:", message.payload.capture);
      break;
    case "captures_updated":
      console.log("Captures list:", message.payload.captures);
      break;
  }
};

Message Types:

| Type | Description | Payload | | ------------------ | --------------------------- | --------------------- | | capture | New webhook captured | { file, capture } | | captures_updated | Initial state on connection | { captures, count } |


Error Handling

The CLI uses standard exit codes:

| Code | Description | | ---- | ------------------------------------------------- | | 0 | Success | | 1 | Error (validation, network, file not found, etc.) |

Detailed error messages are displayed in the terminal:

better-webhook run nonexistent-template --url http://localhost:3000
# ❌ Template not found: nonexistent-template
#    Download it with: better-webhook templates download nonexistent-template

better-webhook capture --port 99999
# Invalid port number

better-webhook replay abc123 invalid-url
# Please enter a valid URL

Development

Building from Source

git clone https://github.com/endalk200/better-webhook.git
cd better-webhook
pnpm install
pnpm --filter @better-webhook/cli build

Running Locally

cd apps/webhook-cli
pnpm start -- capture --port 3001

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Changelog

See CHANGELOG.md for version history.

License

MIT © Endalk

Support