APIAnomaly

Get finding

Fetch one finding.

GET
/anomaly/findings/{id}

Fetch one finding.

Authorization

ApiKeyAuth
headerAuthorizationBearer <token>

Bearer token authentication. Send an API key (a saai_api_-prefixed token) as Authorization: Bearer <token>.

Required permission

view_anomaly_detection

Path Parameters

id*string
Formatuuid

Response Body

The request has succeeded.

application/json
  1. response

One triageable anomaly episode.

id*uuid
Formatuuid
detector*AnomalyDetector

Detector that produced a finding: what kind of unusual activity was flagged.

Value in"llm_cost_spike""mcp_volume_spike""mcp_error_rate""mcp_blocked_spike""new_tool_for_user""new_mcp_for_user""new_model_for_user""off_hours_activity"
family*AnomalyDetectorFamily

Statistical family of a detector — which evidence fields a finding carries.

Value in"baseline""threshold""off_hours""novelty"
grain*AnomalyGrain

The time resolution a finding is bucketed at.

Value in"hour""day"
metric*string

The measured quantity that was scored, e.g. llm.cost_usd or mcp.tool_calls.

dimension*string

The specific thing the finding is about within the subject, e.g. the tool, MCP, or model in question.

dimension_display*string

Human-readable dimension — ids resolved to their names; equals dimension when nothing resolves.

subject_type*AnomalySubjectType

What a finding is about: a person or a running agent.

Value in"user""agent_instance"
subject_id*string

Id of the flagged user or agent instance.

subject_display_name*string

Human-readable name of the subject.

bucket*string

The time bucket where the anomaly was first observed.

Formatdate-time
last_bucket*string

The most recent bucket in the episode; the triage queue and time filters key off this.

Formatdate-time
severity*AnomalySeverity

How much attention a finding needs, from lowest to highest priority.

Value in"low""medium""high""critical"
status*AnomalyFindingStatus

Where a finding sits in the triage workflow.

Value in"open""acknowledged""dismissed""resolved"
observed*number

The flagged metric value, in metric's unit.

Formatdouble
evidence*

Typed "why" behind a finding — never a bare verdict. Every group is optional; a family carries only the groups it computes.

created_at*string
Formatdate-time
updated_at*string
Formatdate-time
status_changed_at?string

When the finding was last triaged; absent while still open.

Formatdate-time
curl -X GET "https://example.com/anomaly/findings/497f6eca-6276-4993-bfeb-53cbbbba6f08"
{  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "detector": "llm_cost_spike",  "family": "baseline",  "grain": "hour",  "metric": "string",  "dimension": "string",  "dimension_display": "string",  "subject_type": "user",  "subject_id": "string",  "subject_display_name": "string",  "bucket": "2019-08-24T14:15:22Z",  "last_bucket": "2019-08-24T14:15:22Z",  "severity": "low",  "status": "open",  "observed": 0.1,  "evidence": {    "baseline": {      "window_days": 0,      "n": 0,      "mean": 0.1,      "stddev": 0.1,      "median": 0.1,      "mad": 0.1,      "mean_ad": 0.1    },    "scoring": {      "robust_score": 0.1,      "z_score": 0.1,      "dispersion_basis": "mad",      "threshold": 0.1,      "floor": 0.1,      "abs_delta": 0.1,      "rel_factor": 0.1    },    "peer": {      "rank": 0.1,      "n": 0    },    "slice": {      "hour": 0,      "dow_class": "string",      "fell_back": true    },    "prior": {      "max": 0.1,      "p90": 0.1,      "exceeded": true    },    "novelty": {      "keys": [        "string"      ],      "keys_display": [        "string"      ],      "new_for_org": true,      "dormancy_days": 0    },    "threshold": {      "floor": 0.1    },    "off_hours": {      "min_calls": 0.1,      "history_days": 0    }  },  "created_at": "2019-08-24T14:15:22Z",  "updated_at": "2019-08-24T14:15:22Z",  "status_changed_at": "2019-08-24T14:15:22Z"}