WebhookBox is a Postgres-first webhook delivery service built in Rust.
It gives you:
- Durable event ingest with idempotency
- Fan-out deliveries to tenant endpoints
- Worker retries, backoff, and DLQ
- Delivery explainability (timeline + replay + curl)
- Tenant-scoped API keys for data-plane access
- Basic Prometheus-style metrics
- Rust
- Axum
- SQLx + Postgres
- Tokio
- Tracing
docker compose up -dCreate .env in project root:
DATABASE_URL=postgres://webhookbox:webhookbox@localhost:5432/webhookbox
HOST=127.0.0.1
PORT=3000
API_KEY=change-me-admin-key
SECRETS_KEY=change-me-secrets-key
RUST_LOG=infoAPI_KEY protects admin routes.
SECRETS_KEY encrypts endpoint secrets at rest.
sqlx migrate runcargo runIn a second terminal:
cargo run --bin worker- Admin routes (require
x-api-key):POST /tenantsPOST /tenants/:id/api-keysGET /metrics
- Tenant data routes (require
x-tenant-api-key):POST /eventsPOST/GET/PATCH /endpointsGET /events/:id/deliveriesGET /deliveries/:id/timelineGET /deliveries/:id/curlPOST /deliveries/:id/replay
curl -s -X POST http://127.0.0.1:3000/tenants \
-H "x-api-key: change-me-admin-key" \
-H "content-type: application/json" \
-d '{"name":"demo-tenant"}'Save tenant.id from the response.
curl -s -X POST http://127.0.0.1:3000/tenants/<TENANT_ID>/api-keys \
-H "x-api-key: change-me-admin-key" \
-H "content-type: application/json" \
-d '{"label":"demo"}'Save api_key.key from the response (returned once).
curl -s -X POST http://127.0.0.1:3000/endpoints \
-H "x-tenant-api-key: <TENANT_API_KEY>" \
-H "content-type: application/json" \
-d '{
"tenant_id":"<TENANT_ID>",
"url":"https://example.com/webhook",
"secret":"super-secret"
}'curl -s -X POST http://127.0.0.1:3000/events \
-H "x-tenant-api-key: <TENANT_API_KEY>" \
-H "content-type: application/json" \
-d '{
"tenant_id":"<TENANT_ID>",
"event_type":"user.created",
"payload":{"user_id":123},
"idempotency_key":"demo-123"
}'curl -s "http://127.0.0.1:3000/events/<EVENT_ID>/deliveries" \
-H "x-tenant-api-key: <TENANT_API_KEY>"curl -s "http://127.0.0.1:3000/deliveries/<DELIVERY_ID>/timeline" \
-H "x-tenant-api-key: <TENANT_API_KEY>"curl -s "http://127.0.0.1:3000/deliveries/<DELIVERY_ID>/curl" \
-H "x-tenant-api-key: <TENANT_API_KEY>"GET /metrics returns Prometheus-style text metrics (admin key required).
curl -s http://127.0.0.1:3000/metrics \
-H "x-api-key: change-me-admin-key"Example metrics:
webhookbox_events_totalwebhookbox_deliveries_status{status="retrying"}webhookbox_jobs_status{status="queued"}webhookbox_dead_letters_total
Use these as initial Prometheus/Grafana panels:
webhookbox_events_totalwebhookbox_deliveries_status{status="pending"}webhookbox_deliveries_status{status="retrying"}webhookbox_jobs_status{status="running"}webhookbox_dead_letters_total
- Failed jobs:
- message contains
job failed
- message contains
- Delivery attempts:
- message contains
sending webhook
- message contains
- Tenant auth failures:
- response error equals
invalid_tenant_api_keyortenant_access_denied
- response error equals
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --allIntegration tests use TEST_DATABASE_URL (or DATABASE_URL fallback).
TBD