Mock APIs. Test webhooks. Debug integrations.
Features β’ Use Cases β’ Quick Start β’ API β’ Contributing
Testing integrations is painful:
- ngrok setup for every webhook project
- No visibility into what external services send
- Hard to mock third-party API responses in tests
- Context switching between terminal and browser
A single binary server that captures webhooks AND mocks API responses. Zero config, instant feedback, beautiful UI.
| Feature | Description |
|---|---|
| π Per-key Routing | /webhook/{key} β each key has independent response config |
| π§ͺ Mock API Server | Use as a mock server in unit/integration tests |
| β‘ Rule Engine | Expression-based conditional responses (docs) |
| π― Real-time Updates | SSE streaming, see requests as they arrive |
| π¨ Beautiful UI | Embedded React + Tailwind, color-coded HTTP methods |
| π§ Configurable Responses | Set status codes and JSON responses per endpoint |
| π¦ Single Binary | No dependencies, just go run . |
- Go 1.18 or later
go run .http://localhost:8080
curl -X POST \
-H "Content-Type: application/json" \
-d '{"message":"hello"}' \
http://localhost:8080/webhookEach key gets its own response configuration:
# Stripe webhooks
curl -X POST -d '{"type":"payment"}' http://localhost:8080/webhook/stripe
# GitHub webhooks
curl -X POST -d '{"action":"push"}' http://localhost:8080/webhook/githubCapture and inspect incoming webhooks from external services:
# Point Stripe/GitHub/etc. to your Hooklab instance
curl -X POST -d '{"event":"payment.success"}' http://localhost:8080/webhook/stripeUse Hooklab as a mock server in your tests:
JavaScript/Jest:
// Configure mock response before test
await fetch('http://localhost:8080/api/response?key=payment-api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
response: { id: 'ch_123', status: 'succeeded' },
statusCode: 200
})
});
// Your code calls the mock instead of real API
const result = await paymentService.charge({
apiUrl: 'http://localhost:8080/webhook/payment-api'
});
expect(result.status).toBe('succeeded');Go:
// Setup: configure Hooklab response
resp, _ := http.Post(
"http://localhost:8080/api/response?key=external-api",
"application/json",
strings.NewReader(`{"response":{"success":true},"statusCode":200}`),
)
// Test: point your code to Hooklab
client := NewClient("http://localhost:8080/webhook/external-api")
result, err := client.DoSomething()
assert.True(t, result.Success)Test how your code handles failures:
# Configure 500 error response
curl -X POST http://localhost:8080/api/response?key=flaky-api \
-H "Content-Type: application/json" \
-d '{"response":{"error":"Internal Server Error"},"statusCode":500}'
# Your integration tests can now verify error handlingCreate conditional responses based on request data:
# Create a rule: return error for high-value transactions
curl -X POST "http://localhost:8080/api/rules?key=payments" \
-H "Content-Type: application/json" \
-d '{
"name": "High Value Alert",
"condition": "body.amount > 1000",
"response": {"status": "review_required"},
"statusCode": 202,
"priority": 1,
"enabled": true
}'Rules are evaluated in priority order. First match wins. See RULES.md for full expression syntax.
Run Hooklab in your CI pipeline:
# GitHub Actions example
services:
hooklab:
image: golang:1.21
ports:
- 8080:8080
command: go run github.com/essajiwa/hooklab@latest| Flag | Description | Default |
|---|---|---|
-port |
HTTP server port | 8080 |
-response |
Default JSON response | {"result":"ok"} |
| Method | Endpoint | Description |
|---|---|---|
ANY |
/webhook or /webhook/{key} |
Capture webhook, return configured response |
GET |
/api/events?key={key} |
List recent events (optional key filter) |
GET |
/api/stream |
SSE stream of all events |
GET |
/api/response?key={key} |
Get response config for a key |
POST |
/api/response?key={key} |
Update response config { response, statusCode } |
GET |
/api/rules?key={key} |
List rules for a webhook key |
POST |
/api/rules?key={key} |
Create a new rule |
PUT |
/api/rules?key={key}&id={id} |
Update an existing rule |
DELETE |
/api/rules?key={key}&id={id} |
Delete a rule |
GET |
/api/keys |
List all known webhook keys |
| Hooklab | webhook.site | ngrok | |
|---|---|---|---|
| Self-hosted | β | β | β |
| Per-key routing | β | β | β |
| Custom responses | β | Limited | β |
| Rule engine | β | β | β |
| Real-time UI | β | β | β |
| Free & Open Source | β | Freemium | Freemium |
| Single binary | β | N/A | β |
Hooklab is designed as a development and testing tool. Before deploying:
| Concern | Status | Notes |
|---|---|---|
| Authentication | β None | All endpoints are public by default |
| Request Size | β Limited | Bodies capped at 1MB to prevent DoS |
| Data Exposure | Request headers (including auth tokens) are stored and displayed | |
| Rate Limiting | β None | No built-in rate limiting |
Recommendations:
- Do NOT expose to the public internet without a reverse proxy (nginx, Caddy) with authentication
- Use behind a VPN or firewall for team access
- Consider the systemd service with
RuntimeMaxSecfor periodic data reset - Sensitive headers (
Authorization,Cookie, etc.) will be visible in the UI
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Look for issues labeled good first issue to get started.
- React β Interactive UI (via CDN)
- Tailwind CSS β Utility-first styling
- CodeMirror β JSON editor with syntax highlighting
- Prism.js β Code syntax highlighting
- Lucide Icons β Beautiful icons
- Google Fonts β IBM Plex Sans & Playfair Display
MIT β see LICENSE.
Made with β€οΈ by @essajiwa


