> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rootly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Investigation Rules API

> Create, read, update, and delete team-scoped AI SRE investigation rules with the beta public REST API.

> [!WARNING]
>
> This API is in beta and may change without notice. Breaking or unstable changes are possible.

Manage [investigation rules](https://docs.rootly.com/ai/ai-sre/investigation-rules.md) at `https://api.rootly.com/v1/ai/sre/investigation_rules`.

Rules support alerts and incidents in both the API and the web app's **Applies to** selector. A matching incident rule can supply instructions to a manual investigation or start an automatic investigation in **Auto-run** mode. AI SRE must be enabled; incident rules don't require a separate feature flag or account-level automatic investigation. Scheduled maintenance incidents are excluded, and an existing investigation on a linked alert can suppress an incident's automatic run.

## Access

Use a Personal or Global API key in `Authorization: Bearer YOUR_API_KEY`. Global API keys (`kind: organization`) are accepted, use their assigned roles and key's team, and receive the same success statuses listed below when authorized. Missing permissions return 403; Global keys aren't rejected just for their kind. Team API keys are rejected with 404. OAuth tokens return 403 with the title `API key required.`.

AI SRE must be enabled for your team. Reads require `view_configure_ai` or `configure_ai_sre_rules`; writes require `configure_ai_sre_rules`. Every operation is scoped to the API key's team.

Personal and Global keys are bound to the team selected when the key is created; these endpoints use that stored team, not a team-selection header or parameter. Personal keys use their owner's membership in that team; Global keys use their service user's membership and the key's assigned roles, not the human creator's permissions.

Send JSON:API requests with `Content-Type: application/vnd.api+json`.

## Operations

| Method | Path | Success |
| - | - | - |
| GET | `/v1/ai/sre/investigation_rules` | 200: `data` array and pagination `meta`, ordered by position |
| POST | `/v1/ai/sre/investigation_rules` | 201: created resource |
| GET | `/v1/ai/sre/investigation_rules/{id}` | 200: resource |
| PUT or PATCH | `/v1/ai/sre/investigation_rules/{id}` | 200: updated resource |
| DELETE | `/v1/ai/sre/investigation_rules/{id}` | 200: deleted resource |

List filters are `filter[name]` (case-insensitive substring), `filter[status]`, and `filter[resource_type]`. Use `page[number]` and `page[size]` for pagination. Rule IDs are UUIDv7 identifiers; missing, malformed, or other-team IDs return 404.

## Create a Rule

POST requires `name` and `resource_type`. These attributes are writable:

| Attribute | Values |
| - | - |
| `name` | Nonempty string |
| `resource_type` | `alert` or `incident` |
| `status` | `manual`, `auto_run`, or `paused` |
| `instruction` | String or null |
| `cooldown_seconds` | Integer from 0 to 2147483647, or null |
| `position` | Integer starting at 1 |
| `conditions` | Array of condition objects; all conditions must match |

```bash theme={null}
curl --request POST 'https://api.rootly.com/v1/ai/sre/investigation_rules' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/vnd.api+json' \
  --data '{
    "data": {
      "type": "investigation_rules",
      "attributes": {
        "name": "Production alerts",
        "resource_type": "alert",
        "status": "manual",
        "instruction": "Check deploys first.",
        "cooldown_seconds": 300,
        "position": 1,
        "conditions": [
          {"field": "payload", "key": "$.environment", "operator": "is_one_of", "values": ["production"]}
        ]
      }
    }
  }'
```

Example response (201):

```json theme={null}
{
  "data": {
    "id": "019a6500-0000-7000-8000-000000000001",
    "type": "investigation_rules",
    "attributes": {
      "name": "Production alerts",
      "resource_type": "alert",
      "status": "manual",
      "instruction": "Check deploys first.",
      "cooldown_seconds": 300,
      "position": 1,
      "conditions": [
        {"field": "payload", "key": "$.environment", "operator": "is_one_of", "values": ["production"]}
      ]
    }
  }
}
```

### Condition Fields

Each condition requires `field`, `operator`, and `values` (an array of strings). `key` is optional or null, except that `payload` requires a JSON path.

| Resource type | Field tokens |
| - | - |
| `alert` | `summary`, `description`, `urgency`, `source`, `status`, `group_status`, `payload`, `alert_field:<id>` |
| `incident` | `severity`, `services`, `groups`, `incident_types` |

Use an existing alert field UUID for `alert_field:<id>`. Incident condition values are the IDs of your team's severities, services, teams, or incident types. Operators are `is_one_of`, `contains_one_of`, and `is_not_one_of`.

The incident field token `groups` corresponds to **Teams** in the web app.

### Position and Priority

Positions are managed as one list per team, shared by alert and incident rules:

* Omitting `position` on create appends the rule after the current highest position. Explicitly sending `position: 1` on create also appends it, because 1 is the database default.
* Creating a rule at another occupied position shifts rules at that position and below down by one.
* Updating a rule to an occupied position moves it there and shifts the intervening rules. Sending its existing position leaves the order unchanged.
* A position beyond the current end is stored as supplied, without clamping or filling gaps. Updating to an unoccupied position doesn't reshuffle neighbors.
* There is no position uniqueness validation or unique database index. Normal saves use list callbacks to handle occupied positions; don't rely on duplicate positions as a supported priority scheme.

Matching uses ascending `position`, after filtering by resource type and run mode. If duplicate positions remain, there is no explicit tie-breaker: don't rely on creation time or ID to choose the winning rule.

## Update a Rule

PUT and PATCH accept partial attributes in the same envelope. Omit `conditions` to keep the existing rows; send `conditions: []` to clear them. If you change `resource_type`, you must supply `conditions`.

GET still returns a deleted custom alert field as `alert_field:<id>`, but sending that condition back in `conditions` returns 422. Remove or replace it, or omit `conditions` to keep the existing conditions unchanged.

Example PATCH body to rename a rule without changing its conditions:

```json theme={null}
{
  "data": {
    "type": "investigation_rules",
    "attributes": {"name": "Production paging alerts"}
  }
}
```

On updates, an optional `data.id` must match the path ID. Don't send `data.id` on create. Unknown attributes, unsupported envelope keys, and invalid condition object shapes return 400. Unsupported fields or operators and validation failures return 422.

Example 422 response:

```json theme={null}
{
  "errors": [
    {"title": "conditions contain an unsupported field", "status": "422"}
  ]
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.