Pairs with: AI Gateway → Budgets. Budgets enforce hard spend limits; anomaly rules are meant to catch a pattern before a budget breach, though only the spend-spike type is evaluated today.
Available on Enterprise plans. See Open-core licensing for the full Apache 2.0, Enterprise split.
AnomalyAlert row when a violation is detected.
This page documents what the detector evaluates and what is preview-only, so that a rule which looks “active” is not mistaken for one that fires.
Rule-type coverage
The schema holds every rule type below; the detector evaluates one of them today.
A ⏳ Preview rule type is persisted and reads as
active, but the detector logs debug and skips it, so no alert is produced. Until the detector slice for each ships, treat such a rule as stored for later rather than as watching anything.
If you need detection beyond spend_spike today, the recommended shape is one of:
- Stand up Prometheus + Grafana against the gateway metrics endpoint, see Cookbook · Prometheus alerts for the alert ruleset.
- Hook your SIEM to the OTel trace stream LangWatch already emits per project.
What a spend_spike rule is made of
These are the fields a rule carries and the detector reads. No screen collects them today and no public API accepts them; they describe rules that already exist.
Threshold config: spend_spike
Sensible starting values:
Scope coverage
Dispatch coverage
A rule’s Destination config lists where to push the alert. Webhook is the only destination type, and it works today: when a rule fires, the alert row is written first, then POSTed to each destination on the rule.
For Slack or PagerDuty, point a webhook at their own incoming-webhook or events endpoint and reshape the payload on your side.
Leave
destinations empty for a rule that records alerts without pushing them. A destination config that fails validation is quarantined: no request is sent, and the rule falls back to recording only.
What the webhook receives
A POST withContent-Type: application/json, carrying the rule and the alert it raised:
detail.dispatch reads pending in every payload, because the alert is written before delivery is attempted. The stored row is updated with the delivery outcome afterwards; the payload you receive is the pre-delivery snapshot.
Delivery guarantees
Delivery is best-effort, and the alert row is the authoritative signal. Each destination gets up to three attempts with a five-second timeout each. A 5xx response is retried with exponential backoff; a 4xx fails immediately, on the reading that it is a configuration error rather than a transient one. A destination that never succeeds is logged and recorded on the alert. It does not fail the detector, and it does not block the other destinations. Requests go out through the same server-side-request-forgery guard the ingestion pullers use: the destination is resolved and its address pinned. A URL resolving to a private or link-local address is refused and recorded as a failed delivery whenBLOCK_LOCAL_HTTP_CALLS is on, which it is on LangWatch Cloud. Self-hosted, that refusal only applies if you set the flag, and a host listed in ALLOWED_PROXY_HOSTS is allowed either way.
The cloud metadata hostnames and addresses themselves are refused regardless of either setting and cannot be allow-listed. An allow-listed name is trusted as written, without a lookup of where it resolves, so only allow-list names you control.
Where an existing rule’s alerts go
In the app: no screen lists them. Each fire is recorded as one alert keyed by(ruleId, triggerWindowStart), so the detector does not raise a second alert for the same window, but the governance section has no page that reads those rows today.
Over a webhook: this is the one place an alert reaches you. Every fire is POSTed to each webhook destination on the rule, with the payload above. A rule with no destinations records the alert and sends no request.
Not in the OCSF export. The OCSF export carries governance events, and no exported record is linked to an anomaly alert. Do not build a SIEM rule that waits for one. Point a webhook destination at your SIEM instead.
CLI: langwatch governance status reports hasAnomalyRules: true once at least one rule exists. That confirms a rule is defined, not that one has fired.
End to end, for an organization that already has a rule:
- Start from a
spend_spikerule scoped to your test IngestionSource withminBaselineUsd: 0.001(tiny floor) andratioVsBaseline: 1.5. - Send one OTel event with
gen_ai.usage.cost_usd: 0.05(baseline seed), see otel-generic, Test it now. - Wait long enough for the baseline window to roll past the seed (default
windowSec: 86400means 24h, so use the tighter values from the table above for fast iteration). - Send a second event with
gen_ai.usage.cost_usd: 2.00(the spike). - Watch your webhook receiver for the payload above. If the rule has no webhook destination, the alert is recorded and no request is sent.
What’s persisted vs what’s evaluated
A rule that is persisted but unevaluated is intentional: a rule can wait for the detector slice that will read it, so there is no rework when that slice ships. But it’s a real failure mode if you don’t know which is which. The two coverage tables above are the source of truth: onlyspend_spike + (organization | source_type | source) actually fires today. Everything else is preview state.