﻿---
title: Query Kibana Universal Alerting alert history in Discover
description: Use ES|QL in Discover to replay incidents, audit triage actions, and measure response times for alerts.
url: https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8924/explore-analyze/alerting/esql/alerts/query-alerts-and-signals-in-discover
products:
  - Elastic Documentation
  - Kibana
applies_to:
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Experimental in 9.5
---

# Query Kibana Universal Alerting alert history in Discover
In Kibana Universal Alerting, use ES|QL in Discover to replay how an incident unfolded, see who acknowledged or snoozed an alert, measure time-to-acknowledge trends, or correlate alert history with other data in your environment. To check the current state of alerts instead, use the [**Alerts** page](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8924/explore-analyze/alerting/esql/alerts/view-and-manage-alerts).
For events with `type: signal`, including basic queries and using them as input to a rule that opens an alert, refer to [Query rule events](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8924/explore-analyze/alerting/esql/alerts/query-signals).
Use the following table to jump to the query you need:

| Query                                                              | What it returns                                                            | Stream           |
|--------------------------------------------------------------------|----------------------------------------------------------------------------|------------------|
| [Reconstruct the lifecycle of a specific alert](#replay-episode)   | Every evaluation for one alert, in chronological order                     | `.rule-events`   |
| [Find all currently active alerts](#find-active-episodes)          | One row per alert currently in `active` state                              | `.rule-events`   |
| [List all breaches for a specific rule](#list-rule-breaches)       | Every evaluation where a rule's condition was met                          | `.rule-events`   |
| [Identify evaluation gaps](#identify-no-data)                      | Recent `no_data` rows that may point to a pipeline issue                   | `.rule-events`   |
| [View the full triage history for an alert](#full-triage-history)  | Every action taken on one alert, in chronological order                    | `.alert-actions` |
| [Find all acknowledgments in a time window](#find-acknowledgments) | Every acknowledgment action across all alerts                              | `.alert-actions` |
| [Check alert assignment state](#check-assignments)                 | Every assignment action and who it was assigned to                         | `.alert-actions` |
| [Audit dispatcher outcomes for a rule](#audit-dispatcher-outcomes) | Notified, suppressed, and unmatched outcomes for a rule                    | `.alert-actions` |
| [Find snoozed series](#find-snoozed-series)                        | Active and historical snoozes, including who set them and when they expire | `.alert-actions` |
| [Trace the full story of an incident](#trace-incident)             | Both streams filtered or joined together for a complete incident timeline  | Both             |


## Before you begin

Before you can query alert history in Discover, add the alert data streams as data views. Repeat these steps for each stream.
1. Open **Discover**, and then open the data view menu.
2. Select **Create a data view**.
3. Give your data view a name, for example `.rule-events` or `.alert-actions`.
4. In the **Index pattern** field, enter the data stream name:
   - `.ds-.rule-events-*` for rule evaluation history.
- `.ds-.alert-actions-*` for triage actions recorded on alerts.
5. Open the **Timestamp field** dropdown and select `@timestamp`.
6. Select **Save data view to Kibana**.

For more details on data view options, refer to [Data views](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8924/explore-analyze/find-and-organize/data-views).

## Query alert history

Kibana writes one [rule event](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8924/explore-analyze/alerting/esql/rules/rule-event-field-reference) to `.rule-events` for each matching row of a scheduled rule run. Kibana never overwrites these events, which means you can reconstruct the full history of any alert by querying all events that share the same `episode.id`. The following sections provide example queries for common scenarios.

### Reconstruct the lifecycle of a specific alert

Use the alert's `episode.id` to pull all of its evaluations in chronological order. This shows exactly how that one alert moved through its lifecycle states from open to close, without mixing in other alerts from the same series.
```esql
FROM .rule-events
// Scope to a single alert by its ID
| WHERE episode.id == "<alert-id>"
// Sort oldest-first to read the progression forward in time
| SORT @timestamp ASC
// Keep the fields most relevant to reading the lifecycle sequence
| KEEP @timestamp, status, episode.id, episode.status, episode.status_count
```

To pull evaluations across every alert in a series instead, filter by `group_hash` in place of `episode.id`.

### Find all currently active alerts

Returns one row for each alert currently in `active` state, along with the timestamp of its most recent evaluation.
```esql
FROM .rule-events
// Only include rows where the alert lifecycle state is active
| WHERE episode.status == "active"
// Deduplicate to one row per alert, showing the most recent evaluation
| STATS latest = MAX(@timestamp) BY episode.id, group_hash
```


### List all breaches for a specific rule

Returns every evaluation row where the rule met its condition for a given rule. Use this to understand how often and how severely a rule fires across all its series.
```esql
FROM .rule-events
// Filter to one rule and only rows where the condition was met
| WHERE rule.id == "my-rule-id" AND status == "breached"
// Return fields that identify severity, series context, and the rule-defined payload
| KEEP @timestamp, group_hash, severity, episode.id, episode.status, data
```


### Identify evaluation gaps

`no_data` rows appear when a rule with [no-data handling](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8924/explore-analyze/alerting/esql/rules/configure-no-data-handling) configured finds no matching data during an evaluation cycle. Events with `type: signal` don't include `no_data`. A cluster of these rows can indicate a data pipeline issue or a misconfigured rule.
```esql
FROM .rule-events
// no_data means the rule ran but found nothing to evaluate against
| WHERE status == "no_data"
| SORT @timestamp DESC
| LIMIT 50
```


## Query triage and action history

Kibana writes one document to `.alert-actions` for every action a user or the system takes on an alert. Use it to audit who did what, measure acknowledgment response times, or check current snooze and assignment state. The following sections provide example queries for common scenarios.
<applies-to>Elastic Stack: Experimental in 9.5</applies-to> In Elastic Stack 9.5, `.alert-actions` stores the alert ID and status as `episode_id` and `episode_status`. Replace `alert_id` and `alert_status` with those names in the following queries.

### View the full triage history for an alert

Returns all actions recorded for a single alert in chronological order. Use this to see the complete response sequence: who acknowledged it, whether a user snoozed it, and how it was eventually resolved. The **Resolve** action writes `deactivate`, and **Unresolve** writes `activate`.
```esql
FROM .alert-actions
// Scope to a single alert by its ID
| WHERE alert_id == "<alert-id>"
// Sort oldest-first to read the response sequence forward in time
| SORT @timestamp ASC
// Keep the fields most relevant to understanding what happened and who did it
| KEEP @timestamp, action_type, actor, alert_status, reason
```


### Find all acknowledgments in a time window

Returns every acknowledgment action across all alerts. Useful for tracking team response activity or measuring time-to-acknowledge trends.
```esql
FROM .alert-actions
// Filter to acknowledgment actions only
| WHERE action_type == "ack"
| SORT @timestamp DESC
```


### Check alert assignment state

Returns all assign actions, showing which alerts carry an assignment and to whom. Use this to audit ownership or find unacknowledged handoffs.
```esql
FROM .alert-actions
// Filter to assignment actions only
| WHERE action_type == "assign"
// Return the fields that identify the alert, who assigned it, and the target user
| KEEP @timestamp, alert_id, actor, assignee_uid
```


### Audit dispatcher outcomes for a rule

Returns all dispatcher decisions for a rule, covering alerts that were notified, suppressed due to throttling, or didn't match to any action policy.
```esql
FROM .alert-actions
// Filter to one rule and only the three dispatcher outcome action types
| WHERE rule_id == "my-rule-id" AND action_type IN ("notified", "suppress", "unmatched")
| SORT @timestamp DESC
```


### Find snoozed series

Returns all snooze actions, including which series a user snoozed, who snoozed it, and when the snooze expires. Use this to find active snoozes or audit snooze history.
```esql
FROM .alert-actions
// Filter to snooze actions only
| WHERE action_type == "snooze"
// Return the fields needed to identify the series, the actor, and the expiry time
| KEEP @timestamp, group_hash, alert_id, expiry, actor
```


## Trace the full story of an incident

To get the complete picture of an incident, filter both streams by the same identifier. Both streams share `group_hash` as a flat keyword, making it the most reliable join key. `episode.id` in `.rule-events` and `alert_id` in `.alert-actions` hold the same value but use different naming conventions: dot-notation in `.rule-events` and flat snake_case in `.alert-actions`.
<note>
  Filter by `alert_id` to return user actions (`ack`, `assign`, `deactivate`, and similar) and notifications for one alert. Dispatcher-level entries might be missing because system-written action types (`fire`, `suppress`, `unmatched`, `notified`) key to `group_hash` and might not carry an `alert_id`. Filter by `group_hash` to include the complete dispatcher history.
</note>

1. Run a `.rule-events` query to find the `episode.id` or `group_hash` you care about.
2. Query `.alert-actions` using that value:

```esql
FROM .alert-actions
// Use group_hash to include dispatcher actions that may not carry an alert_id
| WHERE group_hash == "<group-hash>"
| SORT @timestamp ASC
| KEEP @timestamp, action_type, actor, alert_id, reason
```

If you need to join both streams in a single query, use `LOOKUP JOIN`. This requires configuring `.alert-actions` as a lookup index, which is an extra setup step beyond standard Discover analysis:
```esql
FROM .rule-events
// Only include rows that belong to an alert (events with type: signal have no episode.id)
| WHERE episode.id IS NOT NULL
// Rename to match the join key naming convention in .alert-actions
| EVAL alert_id = episode.id
// Join with .alert-actions to surface triage actions alongside evaluation data
| LOOKUP JOIN .alert-actions ON alert_id
| KEEP @timestamp, status, action_type, actor
| SORT @timestamp DESC
```

For most exploratory analysis, running separate queries filtered by `group_hash` is simpler and avoids the `alert_id` optionality issue.