Capture, inspect, and replay webhooks locally. No tunnels. No accounts. No cloud.
- Capture any incoming HTTP request at a named endpoint
- Inspect full headers, body, method, and timestamp
- Replay any captured request to the original URL or a custom target
- SQLite persistence — data survives restarts, stored in a single local file
- Local-first — runs entirely on your machine, zero external dependencies
# 1. Install
pip install -r requirements.txt
# 2. Run
uvicorn main:app --reload
# HookDump running
# Capture endpoint : http://localhost:8000/hooks/{slug}
# Docs : http://localhost:8000/docs
# 3. Point your webhook provider at:
# http://localhost:8000/hooks/stripe
# http://localhost:8000/hooks/github
# http://localhost:8000/hooks/<anything>
# 4. Send a test request
curl -X POST http://localhost:8000/hooks/stripe \
-H "Content-Type: application/json" \
-d '{"type": "payment.succeeded", "amount": 4200}'
# {"id": "3f2a...","slug": "stripe","status": "captured"}docker compose upThat's it. HookDump is running at http://localhost:8000.
Captured requests are persisted in a Docker named volume (hookdump_data) and survive container restarts and rebuilds.
# Inspect the SQLite file directly
docker compose exec hookdump sqlite3 /data/hookdump.db "SELECT id, slug, method, timestamp FROM requests;"# Send a test webhook
curl -X POST http://localhost:8000/hooks/stripe \
-H "Content-Type: application/json" \
-d '{"type": "payment.succeeded"}'
# Browse the interactive docs
open http://localhost:8000/docs# Capture a webhook
curl -X POST http://localhost:8000/hooks/stripe \
-H "Content-Type: application/json" \
-H "Stripe-Signature: whsec_test" \
-d '{"type":"invoice.paid"}'
# List captured requests
curl http://localhost:8000/requests
# [{"id":"abc123","slug":"stripe","method":"POST","timestamp":"..."}]
# Inspect full detail (headers + body)
curl http://localhost:8000/requests/abc123
# Replay to original URL
curl -X POST http://localhost:8000/requests/abc123/replay
# Replay to a different target
curl -X POST "http://localhost:8000/requests/abc123/replay?target_url=http://localhost:3000/webhooks"| Method | Path | Description |
|---|---|---|
POST |
/hooks/{slug} |
Capture an incoming request. slug is a free label (stripe, github, …) |
GET |
/requests |
List recent captures. Params: ?limit=50&slug=stripe |
GET |
/requests/{id} |
Full detail: method, headers, body, timestamp |
DELETE |
/requests/{id} |
Remove a captured request |
POST |
/requests/{id}/replay |
Replay to original URL or ?target_url=https://… |
GET |
/health |
Liveness check |
GET |
/docs |
Interactive API docs (Swagger UI) |
Stripe webhook debugging — point your Stripe test webhook at /hooks/stripe, trigger events in the Stripe dashboard, inspect exactly what arrived, replay as many times as needed without re-triggering events.
GitHub webhook testing — capture push or PR events from GitHub, inspect the full payload and headers (including X-Hub-Signature), replay against a local handler under development.
Local integration debugging — when building an integration that receives webhooks, HookDump acts as a local inbox: capture once, iterate on your handler, replay on demand.
Webhook provider
│
│ POST /hooks/{slug}
▼
┌─────────────────────────┐
│ HookDump │
│ (FastAPI) │
│ │
│ /hooks/{slug} → db │
│ /requests ← db │
│ /replay → → │──► Target URL
│ │
│ hookdump.db (SQLite) │
└─────────────────────────┘
Single process. Single file database. No moving parts.
No tunneling required. Tools like ngrok expose your machine to the internet. HookDump stays local — point your webhook provider at a tunnel once to populate HookDump, then replay locally forever.
No accounts. Nothing to sign up for, no API keys, no rate limits.
Deterministic. Replay the exact same bytes that arrived. Headers, body, method — everything forwarded verbatim.
Readable codebase. Under 150 lines across 5 files. Audit it in 10 minutes.
MIT — see LICENSE.
