---
type: api
title: Update a rule
description: **Spaces method and path for this operation:**
url: https://www.elastic.co/elastic/docs-builder/docs/4493/api/doc/kibana/operation/operation-patch-alerting-v2-rules-id
resource: https://www.elastic.co/elastic/docs-builder/docs/4493/api/doc/kibana/operation/operation-patch-alerting-v2-rules-id
products:
  - Kibana
---

# Update a rule

Availability: Experimental since 9.5

- Server: `https://{kibana_url}`

## Paths

- `PATCH` `/api/alerting/v2/rules/{id}`
- `PATCH` `/s/{space_id}/api/alerting/v2/rules/{id}`

#### Path parameters

- `id` (string · min length: 1 · max length: 150 · pattern: ^[a-zA-Z0-9_-]+$) — required
  The identifier for the rule. Chosen at creation and permanent — it cannot be changed afterwards. Re-using the id of a deleted resource is allowed but discouraged: execution history, change history, and alerts recorded under that id are retained and are attributed to the new resource. Ids appear in URLs and logs, so keep them free of sensitive data.

## Description

Refer to [Spaces](https://www.elastic.co/docs/deploy-manage/manage-spaces) for more information.

Apply a partial update. A field you omit keeps its stored value, and that applies at every level: send `{"metadata": {"name": "Disk pressure"}}` to change one leaf while leaving its siblings alone. Send `null` to clear a field, again at any level: `{"metadata": {"description": null}}` clears a single leaf, while `{"grouping": null}` clears a whole object. Lists and variant objects are replaced as a unit rather than merged, since a partly-sent variant could never be valid. The merged result is validated as a whole, so a patch that would leave the resource invalid is rejected with a `400` and nothing is stored.

## Required authorization

- Privileges: `manage_alerting-v2-rules`

## Authentication

- Api key: `Authorization: <value>`
  These APIs use key-based authentication. You must create an API key and use the encoded value in the request header. For example: `Authorization: ApiKey base64AccessApiKey`
- Basic: `Authorization: Basic <credentials>`

## Header parameters

- `kbn-xsrf` (string) — required
  A required header to protect against CSRF attacks

## Request body

`application/json`

- `artifacts` (object[] · min items: 1 · max items: 100) — optional — nullable
  Optional objects attached to the rule, such as a runbook or a dashboard. Each item has `id`, `type`, and `data`. The shape of `data` depends on `type`. For example, a `runbook` uses `content` and a `dashboard` uses `dashboard_id`. Known types are validated against that shape. Unknown types are stored when `id`, `type`, and `data` are present. An empty array is rejected: omit `artifacts` on create, or send `null` on PATCH to clear.
  - `data` (map string to object) — required
    Structured artifact data.
  - `id` (string · min length: 1 · max length: 150) — required
    Artifact identifier.
  - `type` (string · min length: 1 · max length: 128) — required
    Artifact type.
- `grouping` (object) — optional
  Grouping configuration.
  - `fields` (string[] · min items: 1 · max items: 16) — optional
    Fields to group alerts by, e.g. ["host.name", "service.name"]. Should match ES|QL GROUP BY fields.
- `metadata` (object) — optional
  Rule metadata.
  - `builder` (object) — optional — nullable
    Identifies the rule builder that authored this rule (e.g. "threshold"). Absent for rules authored directly in ES|QL; send `null` on PATCH to clear it.
    - `type` (string · min length: 1 · max length: 64) — required
      Rule builder type.
  - `description` (string · min length: 1 · max length: 1024) — optional — nullable
    Human-readable description of the rule. Omit to leave it unset; send `null` on PATCH to clear it. An empty string is rejected.
  - `name` (string · min length: 1 · max length: 256) — optional
    Rule name (must be unique within the space).
  - `routing_tags` (string[] · min items: 1 · max items: 20) — optional — nullable
    Routing tags that link alerts from this rule to action policies. An action policy applies when its `matcher.tags` contains at least one of these tags. Only allowed when kind is "alert".
  - `tags` (string[] · min items: 1 · max items: 20) — optional — nullable
    Tags for categorization, e.g. ["production", "infra"].
- `no_data` (union) — optional
  What the rule does when a group has no data. Required when `kind` is `alert`. Not allowed when `kind` is `signal`. Any strategy other than `ignore` requires either `query.breach` or `no_data.query`, so that a group with no data can be told apart from one that stopped breaching.
- `query` (object) — optional
  ES|QL query the rule evaluates. `base` is required. `breach` is an optional clause appended to it.
  - `base` (string · min length: 1 · max length: 10000) — optional
    ES|QL query that specifies the data to evaluate. Must include a `FROM` clause. Kibana applies the time filter from `schedule.lookback` using `time_field`.
  - `breach` (object) — optional
    Optional ES|QL clause appended to `query.base`. If omitted, every row from `query.base` is a match, and a `no_data` strategy other than `ignore` then requires `no_data.query`.
    - `segment` (string · min length: 1 · max length: 10000) — optional
      ES|QL clause appended to `query.base`, for example `WHERE avg_cpu > 0.85`. Don't include a `FROM` clause.
- `recovery` (union) — optional
  When an alert recovers. Required when `kind` is `alert`. Not allowed when `kind` is `signal`.
- `schedule` (object) — optional
  Execution schedule configuration.
  - `every` (string · max length: 32) — optional
    Execution interval, e.g. 1m, 5m, 1h.
  - `lookback` (string · max length: 32) — optional — nullable
    Lookback window for the query, e.g. 5m, 1h. Can also be expressed in ES|QL.
- `state_transition` (object) — optional
  Specifies how many consecutive matches, or how long a condition must hold, before an alert becomes `active` or `inactive`. Allowed only when `kind` is `alert`.
  - `pending` (object) — optional
    Delay before a match opens an alert.
    - `count` (integer · min: 0 · max: 1000) — optional — nullable
      Consecutive matches the alert spends in `pending` before it becomes `active` on the next match. For example, `2` opens it on the third consecutive match. Set to `0` to open it on the first match.
    - `operator` (enum) — optional — nullable
      When both `count` and `timeframe` are set, `and` requires both and `or` requires either. Allowed only when both fields are present.
      Values: `and`, `or`
    - `timeframe` (string · max length: 32) — optional — nullable
      Duration the condition must hold, for example `5m`. Combine with `count` using `operator`.
  - `recovering` (object) — optional
    Delay before a recovered match closes the alert. Has no effect when `recovery.strategy` is `manual`.
    - `count` (integer · min: 0 · max: 1000) — optional — nullable
      Consecutive recoveries the alert spends in `recovering` before it becomes `inactive` on the next recovery. For example, `2` closes it on the third consecutive recovery. Set to `0` to close it on the first recovery.
    - `operator` (enum) — optional — nullable
      When both `count` and `timeframe` are set, `and` requires both and `or` requires either. Allowed only when both fields are present.
      Values: `and`, `or`
    - `timeframe` (string · max length: 32) — optional — nullable
      Duration the condition must hold, for example `5m`. Combine with `count` using `operator`.
- `time_field` (string · min length: 1 · max length: 256) — optional
  Document field Kibana uses with `schedule.lookback` to time-filter `query.base`.

## Responses

#### `200`

Returns the updated rule.

`application/json`

- `artifacts` (object[] · min items: 1 · max items: 100)
  Optional objects attached to the rule, such as a runbook or a dashboard. Each item has `id`, `type`, and `data`. The shape of `data` depends on `type`. For example, a `runbook` uses `content` and a `dashboard` uses `dashboard_id`. Known types are validated against that shape. Unknown types are stored when `id`, `type`, and `data` are present. An empty array is rejected: omit `artifacts` on create, or send `null` on PATCH to clear.
  - `data` (map string to object)
    Structured artifact data.
  - `id` (string · min length: 1 · max length: 150)
    Artifact identifier.
  - `type` (string · min length: 1 · max length: 128)
    Artifact type.
- `created_at` (string · format: date-time)
  ISO timestamp when the rule was created.
- `created_by` (object)
  Actor who created the rule.
  - `profile_uid` (string) — nullable
    User profile ID of the actor, or `null` when it cannot be resolved.
- `enabled` (boolean)
  Whether the rule is enabled.
- `grouping` (object)
  Grouping configuration.
  - `fields` (string[] · min items: 1 · max items: 16)
    Fields to group alerts by, e.g. ["host.name", "service.name"]. Should match ES|QL GROUP BY fields.
- `id` (string)
  Unique rule identifier.
- `kind` (enum)
  Whether the rule creates alerts (`alert`) or only stores matching events (`signal`).
  Values: `alert`, `signal`
- `metadata` (object)
  Rule metadata.
  - `builder` (object)
    Identifies the rule builder that authored this rule (e.g. "threshold"). Absent for rules authored directly in ES|QL; send `null` on PATCH to clear it.
    - `type` (string · min length: 1 · max length: 64)
      Rule builder type.
  - `description` (string · min length: 1 · max length: 1024)
    Human-readable description of the rule. Omit to leave it unset; send `null` on PATCH to clear it. An empty string is rejected.
  - `name` (string · min length: 1 · max length: 256)
    Rule name (must be unique within the space).
  - `routing_tags` (string[] · min items: 1 · max items: 20)
    Routing tags that link alerts from this rule to action policies. An action policy applies when its `matcher.tags` contains at least one of these tags. Only allowed when kind is "alert".
  - `tags` (string[] · min items: 1 · max items: 20)
    Tags for categorization, e.g. ["production", "infra"].
  - `template` (object)
    The rule template this rule was created from.
    - `id` (string)
      Id of the rule template.
- `no_data` (union oneOf)
  What the rule does when a group has no data. Required when `kind` is `alert`. Not allowed when `kind` is `signal`. Any strategy other than `ignore` requires either `query.breach` or `no_data.query`, so that a group with no data can be told apart from one that stopped breaching.
  - **strategy: ignore** (`strategy: ignore`)
    Does not check whether a group still has data. Missing groups do not produce `no_data` events.
    - `strategy` (enum)
      Values: `ignore`
  - **strategy: keep_last** (`strategy: keep_last`)
    Holds the alert's current status when the rule finds no data.
    - `strategy` (enum)
      Values: `keep_last`
    - `query` (string · min length: 1 · max length: 10000)
      Optional ES|QL query that checks whether a group has data. If omitted, `query.base` is used, which then has to be a presence query in its own right — so `query.breach` is required.
  - **strategy: resolve** (`strategy: resolve`)
    Closes the alert the first time the rule finds no data for a group.
    - `strategy` (enum)
      Values: `resolve`
    - `query` (string · min length: 1 · max length: 10000)
      Optional ES|QL query that checks whether a group has data. If omitted, `query.base` is used, which then has to be a presence query in its own right — so `query.breach` is required.
  - **strategy: alert** (`strategy: alert`)
    Marks an existing alert `active` when the rule finds no data. It never opens an alert for a group that has not breached. Not accepted when creating or updating rules.
    - `strategy` (enum)
      Values: `alert`
    - `query` (string · min length: 1 · max length: 10000)
      Optional ES|QL query that checks whether a group has data. If omitted, `query.base` is used, which then has to be a presence query in its own right — so `query.breach` is required.
- `query` (object)
  ES|QL query the rule evaluates. `base` is required. `breach` is an optional clause appended to it.
  - `base` (string · min length: 1 · max length: 10000)
    ES|QL query that specifies the data to evaluate. Must include a `FROM` clause. Kibana applies the time filter from `schedule.lookback` using `time_field`.
  - `breach` (object)
    Optional ES|QL clause appended to `query.base`. If omitted, every row from `query.base` is a match, and a `no_data` strategy other than `ignore` then requires `no_data.query`.
    - `segment` (string · min length: 1 · max length: 10000)
      ES|QL clause appended to `query.base`, for example `WHERE avg_cpu > 0.85`. Don't include a `FROM` clause.
- `recovery` (union oneOf)
  When an alert recovers. Required when `kind` is `alert`. Not allowed when `kind` is `signal`.
  - **strategy: no_breach** (`strategy: no_breach`)
    Recovers the alert when its group no longer appears in the breach results.
    - `strategy` (enum)
      Values: `no_breach`
  - **strategy: condition** (`strategy: condition`)
    Recovers the alert when `query.base` plus `segment` returns the group. Requires `query.breach`.
    - `strategy` (enum)
      Values: `condition`
    - `segment` (string · min length: 1 · max length: 10000)
      ES|QL clause appended to `query.base`, for example `WHERE avg_cpu < 0.60`. Don't include a `FROM` clause.
  - **strategy: query** (`strategy: query`)
    Recovers the alert when this separate query returns the group.
    - `strategy` (enum)
      Values: `query`
    - `query` (string · min length: 1 · max length: 10000)
      Independent ES|QL query, including its own `FROM` clause. A matching group recovers the alert.
  - **strategy: manual** (`strategy: manual`)
    Does not recover automatically. Close the alert with a user action. `state_transition.recovering` has no effect.
    - `strategy` (enum)
      Values: `manual`
- `schedule` (object)
  Execution schedule configuration.
  - `every` (string · max length: 32)
    Execution interval, e.g. 1m, 5m, 1h.
  - `lookback` (string · max length: 32)
    Lookback window for the query, e.g. 5m, 1h. Can also be expressed in ES|QL.
- `state_transition` (object)
  Specifies how many consecutive matches, or how long a condition must hold, before an alert becomes `active` or `inactive`. Allowed only when `kind` is `alert`.
  - `pending` (object)
    - `count` (integer · min: 0 · max: 1000)
      Consecutive matches the alert spends in `pending` before it becomes `active` on the next match. For example, `2` opens it on the third consecutive match. Set to `0` to open it on the first match.
    - `operator` (enum)
      When both `count` and `timeframe` are set, `and` requires both and `or` requires either. Allowed only when both fields are present.
      Values: `and`, `or`
    - `timeframe` (string · max length: 32)
      Duration the condition must hold, for example `5m`. Combine with `count` using `operator`.
  - `recovering` (object)
    - `count` (integer · min: 0 · max: 1000)
      Consecutive recoveries the alert spends in `recovering` before it becomes `inactive` on the next recovery. For example, `2` closes it on the third consecutive recovery. Set to `0` to close it on the first recovery.
    - `operator` (enum)
      When both `count` and `timeframe` are set, `and` requires both and `or` requires either. Allowed only when both fields are present.
      Values: `and`, `or`
    - `timeframe` (string · max length: 32)
      Duration the condition must hold, for example `5m`. Combine with `count` using `operator`.
- `time_field` (string · min length: 1 · max length: 256 · default: @timestamp)
  Document field Kibana uses with `schedule.lookback` to time-filter `query.base`.
- `updated_at` (string · format: date-time)
  ISO timestamp when the rule was last updated.
- `updated_by` (object)
  Actor who last updated the rule.
  - `profile_uid` (string) — nullable
    User profile ID of the actor, or `null` when it cannot be resolved.
- `version` (integer · min: 1 · max: 9007199254740991)
  Monotonically increasing integer number representing a rule configuration version, incremented on every change. Used on generated rule events as `rule.version`.
#### `400`

Indicates an invalid schema or parameters.

`application/json`

- `code` (string)
  Stable error code you can branch on, for example `INVALID_SCHEDULE` or `RULE_ALREADY_EXISTS`.
- `details` (map string to object)
  Optional extra information about the error, for example field validation issues or the `rule_id` when that ID already exists.
- `error` (string)
  A short human-readable summary of the error category (e.g., "Not Found", "Bad Request"). Subject to change without notice. Do not parse or rely on its content.
- `message` (string)
  A readable explanation of the error. The wording can change without notice. Do not parse this field.
#### `401`

Indicates the request was not authenticated.

`application/json`

- `code` (string)
  Stable error code you can branch on, for example `INVALID_SCHEDULE` or `RULE_ALREADY_EXISTS`.
- `details` (map string to object)
  Optional extra information about the error, for example field validation issues or the `rule_id` when that ID already exists.
- `error` (string)
  A short human-readable summary of the error category (e.g., "Not Found", "Bad Request"). Subject to change without notice. Do not parse or rely on its content.
- `message` (string)
  A readable explanation of the error. The wording can change without notice. Do not parse this field.
#### `403`

Indicates the user does not have the required privileges to perform the request.

`application/json`

- `code` (string)
  Stable error code you can branch on, for example `INVALID_SCHEDULE` or `RULE_ALREADY_EXISTS`.
- `details` (map string to object)
  Optional extra information about the error, for example field validation issues or the `rule_id` when that ID already exists.
- `error` (string)
  A short human-readable summary of the error category (e.g., "Not Found", "Bad Request"). Subject to change without notice. Do not parse or rely on its content.
- `message` (string)
  A readable explanation of the error. The wording can change without notice. Do not parse this field.
#### `404`

Indicates a rule with the given ID does not exist.

`application/json`

- `code` (string)
  Stable error code you can branch on, for example `INVALID_SCHEDULE` or `RULE_ALREADY_EXISTS`.
- `details` (map string to object)
  Optional extra information about the error, for example field validation issues or the `rule_id` when that ID already exists.
- `error` (string)
  A short human-readable summary of the error category (e.g., "Not Found", "Bad Request"). Subject to change without notice. Do not parse or rely on its content.
- `message` (string)
  A readable explanation of the error. The wording can change without notice. Do not parse this field.
#### `409`

Indicates the rule was concurrently updated by another caller.

`application/json`

- `code` (string)
  Stable error code you can branch on, for example `INVALID_SCHEDULE` or `RULE_ALREADY_EXISTS`.
- `details` (map string to object)
  Optional extra information about the error, for example field validation issues or the `rule_id` when that ID already exists.
- `error` (string)
  A short human-readable summary of the error category (e.g., "Not Found", "Bad Request"). Subject to change without notice. Do not parse or rely on its content.
- `message` (string)
  A readable explanation of the error. The wording can change without notice. Do not parse this field.
#### `500`

Indicates an unexpected server-side error.

`application/json`

- `code` (string)
  Stable error code you can branch on, for example `INVALID_SCHEDULE` or `RULE_ALREADY_EXISTS`.
- `details` (map string to object)
  Optional extra information about the error, for example field validation issues or the `rule_id` when that ID already exists.
- `error` (string)
  A short human-readable summary of the error category (e.g., "Not Found", "Bad Request"). Subject to change without notice. Do not parse or rely on its content.
- `message` (string)
  A readable explanation of the error. The wording can change without notice. Do not parse this field.
#### `503`

Indicates the alerting engine is disabled by the `alerting:v2:enabled` advanced setting.

`application/json`

- `code` (string)
  Stable error code you can branch on, for example `INVALID_SCHEDULE` or `RULE_ALREADY_EXISTS`.
- `details` (map string to object)
  Optional extra information about the error, for example field validation issues or the `rule_id` when that ID already exists.
- `error` (string)
  A short human-readable summary of the error category (e.g., "Not Found", "Bad Request"). Subject to change without notice. Do not parse or rely on its content.
- `message` (string)
  A readable explanation of the error. The wording can change without notice. Do not parse this field.
## Code examples

#### Console

```console
PATCH kbn:/api/alerting/v2/rules/ID
{
  "metadata": {
    "description": "Updated description.",
    "name": "Host CPU high (updated)",
    "tags": "null"
  }
}
```

#### curl

```curl
curl -X PATCH "${KIBANA_URL}/api/alerting/v2/rules/${ID}" \
  -H "kbn-xsrf: true" \
  -H "Authorization: ApiKey ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "metadata": {
      "description": "Updated description.",
      "name": "Host CPU high (updated)",
      "tags": "null"
    }
  }'
```

## Response examples

#### Updated rule metadata

```json
{
  "created_at": "2026-01-15T12:00:00.000Z",
  "created_by": {
    "profile_uid": "u_elastic_0"
  },
  "enabled": true,
  "grouping": {
    "fields": [
      "host.name"
    ]
  },
  "id": "rule-1",
  "kind": "alert",
  "metadata": {
    "description": "Updated description.",
    "name": "Host CPU high (updated)",
    "routing_tags": [
      "sre-oncall"
    ],
    "tags": [
      "production",
      "infra"
    ]
  },
  "no_data": {
    "strategy": "keep_last"
  },
  "query": {
    "base": "FROM metrics-* | STATS avg_cpu = AVG(host.cpu.usage) BY host.name",
    "breach": {
      "segment": "WHERE avg_cpu \u003E 0.9"
    }
  },
  "recovery": {
    "strategy": "no_breach"
  },
  "schedule": {
    "every": "1m",
    "lookback": "5m"
  },
  "state_transition": {
    "pending": {
      "count": 1
    },
    "recovering": {
      "count": 1
    }
  },
  "time_field": "@timestamp",
  "updated_at": "2026-01-15T12:00:00.000Z",
  "updated_by": {
    "profile_uid": "u_elastic_0"
  },
  "version": 1
}
```

#### Update body includes an unrecognized field

```json
{
  "code": "BAD_REQUEST",
  "details": {
    "errors": {
      "unknownField": [
        "Unrecognized key"
      ]
    }
  },
  "error": "Bad Request",
  "message": "Unrecognized key(s) in object: \u0027unknownField\u0027"
}
```

#### Request was not authenticated

```json
{
  "code": "UNAUTHORIZED",
  "error": "Unauthorized",
  "message": "Authentication required to access this API."
}
```

#### Caller lacks required privileges

```json
{
  "code": "FORBIDDEN",
  "error": "Forbidden",
  "message": "The current user does not have the required privileges for this request."
}
```

#### No rule exists for the given ID

```json
{
  "code": "RULE_NOT_FOUND",
  "details": {
    "rule_id": "rule-1"
  },
  "error": "Not Found",
  "message": "Rule with id \u0022rule-1\u0022 not found"
}
```

#### Indicates the rule was concurrently updated by another caller.

```json
{
  "code": "RULE_VERSION_CONFLICT",
  "details": {
    "rule_id": "rule-1"
  },
  "error": "Conflict",
  "message": "Rule with id \u0022rule-1\u0022 has already been updated by another user"
}
```

#### Unexpected server-side error

```json
{
  "code": "INTERNAL_SERVER_ERROR",
  "error": "Internal Server Error",
  "message": "An unexpected error occurred."
}
```

#### Alerting V2 is disabled

```json
{
  "code": "ALERTING_DISABLED",
  "error": "Service Unavailable",
  "message": "Alerting is disabled."
}
```

