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.

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.

Use the following table to jump to the query you need:

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

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.

Kibana writes one rule event 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.

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.

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.

Returns one row for each alert currently in active state, along with the timestamp of its most recent evaluation.

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
		

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.

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
		

no_data rows appear when a rule with 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.

FROM .rule-events
// no_data means the rule ran but found nothing to evaluate against
| WHERE status == "no_data"
| SORT @timestamp DESC
| LIMIT 50
		

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.

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.

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.

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
		

Returns every acknowledgment action across all alerts. Useful for tracking team response activity or measuring time-to-acknowledge trends.

FROM .alert-actions
// Filter to acknowledgment actions only
| WHERE action_type == "ack"
| SORT @timestamp DESC
		

Returns all assign actions, showing which alerts carry an assignment and to whom. Use this to audit ownership or find unacknowledged handoffs.

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
		

Returns all dispatcher decisions for a rule, covering alerts that were notified, suppressed due to throttling, or didn't match to any action policy.

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
		

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.

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
		

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.

  1. Run a .rule-events query to find the episode.id or group_hash you care about.
  2. Query .alert-actions using that value:
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:

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.