Skip to Content
How It WorksAnomaly Detection

Anomaly Detection

Decision logic

The detection engine runs once per hour. For each beet, it:

  1. Counts events received in the current hour (from HH:00:00 to HH:59:59 UTC)
  2. Fetches the baseline for this hour × day-of-week from beet_baselines_hourly
  3. Compares the actual count to the expected range

Severity thresholds

SeverityCondition
HealthyCount within the expected range
WarningCount below avg - 1.5 × stddev
CriticalCount below avg - 2.5 × stddev, or zero events when average > 0

The multipliers (1.5 and 2.5) are chosen to produce a low false-positive rate in practice. They are not currently user-configurable (custom thresholds are a Pro+ roadmap item).

Cold-start handling

If a beet has never received any events in the current hour historically, Heartbeet cannot compute a meaningful expected range. Two sub-cases:

  • Learning period not complete (< 14 days): no alert fires
  • Zero historical events, zero actual events: treated as healthy (absence is expected)
  • Zero historical events, actual events present: baseline is seeded from current data and no alert fires

Complete silence

If a beet expected events (avg > 0) and received zero this hour, the severity is always Critical — regardless of standard deviation. Silence is always a signal.

Alert lifecycle

┌─────────────────────────────────┐ Healthy ───────▶│ count < warning threshold │──▶ Alert opened (warning) └─────────────────────────────────┘ ┌─────────────────────────────────┐ │ count < critical threshold │──▶ Severity escalated (critical) └─────────────────────────────────┘ ┌─────────────────────────────────┐ │ count recovers to normal range │──▶ Alert resolved └─────────────────────────────────┘

An alert is represented as a row in anomaly_alerts with a status of open or resolved.

What triggers resolution

An alert is resolved when the current hour’s event count returns within the expected range. Heartbeet resolves automatically — you do not need to acknowledge or close alerts manually.

When an alert resolves, a resolved notification is sent via all configured alert channels.