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

# Create or replace an action policy

Availability:

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

## Paths

- `PUT` `/api/alerting/v2/action_policies/{id}`
- `PUT` `/s/{space_id}/api/alerting/v2/action_policies/{id}`

- [Api key auth](/elastic/docs-builder/docs/4371/api/doc/kibana-serverless/authentication#apikeyauth)

#### Path Parameters

- `id` (string · min: 1 · max: 150 · pattern: ^[a-zA-Z0-9_-]+$)
  The ID of the action policy. Copy it from the response when you create a policy, fetch one policy, or fetch the policy list. 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 alert episodes 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 an action policy with the given identifier, or fully replaces it if one already exists.<br/><br/>[Required authorization] Route required privileges: manage_alerting-v2-action-policies AND read_alerting-v2-rules.

## Request Body

`application/json`

- `description` (string · min: 1 · max: 1024) — optional
  A description of the action policy. Absent when the policy has none; send `null` on PATCH to clear it.
- `destinations` ([] union  · min: 1 · max: 10) — required
  The list of destinations. At least one is required.
  - **Kibana_HTTP_APIs_alerting_workflow_action_policy_destination**
    - `id` (string · min: 1 · max: 150) — required
      The workflow identifier.
    - `type` (enum) — required
      The destination type.
      Values: `workflow`
- `grouping` (union) — optional
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_per_alert** (`mode: per_alert`)
    one notification per alert lifecycle (default).
    - `mode` (enum) — required
      Values: `per_alert`
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_all** (`mode: all`)
    a single notification for all matching alerts.
    - `mode` (enum) — required
      Values: `all`
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_per_field** (`mode: per_field`)
    group by the specified `fields`.
    - `fields` ([] string · min: 1 · max: 16) — required
      The fields alerts are grouped by. At least one is required, and no other mode accepts them.
    - `mode` (enum) — required
      Values: `per_field`
- `matcher` (object) — optional
  Selects the alerts this policy applies to. Set `tags` to match alerts from rules with those routing tags. Set `expression` to a KQL query, which will be evaluated against each alert. <br/><br/> If you set both `tags` and `expression`, an alert must match the tags and the expression for the policy to apply. At least one of `tags` and `expression` must be set, so an empty `matcher` is rejected. Omit `matcher` entirely for a catch-all policy that applies to all alerts.
  - `expression` (string · min: 1 · max: 4096) — optional
    A KQL query that's evaluated against each alert. Supported fields are: `alert_id`, `alert_status`, `group_hash`, `last_event_timestamp`, `severity`, and your rule's query output columns under `data.*` (for example, `data.host.name`). Referencing other fields won't work. Omit `matcher.expression` to match on `tags` alone.
  - `tags` ([] string · min: 1 · max: 50) — optional
    Routing tags this policy should match. The policy applies to alerts from any rule whose `metadata.routing_tags` include at least one of these tags. An empty array is not accepted. Omit `matcher.tags` to match on `matcher.expression` alone.
- `name` (string · min: 1 · max: 256) — required
  The name of the action policy.
- `throttle` (union) — optional
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_on_status_change** (`strategy: on_status_change`)
    notify only on alert status transitions (default for `per_alert`).
    - `strategy` (enum) — required
      Values: `on_status_change`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_per_status_interval** (`strategy: per_status_interval`)
    notify on transitions and at regular intervals.
    - `interval` (string · max: 32) — required
      The throttle interval duration (e.g. 5m, 1h) that sets the notification cadence.
    - `strategy` (enum) — required
      Values: `per_status_interval`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_time_interval** (`strategy: time_interval`)
    notify at regular intervals regardless of status (default for `all`/`per_field`).
    - `interval` (string · max: 32) — required
      The throttle interval duration (e.g. 5m, 1h) that sets the notification cadence.
    - `strategy` (enum) — required
      Values: `time_interval`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_every_time** (`strategy: every_time`)
    notify on every evaluation cycle (high volume).
    - `strategy` (enum) — required
      Values: `every_time`

## Responses

#### `200`

Returns the replaced action policy.

`application/json`

- `created_at` (string · pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$)
  The ISO datetime when the action policy was created.
- `created_by` ({})
  The actor who created the action policy.
  - `profile_uid` (string) — nullable
    User profile ID of the actor, or `null` when it cannot be resolved.
- `description` (string)
  A description of the action policy. Omitted when the policy has none.
- `destinations` ([] union)
  The list of destinations.
  - **Kibana_HTTP_APIs_alerting_workflow_action_policy_destination**
    - `id` (string · min: 1 · max: 150)
      The workflow identifier.
    - `type` (enum)
      The destination type.
      Values: `workflow`
- `enabled` (boolean)
  Whether the action policy is enabled.
- `grouping` (union)
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_per_alert** (`mode: per_alert`)
    one notification per alert lifecycle (default).
    - `mode` (enum)
      Values: `per_alert`
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_all** (`mode: all`)
    a single notification for all matching alerts.
    - `mode` (enum)
      Values: `all`
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_per_field** (`mode: per_field`)
    group by the specified `fields`.
    - `fields` ([] string · min: 1 · max: 16)
      The fields alerts are grouped by. At least one is required, and no other mode accepts them.
    - `mode` (enum)
      Values: `per_field`
- `id` (string)
  The unique identifier for the action policy.
- `matcher` (object)
  Selects the alerts this policy applies to. Set `tags` to match alerts from rules with those routing tags. Set `expression` to a KQL query, which will be evaluated against each alert. <br/><br/> If you set both `tags` and `expression`, an alert must match the tags and the expression for the policy to apply. At least one of `tags` and `expression` must be set, so an empty `matcher` is rejected. Omit `matcher` entirely for a catch-all policy that applies to all alerts.
  - `expression` (string · min: 1 · max: 4096)
    A KQL query that's evaluated against each alert. Supported fields are: `alert_id`, `alert_status`, `group_hash`, `last_event_timestamp`, `severity`, and your rule's query output columns under `data.*` (for example, `data.host.name`). Referencing other fields won't work. Omit `matcher.expression` to match on `tags` alone.
  - `tags` ([] string · min: 1 · max: 50)
    Routing tags this policy should match. The policy applies to alerts from any rule whose `metadata.routing_tags` include at least one of these tags. An empty array is not accepted. Omit `matcher.tags` to match on `matcher.expression` alone.
- `name` (string)
  The name of the action policy.
- `snoozed_until` (string)
  The ISO datetime until which the policy is snoozed. Omitted when the policy is not snoozed.
- `throttle` (union)
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_on_status_change** (`strategy: on_status_change`)
    notify only on alert status transitions (default for `per_alert`).
    - `strategy` (enum)
      Values: `on_status_change`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_per_status_interval** (`strategy: per_status_interval`)
    notify on transitions and at regular intervals.
    - `interval` (string · max: 32)
      The throttle interval duration (e.g. 5m, 1h) that sets the notification cadence.
    - `strategy` (enum)
      Values: `per_status_interval`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_time_interval** (`strategy: time_interval`)
    notify at regular intervals regardless of status (default for `all`/`per_field`).
    - `interval` (string · max: 32)
      The throttle interval duration (e.g. 5m, 1h) that sets the notification cadence.
    - `strategy` (enum)
      Values: `time_interval`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_every_time** (`strategy: every_time`)
    notify on every evaluation cycle (high volume).
    - `strategy` (enum)
      Values: `every_time`
- `updated_at` (string · pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$)
  The ISO datetime when the action policy was last updated.
- `updated_by` ({})
  The actor who last updated the action policy.
  - `profile_uid` (string) — nullable
    User profile ID of the actor, or `null` when it cannot be resolved.
#### `201`

Returns the newly created action policy.

`application/json`

- `created_at` (string · pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$)
  The ISO datetime when the action policy was created.
- `created_by` ({})
  The actor who created the action policy.
  - `profile_uid` (string) — nullable
    User profile ID of the actor, or `null` when it cannot be resolved.
- `description` (string)
  A description of the action policy. Omitted when the policy has none.
- `destinations` ([] union)
  The list of destinations.
  - **Kibana_HTTP_APIs_alerting_workflow_action_policy_destination**
    - `id` (string · min: 1 · max: 150)
      The workflow identifier.
    - `type` (enum)
      The destination type.
      Values: `workflow`
- `enabled` (boolean)
  Whether the action policy is enabled.
- `grouping` (union)
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_per_alert** (`mode: per_alert`)
    one notification per alert lifecycle (default).
    - `mode` (enum)
      Values: `per_alert`
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_all** (`mode: all`)
    a single notification for all matching alerts.
    - `mode` (enum)
      Values: `all`
  - **Kibana_HTTP_APIs_alerting_action_policy_grouping_per_field** (`mode: per_field`)
    group by the specified `fields`.
    - `fields` ([] string · min: 1 · max: 16)
      The fields alerts are grouped by. At least one is required, and no other mode accepts them.
    - `mode` (enum)
      Values: `per_field`
- `id` (string)
  The unique identifier for the action policy.
- `matcher` (object)
  Selects the alerts this policy applies to. Set `tags` to match alerts from rules with those routing tags. Set `expression` to a KQL query, which will be evaluated against each alert. <br/><br/> If you set both `tags` and `expression`, an alert must match the tags and the expression for the policy to apply. At least one of `tags` and `expression` must be set, so an empty `matcher` is rejected. Omit `matcher` entirely for a catch-all policy that applies to all alerts.
  - `expression` (string · min: 1 · max: 4096)
    A KQL query that's evaluated against each alert. Supported fields are: `alert_id`, `alert_status`, `group_hash`, `last_event_timestamp`, `severity`, and your rule's query output columns under `data.*` (for example, `data.host.name`). Referencing other fields won't work. Omit `matcher.expression` to match on `tags` alone.
  - `tags` ([] string · min: 1 · max: 50)
    Routing tags this policy should match. The policy applies to alerts from any rule whose `metadata.routing_tags` include at least one of these tags. An empty array is not accepted. Omit `matcher.tags` to match on `matcher.expression` alone.
- `name` (string)
  The name of the action policy.
- `snoozed_until` (string)
  The ISO datetime until which the policy is snoozed. Omitted when the policy is not snoozed.
- `throttle` (union)
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_on_status_change** (`strategy: on_status_change`)
    notify only on alert status transitions (default for `per_alert`).
    - `strategy` (enum)
      Values: `on_status_change`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_per_status_interval** (`strategy: per_status_interval`)
    notify on transitions and at regular intervals.
    - `interval` (string · max: 32)
      The throttle interval duration (e.g. 5m, 1h) that sets the notification cadence.
    - `strategy` (enum)
      Values: `per_status_interval`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_time_interval** (`strategy: time_interval`)
    notify at regular intervals regardless of status (default for `all`/`per_field`).
    - `interval` (string · max: 32)
      The throttle interval duration (e.g. 5m, 1h) that sets the notification cadence.
    - `strategy` (enum)
      Values: `time_interval`
  - **Kibana_HTTP_APIs_alerting_action_policy_throttle_every_time** (`strategy: every_time`)
    notify on every evaluation cycle (high volume).
    - `strategy` (enum)
      Values: `every_time`
- `updated_at` (string · pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$)
  The ISO datetime when the action policy was last updated.
- `updated_by` ({})
  The actor who last updated the action policy.
  - `profile_uid` (string) — nullable
    User profile ID of the actor, or `null` when it cannot be resolved.
#### `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, or the current license does not support action policies. Creating, updating, and enabling action policies requires an active Enterprise license.

`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 action policy was created or updated concurrently 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
PUT /api/alerting/v2/action_policies/{id}
{
  "description": "Sends notifications for alerts generated by rules with the production tag.",
  "destinations": [
    {
      "id": "workflow-1",
      "type": "workflow"
    }
  ],
  "grouping": {
    "mode": "per_alert"
  },
  "matcher": {
    "tags": [
      "production"
    ]
  },
  "name": "Notify on production alerts",
  "throttle": {
    "strategy": "on_status_change"
  }
}
```

#### curl

```curl
curl -X PUT "https://{kibana_url}/api/alerting/v2/action_policies/{id}" \
  -H "kbn-xsrf: true" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Sends notifications for alerts generated by rules with the production tag.",
  "destinations": [
    {
      "id": "workflow-1",
      "type": "workflow"
    }
  ],
  "grouping": {
    "mode": "per_alert"
  },
  "matcher": {
    "tags": [
      "production"
    ]
  },
  "name": "Notify on production alerts",
  "throttle": {
    "strategy": "on_status_change"
  }
}'
```

## Response Examples

#### Replaced an existing action policy

```json
{
  "created_at": "2026-01-15T12:00:00.000Z",
  "created_by": {
    "profile_uid": "u_elastic_0"
  },
  "description": "Sends notifications for alerts generated by rules with the production tag.",
  "destinations": [
    {
      "id": "workflow-1",
      "type": "workflow"
    }
  ],
  "enabled": true,
  "grouping": {
    "mode": "per_alert"
  },
  "id": "action-policy-1",
  "matcher": {
    "tags": [
      "production"
    ]
  },
  "name": "Notify on production alerts",
  "throttle": {
    "strategy": "on_status_change"
  },
  "updated_at": "2026-01-15T12:00:00.000Z",
  "updated_by": {
    "profile_uid": "u_elastic_0"
  }
}
```

#### Created a new action policy

```json
{
  "created_at": "2026-01-15T12:00:00.000Z",
  "created_by": {
    "profile_uid": "u_elastic_0"
  },
  "description": "Sends notifications for alerts generated by rules with the production tag.",
  "destinations": [
    {
      "id": "workflow-1",
      "type": "workflow"
    }
  ],
  "enabled": true,
  "grouping": {
    "mode": "per_alert"
  },
  "id": "action-policy-1",
  "matcher": {
    "tags": [
      "production"
    ]
  },
  "name": "Notify on production alerts",
  "throttle": {
    "strategy": "on_status_change"
  },
  "updated_at": "2026-01-15T12:00:00.000Z",
  "updated_by": {
    "profile_uid": "u_elastic_0"
  }
}
```

#### Indicates an invalid schema or parameters.

```json
{
  "code": "INVALID_ACTION_POLICY_DATA",
  "details": {
    "context": "upsert",
    "errors": {
      "errors": [],
      "properties": {
        "name": {
          "errors": [
            "Invalid input: expected string, received undefined"
          ]
        }
      }
    }
  },
  "error": "Bad Request",
  "message": "Error validating upsert action policy data - name: Invalid input: expected string, received undefined"
}
```

#### Request was not authenticated

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

#### The current license does not support action policies

```json
{
  "code": "ACTION_POLICY_LICENSE_NOT_SUPPORTED",
  "details": {
    "current_license": "basic",
    "license_status": "active",
    "required_license": "enterprise"
  },
  "error": "Forbidden",
  "message": "Action policies require an active enterprise license"
}
```

#### Caller lacks required privileges

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

#### Indicates the action policy was concurrently updated by another caller.

```json
{
  "code": "ACTION_POLICY_VERSION_CONFLICT",
  "details": {
    "action_policy_id": "action-policy-1"
  },
  "error": "Conflict",
  "message": "Action policy with ID \u0022action-policy-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."
}
```

