Webhook Dispatcher — Service Overview
Status: populated Owner: Platform Engineering Last updated: 2026-04-18 Companion: DOMAIN_MODEL · API_CONTRACTS · EVENT_SCHEMAS
1. Purpose
The Webhook Dispatcher delivers signed HTTP POST notifications to customer-registered webhook endpoints whenever a delivery receipt (DLR) event is processed. It consumes webhook.dispatch events from NATS JetStream, resolves the target URL(s) for the event's accountId, signs the payload with HMAC-SHA256, and delivers the notification with configurable retry and exponential backoff.
After all retry attempts are exhausted, the delivery is routed to a dead-letter NATS subject and the hook.delivery_attempts record is marked DEAD_LETTER.
2. Responsibilities
| Responsibility | Description |
|---|---|
| Webhook CRUD | REST API for customers to register, update, and delete webhook endpoints |
| Event consumption | Durable NATS JetStream consumer on webhook.dispatch |
| Signature generation | HMAC-SHA256 signing of each request body with per-webhook secret |
| HTTP delivery | POST to customer URL; 5 s timeout; no redirect follow; 2xx = success |
| Retry with backoff | 5 attempts: 30 s → 5 min → 30 min → 2 h → 8 h |
| Retry state in DB | All attempt state stored in hook.delivery_attempts (no Redis) |
| Dead-letter routing | After 5 failed attempts: publish webhook.dispatch.deadletter; mark DEAD_LETTER |
| Observability | Prometheus /metrics, structured JSON logs, OTLP traces |
3. Position in the Platform
dlr-processor ──► webhook.dispatch ──► webhook-dispatcher ──► customer HTTPS endpoint
│
├──► hook.webhook_configs (PG) — URL lookup
├──► hook.delivery_attempts (PG) — retry state
└──► webhook.dispatch.deadletter (NATS, on exhaustion)
Customer API (via Kong) ──► webhook-dispatcher REST API ──► hook.webhook_configs (PG)
4. Technology Stack
| Concern | Technology |
|---|---|
| Runtime | Node.js 20 LTS |
| Framework | NestJS 10 |
| Language | TypeScript 5 |
| Message broker | NATS JetStream |
| Database | PostgreSQL 15 (schema: hook) |
| HTTP client | undici (Node.js native, 5 s timeout, no redirect) |
| Signing | Node.js built-in crypto.createHmac (HMAC-SHA256) |
| Migrations | Flyway |
| API Gateway | Kong (REST API routes) |
| Containerisation | Docker / Kubernetes |
| Observability | Pino (logs), OpenTelemetry (traces), Prometheus (metrics) |
5. Non-Goals
- Does not own the DLR processing pipeline (delegated to
dlr-processor). - Does not store message content or phone numbers persistently.
- Does not use Redis for retry state (all retry tracking in PostgreSQL).
- Does not support protocols other than HTTPS POST.
- Does not support webhook fanout to > 10 endpoints per account (platform limit).
6. SLOs
| Metric | Target |
|---|---|
| First delivery attempt latency (p99) | < 2 s from event receipt |
| Delivery success rate (at least one attempt) | ≥ 99% of correlated DLRs |
| Retry exhaustion rate | < 1% of dispatched events |
| REST API latency (p99) | < 200 ms |
| REST API availability | ≥ 99.9% |