← Back to the playground

Integrations · Interactive Tools

HookScope: see why a webhook fails

HookScope is the self-hosted webhook receiver I built in Python and FastAPI. It verifies every signature, stores every delivery (including the rejected ones), forwards events with retries, and lets you replay or reshape them. This page runs a working model of it in your browser, so you can send real signed requests, break them on purpose and watch what happens. It models GitHub, Stripe and generic senders; the signature lab covers Slack, Shopify, Twilio and Svix.

What usually goes wrong
  • wrong secretThe sender and receiver were configured with different keys.
  • body re-encodedA proxy reformats the JSON, so the bytes no longer match the signature.
  • replayedAn old, captured request is sent again later.
  • target downThe event arrived fine, but the service it feeds was failing.

Send a webhook

Pick a provider and a failure mode, then send. The signature is computed with real HMAC-SHA256 in your browser and checked the same way HookScope's signatures.py checks it.

Sender
What happens on the way
Request HookScope received
Send a webhook to see the request.
Signature check
Waiting for a delivery.
Response to the provider
-

Rejected deliveries get a 401 but are still stored, so you can see why they failed. Accepted ones get a 202 straight away and are forwarded in the background.

Dashboard

Every delivery lands here. Select one to see its headers, the forwards it triggered, and to preview a payload transform against it. The first three are example deliveries.

EventsGET /api/events
#SourceTypeSignature
Event

Forwarding rules in this demo

The same shape as HookScope's HOOKSCOPE_RULES file. The demo targets are simulated: billing is flaky, warehouse is down.

How it's built

A small FastAPI service with one pure function per provider, a thin SQLite store, and forwarding that runs after the provider has its answer.

GitHub · Stripegeneric HMAC POST /hooks/{source}main.py (FastAPI) signatures.verify()constant-time compare EventStoreSQLite rules.deliver()retry, backoff, DLQ targets · Slack · transformsslack.py, transform.py, replay.py DashboardJSON API accepted only,after the 202

Signatures that fail loudly

Each verifier is a pure function that never raises. A malformed or missing header becomes an invalid or unsigned result with a reason, stored next to the payload, instead of a stack trace.

Forwarding that never blocks

Matched rules run after HookScope has answered the provider. Transient failures (no response, 408, 425, 429, 5xx) retry with exponential backoff that honours Retry-After. Anything still failing becomes a dead letter you can re-send.

Reshape before you forward

Rules can post a readable Slack summary or a new JSON document built with JSONPath mappings, including wildcards, recursive descent and filter expressions, with a preview against any stored event.

Tested, linted, containerised

An app factory lets tests inject a temporary database, secrets, an HTTP client and a fake clock. Every push runs ruff and pytest in GitHub Actions, and a Dockerfile ships it.

Python 3FastAPISQLitehttpxJinja2pytestruffGitHub ActionsDocker

The sample payloads are illustrative test data, the secrets are public demo values, and the forwarding targets are simulated. This page models the backend in your browser, so nothing you type leaves it. HookScope is MIT licensed.

🖥 HookScope on GitHub 🔐 Webhook signature lab 🛝 More playground tools 🔧 Tech Blog
Rate my site!