# Alert analytics

`GET https://evohub.io/api/v1/alerts/analytics`

Part of the [On-Call API](https://docs-dev.evohub.io/oncall.md) reference · operationId `getAlertAnalytics`.

Aggregates over the organization's alerts in a time range: MTTA and MTTR
(p50, p90, average), counts per severity and per integration, a time series
(hourly up to 48 hours, daily beyond), noise (flapping alerts, alerts resolved
without anyone acknowledging them, the noisiest fingerprints), an alerts-per-
hour heatmap, notification totals and the load on each responder.

The heatmap and off-hours counts use `tz`, else the timezone most of the
organization's schedules use, else UTC; `range.tz_source` says which.

**Permission (API-key scope):** `oncall:alert:read`.

## Authorization

Any one of:

- `bearerAuth` (oncall:alert:read)
- `apiKeyHeader` (oncall:alert:read)

Where:

- `bearerAuth`: HTTP Bearer — An EvoHub API key (`evohub_…`) as a bearer token.
- `apiKeyHeader`: API key in the header `X-API-Key` — An EvoHub API key (`evohub_…`).

## Query parameters

- `from` (string, example `2026-09-01`): Start of the range: a date (`YYYY-MM-DD`, start of that UTC day) or an RFC 3339 instant. Give `from` and `to` together, or neither for the last 30 UTC days including today.
- `to` (string, example `2026-09-30`): End of the range: a date (the last UTC day included) or an RFC 3339 instant (exclusive). At most 366 days after `from`.
- `severity` (string, example `critical,high`): Comma-separated severities to include.
- `compare` (string, one of `previous`, `year`): Also compute the totals of a comparison period: `previous` (the same length immediately before) or `year` (the same dates a year earlier).
- `team_id` (string): Only this team's alerts.
- `integration_id` (string): Comma-separated integration ids.
- `source` (string): Comma-separated integration kinds, for example `prometheus,grafana`.
- `tz` (string, example `Europe/Istanbul`): IANA timezone for the heatmap and off-hours counts.
- `ack_target_seconds` (integer, default `300`, min 1, max 86400): The acknowledgement target `acked_within_target` counts against, in seconds (1–86400).
- `flap_window_minutes` (integer, default `30`, min 1, max 1440): An alert re-opening within this many minutes of being resolved counts as flapping (1–1440).

## Responses

### 200 — The aggregates.

Content type: `application/json`

Type: `object`

- `data` (AlertAnalytics, required)
  - `range` (AnalyticsRange)
    - `from` (string (date-time))
    - `to` (string (date-time)): Exclusive.
    - `granularity` (string, one of `hour`, `day`)
    - `tz` (string)
    - `tz_source` (string, one of `param`, `schedules`, `default`)
  - `ack_target_seconds` (integer)
  - `totals` (AlertGroupStats)
    - `alerts` (integer)
    - `acknowledged` (integer)
    - `resolved` (integer)
    - `auto_resolved` (integer)
    - `resolved_without_ack` (integer)
    - `escalated` (integer)
    - `retriggers` (integer)
    - `flapping` (integer)
    - `suppressed` (integer)
    - `closed_before_notify` (integer)
    - `acked_within_target` (integer)
    - `mtta` (DurationStats)
    - `mttr` (DurationStats)
  - `series` (array of AlertGroupStats & object)
    - `alerts` (integer)
    - `acknowledged` (integer)
    - `resolved` (integer)
    - `auto_resolved` (integer)
    - `resolved_without_ack` (integer)
    - `escalated` (integer)
    - `retriggers` (integer)
    - `flapping` (integer)
    - `suppressed` (integer)
    - `closed_before_notify` (integer)
    - `acked_within_target` (integer)
    - `mtta` (DurationStats)
      - `count` (integer): How many were measured.
      - `p50_seconds` (number | null)
      - `p90_seconds` (number | null)
      - `avg_seconds` (number | null)
    - `mttr` (DurationStats)
      - `count` (integer): How many were measured.
      - `p50_seconds` (number | null)
      - `p90_seconds` (number | null)
      - `avg_seconds` (number | null)
    - `bucket` (string (date-time))
    - `by_severity` (object)
      - Other keys: integer
  - `by_severity` (array of AlertGroupStats & object)
    - `alerts` (integer)
    - `acknowledged` (integer)
    - `resolved` (integer)
    - `auto_resolved` (integer)
    - `resolved_without_ack` (integer)
    - `escalated` (integer)
    - `retriggers` (integer)
    - `flapping` (integer)
    - `suppressed` (integer)
    - `closed_before_notify` (integer)
    - `acked_within_target` (integer)
    - `mtta` (DurationStats)
      - `count` (integer): How many were measured.
      - `p50_seconds` (number | null)
      - `p90_seconds` (number | null)
      - `avg_seconds` (number | null)
    - `mttr` (DurationStats)
      - `count` (integer): How many were measured.
      - `p50_seconds` (number | null)
      - `p90_seconds` (number | null)
      - `avg_seconds` (number | null)
    - `severity` (string)
  - `by_integration` (array of AlertIntegrationStats)
    - `alerts` (integer)
    - `acknowledged` (integer)
    - `resolved` (integer)
    - `auto_resolved` (integer)
    - `resolved_without_ack` (integer)
    - `escalated` (integer)
    - `retriggers` (integer)
    - `flapping` (integer)
    - `suppressed` (integer)
    - `closed_before_notify` (integer)
    - `acked_within_target` (integer)
    - `mtta` (DurationStats)
    - `mttr` (DurationStats)
    - `integration_id` (string): Empty for alerts raised by hand or by another EvoHub product.
    - `integration_name` (string)
    - `integration_type` (string)
  - `noise` (object)
    - `flap_window_minutes` (integer)
    - `flapping` (integer)
    - `resolved_without_ack` (integer)
    - `top_integrations` (array of AlertIntegrationStats)
      - `alerts` (integer)
      - `acknowledged` (integer)
      - `resolved` (integer)
      - `auto_resolved` (integer)
      - `resolved_without_ack` (integer)
      - `escalated` (integer)
      - `retriggers` (integer)
      - `flapping` (integer)
      - `suppressed` (integer)
      - `closed_before_notify` (integer)
      - `acked_within_target` (integer)
      - `mtta` (DurationStats)
      - `mttr` (DurationStats)
      - `integration_id` (string): Empty for alerts raised by hand or by another EvoHub product.
      - `integration_name` (string)
      - `integration_type` (string)
    - `top_fingerprints` (array of object)
      - `fingerprint` (string)
      - `title` (string)
      - `integration_id` (string)
      - `alerts` (integer)
      - `resolved_without_ack` (integer)
      - `retriggers` (integer)
      - `flapping` (integer)
  - `heatmap` (array of object)
    - `weekday` (integer, min 1, max 7): ISO weekday: 1 Monday … 7 Sunday.
    - `hour` (integer, min 0, max 23)
    - `alerts` (integer)
  - `notifications` (object)
    - `total` (integer)
    - `voice` (integer)
    - `sms` (integer)
    - `email` (integer)
    - `push` (integer)
    - `calls_unanswered` (integer)
    - `suppressed` (integer)
  - `responders` (array of object)
    - `user_id` (string)
    - `notifications` (integer)
    - `voice` (integer)
    - `sms` (integer)
    - `email` (integer)
    - `push` (integer)
    - `calls_unanswered` (integer)
    - `alerts_notified` (integer)
    - `interruptions` (integer)
    - `off_hours_interruptions` (integer)
    - `sleep_hours_interruptions` (integer)
    - `acknowledged` (integer)
    - `resolved` (integer)
    - `suppressed` (integer): Notifications held back by maintenance or the person's notification schedule.
  - `compare` (AlertGroupStats & object | null)
    - One of:
      - AlertGroupStats & object
        - `alerts` (integer)
        - `acknowledged` (integer)
        - `resolved` (integer)
        - `auto_resolved` (integer)
        - `resolved_without_ack` (integer)
        - `escalated` (integer)
        - `retriggers` (integer)
        - `flapping` (integer)
        - `suppressed` (integer)
        - `closed_before_notify` (integer)
        - `acked_within_target` (integer)
        - `mtta` (DurationStats)
          - `count` (integer): How many were measured.
          - `p50_seconds` (number | null)
          - `p90_seconds` (number | null)
          - `avg_seconds` (number | null)
        - `mttr` (DurationStats)
          - `count` (integer): How many were measured.
          - `p50_seconds` (number | null)
          - `p90_seconds` (number | null)
          - `avg_seconds` (number | null)
        - `mode` (string)
        - `from` (string (date-time))
        - `to` (string (date-time))
      - null
- `success` (boolean, required, value `true`)

### 400 — A parameter is invalid; each problem is listed in `details` (`VALIDATION_FAILED`).

Content type: `application/json`

Type: `ValidationError`

- `error` (object, required)
  - `code` (string, required, value `VALIDATION_FAILED`)
  - `message` (string, required)
  - `details` (array of object, required)
    - `field` (string)
    - `message` (string)

### 401 — No API key was sent, or it is unknown, revoked or expired (`UNAUTHORIZED`).

Content type: `application/json`

Type: `Error`

- `error` (object, required)
  - `code` (string, required): Machine-readable code. Branch on this.
  - `message` (string, required): Human-readable explanation.

### 403 — The key lacks the scope this endpoint needs (`FORBIDDEN`).

Content type: `application/json`

Type: `Error`

- `error` (object, required)
  - `code` (string, required): Machine-readable code. Branch on this.
  - `message` (string, required): Human-readable explanation.

### 429 — Too many requests (`RATE_LIMITED`). Wait `Retry-After` seconds.

Headers:

- `Retry-After` (integer): Seconds to wait.

Content type: `application/json`

Type: `Error`

- `error` (object, required)
  - `code` (string, required): Machine-readable code. Branch on this.
  - `message` (string, required): Human-readable explanation.

### 500 — Something went wrong on EvoHub's side (`INTERNAL_ERROR`). Retry later.

Content type: `application/json`

Type: `Error`

- `error` (object, required)
  - `code` (string, required): Machine-readable code. Branch on this.
  - `message` (string, required): Human-readable explanation.

## Example request

```bash
curl -X GET 'https://evohub.io/api/v1/alerts/analytics' \
  -H 'Authorization: Bearer <TOKEN>'
```
