Webhooks
v3.8.1Last updated: 2026-05-13
Was this page helpful?
Loading OmniRoute...
Source of truth: , ,
Last updated: 2026-05-13 — v3.8.0
type () currently models:
to receive every event. Unknown event
names in are ignored at dispatch time.
events are still landing. Check to see
which paths currently invoke the dispatcher in your release.
swallows per-webhook errors so one bad receiver cannot block the others.
, OmniRoute signs the JSON body and sends:
prefix (not ). The signature
value is — verify the full prefix.
is called without a secret, the DB module generates one
() so all webhooks are signed by default.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, signature: string, secret: string) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && timingSafeEqual(a, b);
}
raw request body, before any JSON parsing.
:
, and either resets
or increments . is automatically disabled. (migration ):
no separate table in the current schema —
delivery history is aggregated on the row. If you need full audit
history, consume / style events from a downstream
log store.
).
| (no retries) |
masks the secret to to avoid leaking
on listing pages. Use the GET when you actually need the secret.
curl -X POST http://localhost:20128/api/webhooks \
-H "Cookie: auth_token=..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.slack.com/services/...",
"secret": "whsec_my_shared_secret",
"events": ["quota.exceeded", "provider.error"],
"description": "Slack alerts"
}'
is omitted, the server generates a secret and returns
it in the response.
curl -X POST http://localhost:20128/api/webhooks/<id>/test \ -H "Cookie: auth_token=..."
. No retries are attempted — useful for quickly validating that the receiver accepts the payload and signature.
(see
) provides:
, and {
"event": "request.completed",
"timestamp": "2026-05-13T20:30:00.123Z",
"data": {
"trace_id": "...",
"api_key_id": "...",
"provider": "openai",
"model": "gpt-5",
"status": 200,
"tokens_in": 142,
"tokens_out": 350,
"cost_usd": 0.0042
}
}
{
"event": "provider.error",
"timestamp": "2026-05-13T20:31:00.000Z",
"data": {
"provider": "anthropic",
"status": 503,
"consecutive_failures": 5,
"circuit_state": "open"
}
}
{
"event": "test.ping",
"timestamp": "2026-05-13T20:32:00.000Z",
"data": {
"message": "Test webhook delivery from OmniRoute",
"webhookId": "<uuid>"
}
}
events are defined by the call sites that emit
them; treat the object as forward-compatible (add fields, don't depend on
absence).
. will
add cost on receivers you do not control. — endpoints are auto-disabled at 10 consecutive
failures; reset by calling with
after fixing the receiver. a new , deploy the new value
to the receiver, and confirm via the test endpoint. /