---
title: "Anomaly Detection"
description: "PgBeam learns each credential's normal behavior and alerts when it drifts. Volume spikes, off-hours access, new query shapes, and error or egress spikes."
canonical: "https://pgbeam.com/docs/anomaly-detection"
last-updated: "2026-10-29T19:01:20.000Z"
---

# Anomaly Detection

> PgBeam learns each credential's normal behavior and alerts when it drifts. Volume spikes, off-hours access, new query shapes, and error or egress spikes.

URL: https://pgbeam.com/docs/anomaly-detection

Anomaly detection watches each credential's traffic and raises an alert when it drifts from its own baseline. A bot that quietly read four tables for a month and suddenly scans a fifth, runs at 3am, or pulls ten times its usual rows is doing something worth a look. PgBeam flags it with no thresholds to write up front; [custom rules](#custom-rules) let you retune a metric when the defaults do not fit a credential.

It runs on agent credentials and human credentials alike. The baseline is per-credential, so a noisy analytics agent and a quiet support bot each get judged against their own history, not a shared average.

## What it watches

| Signal           | What trips it                                                                                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Volume spike     | Query count or rate well above the credential's recent baseline.                                                                                                         |
| Off-hours access | Activity outside the hours this credential normally runs.                                                                                                                |
| New query shape  | A normalized statement shape never seen from this credential before.                                                                                                     |
| Error spike      | A burst of blocked or failing statements (often probing or a loop).                                                                                                      |
| Egress spike     | Bytes returned well above baseline (a possible bulk exfiltration).                                                                                                       |
| Honeytoken trip  | A statement referenced a [honeytoken](/docs/honeytokens). Raised on ingest, not from a baseline.                                                                         |
| Content flagged  | The [result-content scan](/docs/webhook-events#content_flagged) found high-confidence instruction-shaped text in returned values. Raised on ingest, not from a baseline. |

Each alert carries a machine-readable `kind`: `volume_spike`, `off_hours`, `new_query_shape`, `error_spike`, `egress_spike`, `canary_tripped`, or `content_flagged`. The last two fire the moment the audit entry arrives, so a brand new credential with no history can still raise them, and custom rules do not change them.

Volume, egress and new-shape detection count every statement that ran on your database, including ones whose result was masked, capped, auto-approved, or withheld by content scanning (`content_scan_mode=block`, see [audit log](/docs/audit-log)). The error count includes every refusal the agent saw: policy blocks, exhausted budgets, failed logins, honeytoken trips, rejected or expired approvals, and withheld results.

A "query shape" is the statement with its literals stripped, so `SELECT * FROM orders WHERE id = 1` and `... WHERE id = 2` are the same shape. A genuinely new shape means the credential is doing something it has not done before.

## Alerts

Each detection becomes an anomaly alert with the signal, the credential, the window, and the statements that triggered it. Review them on the **Anomalies** tab, or pull them from the API:

```bash title="List open anomaly alerts"
curl "https://api.pgbeam.com/v1/projects/{projectId}/anomalies?status=open" \
  -H "Authorization: Bearer $PGBEAM_TOKEN"
```

Acknowledge or resolve an alert once you have looked at it:

```bash title="Acknowledge an alert"
curl -X PATCH \
  "https://api.pgbeam.com/v1/projects/{projectId}/anomalies/{alertId}" \
  -H "Authorization: Bearer $PGBEAM_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"status": "acknowledged"}'
```

The CLI does the same with [`pgbeam anomalies ack`](/docs/cli/anomalies/ack): pass one or more alert ids, or `--all` to acknowledge every open alert in the project. [`pgbeam anomalies resolve`](/docs/cli/anomalies/resolve) closes them out, and [`pgbeam anomalies list`](/docs/cli/anomalies/list) shows what is open.

Every alert can also fire an [`anomaly_alert`](/docs/audit-export) webhook, so it lands in Slack, PagerDuty, or your SIEM the moment it is raised.

## Custom rules

The baseline signals use deployment defaults until you add a rule. A rule retunes one metric for the whole project, or for one credential in it, and sets:

* `metric`: one of `queries_per_hour`, `bytes_per_hour`, `distinct_shapes`, `errors_per_hour`, or `active_hours`.
* `credential_id`: the credential to scope it to. Omit it to apply the rule to every credential in the project.
* `sigma_threshold`: N in the "mean + N × dispersion" spike rule. Higher is quieter.
* `floor`: an absolute value below which the metric never alerts.
* `enabled`: `false` silences the metric for that scope. The baseline keeps advancing, so turning it back on resumes from existing history.

`sigma_threshold` and `floor` apply only to the rate metrics (`queries_per_hour`, `bytes_per_hour`, `errors_per_hour`); leave either out to keep the default. One rule exists per project, credential and metric. A rule adds no new alert kind.

```bash title="Loosen egress detection for a bulk-export agent"
pgbeam anomalies rules create bytes_per_hour \
  --credential-id agt_01j9x8y7z6w5v4u3t2s1r0q9p8 \
  --sigma-threshold 6 --floor 50000000
```

Manage rules with [`pgbeam anomalies rules`](/docs/cli/anomalies/rules/list), the API, or the [`pgbeam_anomaly_rule`](/docs/terraform) Terraform resource.

## From alert to action

Anomaly detection tells you something changed; the rest of the policy engine lets you respond.

* A volume or egress spike on a credential that should be quiet: trip the [kill-switch](/docs/kill-switch) and investigate.
* A new query shape that should never happen: tighten the [allowlist](/docs/allowlists) or add a [row filter](/docs/row-level-policies).
* A run of blocked statements: read the [audit log](/docs/audit-log) to see what the agent kept trying, then adjust its policy.

> **Baselines need history**
>
> A new credential has no baseline yet, so its first hours of traffic establish
> one rather than triggering alerts. Detection sharpens as a credential builds a
> track record.

## Related

* [Audit log](/docs/audit-log): the per-statement record alerts are built from.
* [Audit export](/docs/audit-export): forward `anomaly_alert` events to a SIEM.
* [Kill-switch](/docs/kill-switch): stop a credential the moment something looks wrong.
* [Budgets](/docs/budgets): hard ceilings that complement baseline detection.