Back to Projects
LiveSaaS2026

HookScope

Real-time webhook inspector for developers. See incoming webhooks instantly in your dashboard, forward them to your local server automatically, and retry on failure with exponential backoff. Ships with a Go CLI for zero-config localhost tunneling.

NestJSTypeScriptGoPostgreSQLRedisBullMQWebSockets

Webhook development is broken. You set up ngrok, paste the tunnel URL into Stripe, trigger a test event, and — nothing arrives. HookScope replaces that entire workflow with a live dashboard, automatic localhost forwarding, and a zero-config Go CLI. I built it because I was frustrated enough to spend a week making the frustration disappear.

The Problem in Detail

Every developer working with webhooks has the same setup ritual: run ngrok, copy the dynamic URL, update it in the provider dashboard, test, realise the URL changed after a restart, update it again. Then there is the debugging problem — when a webhook fails, you get a status code and maybe a vague error. You do not get the raw payload, the headers, or a timeline of what happened.

HookScope gives you a permanent endpoint that never changes. You point Stripe, GitHub, or any other provider at it once. Every webhook that hits it appears instantly in your dashboard with the full payload, all headers, and the processing timeline. Your local dev server receives a forwarded copy automatically.

System Architecture

HookScope is three separate services that work together:

┌─────────────────────────────────────────┐
│           Webhook Provider               │
│     (Stripe, GitHub, Twilio, ...)        │
└──────────────────┬──────────────────────┘
                   │ POST
                   ▼
┌─────────────────────────────────────────┐
│           NestJS API (backend)           │
│  • Receives & stores webhook             │
│  • Publishes to Redis pub/sub            │
│  • Queues retry jobs (BullMQ)            │
│  • Forwards to local CLI via WS          │
└───────────┬─────────────────────────────┘
            │ WebSocket                    
            ▼                             
┌───────────────────┐   ┌─────────────────┐
│  React Dashboard  │   │   Go CLI         │
│  (live feed,      │   │  (local tunnel,  │
│   replay, logs)   │   │   zero-config)   │
└───────────────────┘   └─────────────────┘

Real-Time Dashboard with WebSockets

The dashboard subscribes to a WebSocket connection on load. When a webhook arrives at the NestJS endpoint, it is stored in PostgreSQL, then published to a Redis channel. A NestJS gateway subscribes to that channel and fans out the event to all connected dashboard clients for that endpoint.

This architecture means the dashboard update happens in under 100ms from the time the webhook arrives — fast enough that you see the event appear before you switch windows from the provider's test tool.

The Go CLI

The CLI is the piece I am most proud of. It is a single binary that authenticates with your HookScope account, opens a persistent WebSocket connection to the backend, and forwards every incoming webhook to a local port of your choice:

# Install
go install github.com/NowackiKuba/hookscope-cli@latest

# Run (zero config after first auth)
hookscope forward --port 3000

# Output
✓ Connected to HookScope
✓ Forwarding webhooks → http://localhost:3000
  
→ POST /webhooks/stripe  200 OK  (47ms)
→ POST /webhooks/stripe  200 OK  (23ms)
→ POST /webhooks/stripe  500 ERR (12ms) [queued for retry]

The CLI maintains the WebSocket connection with exponential backoff reconnection. If your local server is down when a webhook arrives, HookScope queues the forward attempt and retries automatically when the CLI reconnects.

Retry System with BullMQ and Redis

Failed webhook deliveries — whether to your local server or to your production endpoint — are queued in BullMQ with exponential backoff and jitter:

const delay = Math.min(
  BASE_DELAY * Math.pow(2, attempt) + Math.random() * JITTER,
  MAX_DELAY
);
// attempt 1: ~2s, attempt 2: ~4s, attempt 3: ~8s, ...
// capped at 30 minutes

Every retry attempt is logged with its status, delay, and response. The dashboard shows the full retry timeline so you can see exactly what happened and when.

Schema Drift Detection

HookScope watches for changes in the shape of webhook payloads over time. Each incoming payload is flattened into a dot-notation schema map and compared against the stored baseline for that event type. If a field appears, disappears, or changes type, an alert is raised in the dashboard.

I wrote a full deep-dive on the schema drift implementation — see the blog post How I Built Webhook Schema Drift Detection in HookScope for the technical details.

Signature Verification

HookScope verifies incoming webhook signatures from known providers (Stripe, GitHub, Twilio, Shopify) using provider-specific adapters. Secrets are stored encrypted with AES-256-GCM. The raw request body is preserved before any parsing — a critical detail since HMAC-SHA256 verification requires the exact byte sequence the provider signed.

What I Would Do Differently

The WebSocket fan-out through Redis pub/sub works well but adds a round-trip that would not be necessary in a single-instance deployment. For the current scale, it is fine. If HookScope grew to requiring horizontal scaling of the NestJS service, the pub/sub architecture would pay for itself immediately. For a solo project, it was probably premature optimisation.

I would also ship the Go CLI earlier. It was the last thing I built, but in testing it turned out to be the feature that most impressed users. Build the DX-facing piece first.

Status

HookScope is live at app.hookscope.dev. The CLI is published to GitHub. The dashboard, forwarding, retry system, schema drift detection, and signature verification are all in production.

Interested in working together?

I am available for new projects — whether you have a clear brief or just an idea worth exploring.