﻿---
title: ES|QL rules
description: Create detection rules using Elasticsearch Query Language (ESQL) with aggregation and pipeline processing.
url: https://www.elastic.co/elastic/docs-builder/docs/2999/solutions/security/detect-and-alert/esql
products:
  - Elastic Cloud Serverless
  - Elastic Security
applies_to:
  - Serverless Security projects: Generally available
  - Elastic Stack: Generally available
---

# ES|QL rules
ES|QL rules use [Elasticsearch Query Language (ES|QL)](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/2999/reference/query-languages/esql) to query source events and aggregate or transform data using a pipeline syntax. Query results are returned as a table where each row becomes an alert. ES|QL rules combine the flexibility of a full query pipeline with the detection capabilities of Elastic Security.

### When to use an ES|QL rule

ES|QL rules are the right fit when:
- You need **aggregation, transformation, or enrichment** within the query itself, such as computing statistics, renaming fields, or filtering on calculated values.
- The detection logic requires **pipe-based processing** that KQL and EQL cannot express, such as `STATS...BY` followed by `WHERE` to filter aggregated results.
- You want to create **new computed fields** (using `EVAL`) and alert on values derived from source data rather than raw field values.

ES|QL rules are **not** the best fit when:
- A field-value match is sufficient. Use a [custom query rule](https://www.elastic.co/elastic/docs-builder/docs/2999/solutions/security/detect-and-alert/custom-query) instead.
- You need to detect ordered event sequences. Use an [EQL rule](https://www.elastic.co/elastic/docs-builder/docs/2999/solutions/security/detect-and-alert/eql) instead.
- You want anomaly detection without explicit query logic. Use a [machine learning rule](https://www.elastic.co/elastic/docs-builder/docs/2999/solutions/security/detect-and-alert/machine-learning) instead.


### Data requirements

ES|QL rules query Elasticsearch indices directly using the `FROM` command. The indices must be accessible to the user who creates or last edits the rule.

## Annotated examples

The following examples use the [detections API](https://www.elastic.co/elastic/docs-builder/docs/2999/solutions/security/detect-and-alert/using-the-api) request format to show how ES|QL rules are defined. Each example is followed by a breakdown of the ES|QL-specific fields. For common fields like `name`, `severity`, and `interval`, refer to the [detections API documentation](https://www.elastic.co/docs/api/doc/kibana/group/endpoint-detection-engine-rules-api).

### Aggregating query

This rule counts failed login attempts per user and alerts when any user exceeds 20 failures within the query window.
```json
{
  "type": "esql",
  "language": "esql",
  "name": "High failed login count per user",
  "description": "Detects users with more than 20 failed login attempts in the query window.",
  "query": "FROM logs-* | WHERE event.category == \"authentication\" AND event.outcome == \"failure\" | STATS failed_count = COUNT(*) BY user.name | WHERE failed_count > 20",
  "severity": "high",
  "risk_score": 73,
  "interval": "5m",
  "from": "now-6m"
}
```


| Field               | Value                                                       | Purpose                                                                                                                                                                                                                                                                               |
|---------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` / `language` | `"esql"` / `"esql"`                                         | Both must be `"esql"`.                                                                                                                                                                                                                                                                |
| `query`             | `FROM logs-* \| WHERE ... \| STATS ... BY ... \| WHERE ...` | An aggregating query pipeline. `FROM` specifies the source indices. `STATS...BY` groups failed logins by `user.name` and counts them. The final `WHERE` filters to users exceeding 20 failures. Each result row becomes an alert containing only the `BY` fields and computed values. |

<note>
  ES|QL rules don't use a separate `index` field. Source indices are specified in the `FROM` command within the query.
</note>


### Non-aggregating query with deduplication

This rule detects process-start events with suspicious encoded arguments and uses `METADATA` to enable alert deduplication across rule executions.
```json
{
  "type": "esql",
  "language": "esql",
  "name": "Process execution with encoded arguments",
  "description": "Detects process start events where the command line contains encoded content.",
  "query": "FROM logs-endpoint.events.* METADATA _id, _index, _version | WHERE event.category == \"process\" AND event.type == \"start\" AND process.command_line LIKE \"*-encoded*\" | LIMIT 100",
  "severity": "medium",
  "risk_score": 47,
  "interval": "5m",
  "from": "now-6m"
}
```


| Field   | Value                                                               | Purpose                                                                                                                                                                                                                                      |
|---------|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | `FROM ... METADATA _id, _index, _version \| WHERE ... \| LIMIT 100` | A non-aggregating query. `METADATA _id, _index, _version` after `FROM` enables alert deduplication so the same source event does not generate duplicate alerts across rule executions. Without it, repeated matches produce repeated alerts. |
| `LIMIT` | `100`                                                               | Caps the number of results per execution. Interacts with the **Max alerts per run** setting, and the rule uses the lower of the two values.                                                                                                  |


## ES|QL rule field reference

The following settings appear in the **Define rule** section when creating an ES|QL rule. For settings shared across all rule types, refer to [Rule settings reference](https://www.elastic.co/elastic/docs-builder/docs/2999/solutions/security/detect-and-alert/common-rule-settings).
<definitions>
  <definition term="ES|QL query">
    The [ES|QL query](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/2999/reference/query-languages/esql) that defines the detection logic. Can be aggregating (with `STATS...BY`) or non-aggregating. Each row in the query result becomes an alert.
  </definition>
  <definition term="Suppress alerts by (optional)">
    Reduce repeated or duplicate alerts by grouping them on one or more fields. For details, refer to [Alert suppression](https://www.elastic.co/elastic/docs-builder/docs/2999/solutions/security/detect-and-alert/alert-suppression).
  </definition>
</definitions>