# How the Webhook Dispatcher in OmniRoute Works: Event Taxonomy, Retry Logic, and Secret Rotation

> Understand OmniRoute's Webhook Dispatcher: explore event taxonomy, retry logic, and automatic secret rotation for reliable webhook delivery. Learn how it filters, signs, and retries events.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: deep-dive
- Published: 2026-08-22

---

**The Webhook Dispatcher in OmniRoute is a centralized delivery engine that filters events by taxonomy, signs payloads with HMAC-SHA256, implements exponential backoff retry logic with a 10-second timeout, and automatically disables endpoints after 10 consecutive failures.**

The Webhook Dispatcher in OmniRoute orchestrates real-time event delivery to external endpoints using a layered architecture that separates event taxonomy, payload construction, and delivery resilience. Written in TypeScript, this system handles everything from LLM request completions to quota alerts, ensuring secure transmission through signed payloads and intelligent retry mechanisms. Understanding its implementation reveals how the platform manages webhook subscriptions, transient network failures, and seamless secret rotation without requiring code changes.

## Event Taxonomy and Subscription Filtering

OmniRoute defines a strict event taxonomy in [`src/lib/webhooks/eventDescriptions.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/webhooks/eventDescriptions.ts), declaring the `WebhookEvent` type that supports four distinct events: `request.completed`, `request.failed`, `quota.exceeded`, and `test.ping`. Each event carries human-readable metadata through the accompanying `EVENT_DESCRIPTIONS` map, providing clear documentation for integrators.

The dispatcher filters subscriptions dynamically in `dispatchEvent` (line 68 of [`src/lib/webhookDispatcher.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/webhookDispatcher.ts)). When processing an event, the system checks each enabled webhook’s `events` array for either the specific event name or a wildcard `"*"`: `wh.events.includes("*") || wh.events.includes(event)`. This pattern enables both granular per-event subscriptions and catch-all configurations, ensuring endpoints receive only the traffic they explicitly request.

## Payload Construction and HMAC Signing

When an event fires, the dispatcher constructs a uniform `WebhookPayload` object (lines 61‑65 of [`src/lib/webhookDispatcher.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/webhookDispatcher.ts)) containing the event name, ISO timestamp, and event-specific data blob. For **custom webhooks**, the system invokes `deliverWebhook` (lines 71‑129), which handles provider-agnostic delivery with cryptographic verification.

Custom webhooks receive HMAC-SHA256 signed payloads. In `createWebhook` (lines 94‑95 of [`src/lib/db/webhooks.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/webhooks.ts)), secrets are either user-supplied or automatically generated with a `whsec_` prefix. The `signPayload` helper (lines 20‑22) adds the `X-Webhook-Signature` header only when `wh.secret` exists, computed at request time using the current secret value. Built-in integrations (Slack, Discord, Telegram) bypass this signing flow; instead, `deliverRaw` (lines 39‑68) transforms payloads via provider-specific builder modules in `src/lib/webhooks/integrations/`.

## Retry Semantics and Delivery Resilience

The `deliverWebhook` function implements configurable exponential backoff with a default `maxRetries` of 3 (line 75). The retry loop (lines 94‑126) executes the following logic for each attempt:

1. **Abort Handling**: Initializes an `AbortController` with a 10‑second timeout (lines 96‑98) to prevent hanging connections.
2. **Request Execution**: Performs the POST request to the webhook URL.
3. **Success Criteria**: Returns immediately on HTTP 2xx success or non‑5xx client errors (4xx responses trigger immediate failure without retry).
4. **Backoff Calculation**: On 5xx server errors or network failures, waits `2^attempt` seconds before retrying (lines 17‑19).

If all retries exhaust, the function returns `{ success: false, error: "..." }` (line 28), triggering failure recording and health check evaluation.

## Automatic Health Monitoring and Endpoint Disabling

