A self-hosted, open-source alternative to webhook.site. Captures HTTP requests, inbound emails, and DNS queries in real time and displays them in a live UI.
Two deployment targets — pick one or run both:
| Local dev | AWS | Cloudflare | |
|---|---|---|---|
| Start | node dev-server.js |
cdk deploy |
wrangler deploy |
| HTTP capture | ✓ | API Gateway + Lambda | Workers |
| Email capture | ✓ SMTP on :2525 | SES receipt rules | Email Workers |
| DNS capture | ✓ UDP on :5353 | Route 53 query logs | DNS-over-HTTPS |
| Real-time UI | SSE + WebSocket | API GW WS + DDB Streams | Durable Objects |
| Cost | Free | ~$5/mo | Free tier |
Every captured event is identified by a token ID — a short random string the browser generates and stores in localStorage. The token appears in all three capture URLs shown in the UI header.
| Source | Token convention |
|---|---|
| HTTP | First path segment: /{tokenId}/anything |
Local part of recipient: {tokenId}@hooks.example.com |
|
| DNS | First label of queried domain: {tokenId}.hooks.example.com |
Events arrive in the left panel in real time. Clicking one opens the detail view with full headers, body (JSON-highlighted), and a ready-to-copy curl replay command.
webhookit/
├── frontend/dist/index.html # Single-file SPA — same for all deployments
├── dev-server.js # Zero-dependency local dev server (Node.js stdlib only)
├── test_webhookit.py # Test suite — runs against any deployment target
│
├── cloudflare/ # Cloudflare Workers deployment
│ ├── wrangler.toml
│ └── src/
│ ├── worker.ts # Router — all HTTP routes
│ ├── token-hub.ts # Durable Object — WebSocket hub per token
│ ├── kv-store.ts # Event persistence in Workers KV
│ ├── http-capture.ts # HTTP request capture
│ ├── email-handler.ts # Email Workers handler
│ ├── dns-handler.ts # DNS-over-HTTPS proxy + capture
│ ├── broadcast.ts # Routes events to the TokenHub DO
│ ├── types.ts
│ └── util.ts
│
├── bin/app.ts # CDK app entry point
├── lib/
│ ├── webhookit-stack.ts # Root CDK stack
│ └── constructs/
│ ├── storage.ts # DynamoDB tables + S3 bucket
│ ├── event-bus.ts # SNS topic + Lambda processors
│ ├── http-ingress.ts # API Gateway HTTP catch-all
│ ├── email-ingress.ts # SES receipt rules
│ ├── dns-ingress.ts # Route 53 → CloudWatch → Lambda
│ ├── realtime.ts # WebSocket API + DynamoDB Stream broadcaster
│ └── frontend.ts # S3 + CloudFront
└── lib/lambdas/
├── http-capture.ts
├── http-processor.ts
├── email-processor.ts
├── dns-log-parser.ts
├── dns-processor.ts
├── ws-connect.ts
└── ws-broadcast.ts
No npm install required — dev-server.js uses only the Node.js standard library.
node dev-server.jsOpen http://localhost:8080. The server starts three listeners:
| Protocol | Port | Purpose |
|---|---|---|
| HTTP + WebSocket | 8080 | UI, capture endpoint, API |
| SMTP | 2525 | Email capture |
| DNS (UDP) | 5353 | DNS query capture |
To use a different port:
PORT=3000 DNS_PORT=8053 SMTP_PORT=2526 node dev-server.jsTOKEN=your-token-id # copy from the UI
# HTTP
curl -X POST http://localhost:8080/$TOKEN/webhook \
-H "Content-Type: application/json" \
-d '{"event": "push", "repo": "acme/api"}'
# Email (Python stdlib)
python3 -c "
import smtplib
s = smtplib.SMTP('localhost', 2525)
s.sendmail('from@example.com', '$TOKEN@localhost', 'Subject: test\n\nhello')
s.quit()
"
# DNS
dig @localhost -p 5353 $TOKEN.hooks.example.com AThe test suite works against any deployment target — local, AWS, or Cloudflare.
# Against local dev server (all tests)
python3 test_webhookit.py --token my-token
# Skip DNS (port 5353 in use on macOS — system resolver)
python3 test_webhookit.py --token my-token --skip-dns
# Use a free DNS port
DNS_PORT=8053 node dev-server.js
python3 test_webhookit.py --token my-token --dns-port 8053
# Against a deployed instance
python3 test_webhookit.py \
--http-url https://your-api-endpoint \
--token my-token \
--skip-dns \
--skip-emailAll 32 tests, covering:
- API endpoints (
/_/entries,/_/entries/{id},/_/clear,/config.json) - HTTP: POST/GET/PUT/DELETE, headers, body, query strings, nested paths, large bodies, token isolation
- DNS: A/AAAA/MX/TXT records, NXDOMAIN response, field extraction
- Email: connect, EHLO, send, multiple messages, raw body, token derivation
Prerequisites: A Cloudflare account with Workers enabled (free tier is enough).
cd cloudflare
# 1. Authenticate
npm install -g wrangler
wrangler login
# 2. Create the KV namespace and copy the id into wrangler.toml
wrangler kv:namespace create EVENTS
# 3. Deploy
npm install
npm run deployYour Worker is live at https://webhookit.<subdomain>.workers.dev.
- Cloudflare dashboard → Email → Email Routing → Routing Rules
- Add rule: match
*@yourdomain.com→ Send to Worker →webhookit - Send email to
{tokenId}@yourdomain.com
Cloudflare Workers can't bind UDP port 53, so DNS is captured via DNS-over-HTTPS. Point your browser or OS to:
https://webhookit.<subdomain>.workers.dev/dns-query?token={tokenId}
The Worker proxies to 1.1.1.1 so DNS resolution still works normally.
Add to cloudflare/wrangler.toml:
routes = [
{ pattern = "hooks.yourdomain.com/*", zone_name = "yourdomain.com" }
]Then add a CNAME: hooks.yourdomain.com → webhookit.<subdomain>.workers.dev
cd cloudflare
npm run dev # http://localhost:8787, KV + DO simulated by MiniflarePrerequisites: AWS CLI configured, Node.js 20+.
npm install -g aws-cdk
# One-time bootstrap per account/region
cdk bootstrap
# Deploy — HTTP and DNS capture, no custom domain
cdk deploy
# Deploy with email and a custom domain
cdk deploy \
--context domain=hooks.example.com \
--context sesDomain=hooks.example.com \
--context retentionDays=14cdk deploy prints all endpoints as stack outputs.
- Verify your domain in SES → Verified identities
- Add an MX record:
hooks.example.com MX 10 inbound-smtp.us-east-1.amazonaws.com - Deploy with
--context sesDomain=hooks.example.com - SES console → Email receiving — activate the
webhookit-inboundrule set
Run the command printed in the EnableDnsLoggingCommand stack output:
aws route53 create-query-logging-config \
--hosted-zone-id YOUR_ZONE_ID \
--cloud-watch-logs-log-group-arn <ARN from stack output>| Service | Monthly cost |
|---|---|
| Lambda (1M invocations) | ~$0.20 |
| DynamoDB on-demand | ~$0.25 per million writes |
| API Gateway HTTP | ~$1 per million requests |
| API Gateway WebSocket | ~$0.25 per million messages |
| CloudFront | ~$0.01 per GB |
| SNS | ~$0.50 per million publishes |
| Total | < $5/month at typical dev volume |
cdk destroyThe webhookit-events DynamoDB table and raw-email S3 bucket use RETAIN policy — delete them manually if you want a clean sweep.
| AWS | Cloudflare | |
|---|---|---|
| HTTP capture | API Gateway + Lambda | Worker fetch handler |
| Real-time push | API GW WebSocket + DDB Streams + Lambda broadcaster | Durable Object (WebSocket hibernation) |
| SES receipt rules | Email Workers | |
| DNS | Route 53 query logs → CloudWatch → Lambda | DNS-over-HTTPS proxy |
| Event storage | DynamoDB (TTL) + S3 (raw emails) | Workers KV (TTL) |
| Frontend | S3 + CloudFront | Workers static assets |
| IaC | AWS CDK (TypeScript) | wrangler.toml |
| Cold start | ~100ms (Lambda) | ~0ms (V8 isolates) |
| Deploy time | ~5 min | ~10 seconds |
| Free tier | Limited | Generous |