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

# Create or replace a rule

Availability: Experimental

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

## Paths

- `PUT` `/api/alerting/v2/rules/{id}`
- `PUT` `/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.

Creates a rule with the given identifier, or fully replaces it if one already exists.

## Required authorization

- Privileges: `manage_alerting-v2-rules`

## Authentication

- Api key: `Authorization: <value>`
  You must create an API key and use the encoded value in the request header. To learn about creating keys, go to [API keys](https://www.elastic.co/docs/current/serverless/api-keys).

## 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
  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) — required
    Fields to group alerts by, e.g. ["host.name", "service.name"]. Should match ES|QL GROUP BY fields.
- `kind` (enum) — required
  Whether the rule creates alerts (`alert`) or only stores matching events (`signal`).
  Values: `alert`, `signal`
- `metadata` (object) — required
  Rule metadata.
  - `builder` (object) — optional
    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
    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) — required
    Rule name (must be unique within the space).
  - `routing_tags` (string[] · min items: 1 · max items: 20) — optional
    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
    Tags for categorization, e.g. ["production", "infra"].
- `no_data` (union oneOf) — 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.
  - **strategy: ignore** (`strategy: ignore`)
    Does not check whether a group still has data. Missing groups do not produce `no_data` events.
    - `strategy` (enum) — required
      Values: `ignore`
  - **strategy: keep_last** (`strategy: keep_last`)
    Holds the alert's current status when the rule finds no data.
    - `strategy` (enum) — required
      Values: `keep_last`
    - `query` (string · min length: 1 · max length: 10000) — optional
      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) — required
      Values: `resolve`
    - `query` (string · min length: 1 · max length: 10000) — optional
      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) — required
      Values: `alert`
    - `query` (string · min length: 1 · max length: 10000) — optional
      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) — required
  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) — required
    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) — required
      ES|QL clause appended to `query.base`, for example `WHERE avg_cpu > 0.85`. Don't include a `FROM` clause.
- `recovery` (union oneOf) — optional
  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) — required
      Values: `no_breach`
  - **strategy: condition** (`strategy: condition`)
    Recovers the alert when `query.base` plus `segment` returns the group. Requires `query.breach`.
    - `strategy` (enum) — required
      Values: `condition`
    - `segment` (string · min length: 1 · max length: 10000) — required
      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) — required
      Values: `query`
    - `query` (string · min length: 1 · max length: 10000) — required
      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) — required
      Values: `manual`
- `schedule` (object) — required
  Execution schedule configuration.
  - `every` (string · max length: 32) — required
    Execution interval, e.g. 1m, 5m, 1h.
  - `lookback` (string · max length: 32) — optional
    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
    - `count` (integer · min: 0 · max: 1000) — optional
      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
      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
      Duration the condition must hold, for example `5m`. Combine with `count` using `operator`.
  - `recovering` (object) — optional
    - `count` (integer · min: 0 · max: 1000) — optional
      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
      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
      Duration the condition must hold, for example `5m`. Combine with `count` using `operator`.
- `time_field` (string · min length: 1 · max length: 256 · default: @timestamp) — optional
  Document field Kibana uses with `schedule.lookback` to time-filter `query.base`.

## Responses

#### `200`

Returns the replaced 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`.
#### `201`

Returns the newly created 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.
#### `409`

Indicates the rule was created or updated concurrently, or the request changes immutable fields.

`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
PUT kbn:/api/alerting/v2/rules/ID
{
  "grouping": {
    "fields": [
      "host.name"
    ]
  },
  "kind": "alert",
  "metadata": {
    "description": "Alerts when average CPU usage exceeds a threshold.",
    "name": "Host CPU high",
    "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"
}
```

#### curl

```curl
curl -X PUT "${KIBANA_URL}/api/alerting/v2/rules/${ID}" \
  -H "kbn-xsrf: true" \
  -H "Authorization: ApiKey ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "grouping": {
      "fields": [
        "host.name"
      ]
    },
    "kind": "alert",
    "metadata": {
      "description": "Alerts when average CPU usage exceeds a threshold.",
      "name": "Host CPU high",
      "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"
  }'
```

## Response examples

#### Replaced an existing rule

```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": "Alerts when average CPU usage exceeds a threshold.",
    "name": "Host CPU high",
    "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
}
```

#### Created a new rule with the given ID

```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": "Alerts when average CPU usage exceeds a threshold.",
    "name": "Host CPU high",
    "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
}
```

#### Upsert body is missing required rule metadata

```json
{
  "code": "BAD_REQUEST",
  "details": {
    "errors": {
      "metadata": [
        "Required"
      ]
    }
  },
  "error": "Bad Request",
  "message": "metadata: Required"
}
```

#### 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."
}
```

#### Rule was changed concurrently 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."
}
```