After each delivery attempt, `recordWebhookDelivery` persists the result and updates success or failure counters in the database. The dispatcher then calls `disableWebhooksWithHighFailures(10)` (line 20 of `dispatchEvent`), which automatically disables any webhook accumulating 10 or more consecutive failures. This circuit-breaker pattern prevents the system from repeatedly hammering misconfigured or downed endpoints, preserving system resources and reducing noise.

## Secret Rotation Without Downtime

Secret rotation requires zero code changes in OmniRoute. Administrators update the `secret` column via the `updateWebhook` API, and subsequent deliveries automatically use the new value because signing occurs at request time rather than at configuration time. This design eliminates the need for rolling deployments or service restarts when rotating compromised or expired credentials.

## Practical Implementation Examples

To fire a webhook event after a successful LLM inference:

```typescript
import { notifyWebhookEvent } from "@/lib/webhookDispatcher";

const event = "request.completed";
const data = {
  model: "claude-opus-4-7",
  provider: "claude",
  latencyMs: 1240,
  tokensIn: 142,
  tokensOut: 38,
};

notifyWebhookEvent(event, data);

```

To register a new custom endpoint with explicit secret management:

```typescript
import { createWebhook } from "@/lib/db/webhooks";

const webhook = createWebhook({
  url: "https://example.com/omniroute-hook",
  events: ["request.completed", "request.failed"],
  secret: "my-very-secret-key", // Rotate by updating this value later
  description: "Audit log endpoint",
  kind: "custom",
});

```

## Summary

- **Event Taxonomy**: Four typed events (`request.completed`, `request.failed`, `quota.exceeded`, `test.ping`) defined in [`src/lib/webhooks/eventDescriptions.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/webhooks/eventDescriptions.ts), with wildcard `"*"` support for catch-all subscriptions.
- **Signed Payloads**: Custom webhooks receive HMAC-SHA256 signatures in the `X-Webhook-Signature` header, computed at request time using secrets from [`src/lib/db/webhooks.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/webhooks.ts).
- **Retry Logic**: Exponential backoff (`2^attempt` seconds) with a 10-second timeout, defaulting to 3 retries, treating 4xx errors as permanent failures and 5xx/network errors as transient.
- **Health Monitoring**: Automatic disabling of webhooks after 10 consecutive failures via `disableWebhooksWithHighFailures(10)`.
- **Secret Rotation**: Immediate effect upon database update without requiring code changes or restarts.

## Frequently Asked Questions

### What event types does the OmniRoute Webhook Dispatcher support?

The dispatcher supports four core events defined in [`src/lib/webhooks/eventDescriptions.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/webhooks/eventDescriptions.ts): `request.completed` for successful LLM inferences, `request.failed` for errors, `quota.exceeded` for rate-limit breaches, and `test.ping` for connectivity verification. Endpoints can subscribe to specific events or use the `"*"` wildcard to receive all events.

### How does the retry logic handle different HTTP status codes?

According to the implementation in [`src/lib/webhookDispatcher.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/webhookDispatcher.ts) (lines 13‑15), the dispatcher treats HTTP 4xx client errors as permanent failures that do not trigger retries, while 5xx server errors and network timeouts initiate exponential backoff. This prevents wasted retries on authentication failures (401/403) or bad requests (400) while resiliently handling temporary server outages.

### What triggers automatic webhook disabling in OmniRoute?

The system automatically disables webhooks that accumulate 10 or more consecutive failures. After each delivery attempt, `disableWebhooksWithHighFailures(10)` evaluates the failure count tracked in [`src/lib/db/webhooks.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/webhooks.ts) and disables the endpoint if the threshold is exceeded, acting as a circuit breaker to protect both OmniRoute and the receiving server.

### How do you rotate a webhook secret without downtime?

Administrators simply call the `updateWebhook` API to change the `secret` column value in the database. Since the `deliverWebhook` function reads the secret at request time and computes the HMAC signature dynamically (lines 20‑22), the new secret takes effect immediately on the next delivery without requiring code deployments or service restarts.