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

# Alarms API reference

> HTTP routes for alarm definitions, alarm instances, silences, and inhibitions, with permissions, request fields, and response shapes.

The Alarms API lives on the management API. Every route is scoped to a Project. Set these before running the examples:

```bash theme={null}
export API=https://api.ilyama.golain.io/core/api/v1
export TOKEN=…          # OIDC access token (same as Console)
export ORG_ID=…         # Organization UUID
export PROJECT_ID=…
```

| Resource | Base path | Purpose |
| - | - | - |
| **Definitions** | `/projects/{project_id}/alarm-definitions` | Author, activate, and deactivate alarm rules |
| **Instances** | `/projects/{project_id}/alarm-instances` | List, acknowledge, shelve, and unshelve alarms that fired |
| **Silences** | `/projects/{project_id}/alarm-silences` | Mute notifications for alarms whose labels match, for a time window |
| **Inhibitions** | `/projects/{project_id}/alarm-inhibitions` | Mute notifications for target alarms while a source alarm is firing |

All path IDs are UUIDs.

## Authentication

Every route requires:

| Header | Value |
| - | - |
| `Authorization` | `Bearer {oidc_access_token}` |
| `ORG-ID` | Active organization UUID |

Requests with a body also need `Content-Type: application/json`.

On the three create routes (definition, silence, inhibition) you can send an **`Idempotency-Key`** header. A retry with the same key returns the result of the first request instead of creating a duplicate. Create responses also carry an `X-Command-Id` header.

## Permissions

Every route requires you to be a member of the Project. Some routes check one more permission:

| Operation | Permission |
| - | - |
| `GET` definitions, silences, inhibitions | `can_read_assets` on the Project |
| `GET` instances | `can_read_assets` on the Project, **or** `can_read` on the instance's asset |
| `POST`, `PATCH`, `DELETE` definitions, including `…/activate` and `…/deactivate` | `can_manage_assets` on the Project |
| `POST`, `PATCH`, `DELETE` silences and inhibitions | `can_manage_assets` on the Project |
| `POST …/acknowledge`, `…/shelve`, `…/unshelve` | Project member |

List reads are filtered by permission. Items you cannot read are left out of the list. A single item you cannot read returns `404`.

## Rate limits

| Operation | Limit |
| - | - |
| Reads (`GET`) | 64 req/s |
| Creates (`POST` to a collection) | 12 req/s |
| Updates, deletes, and actions (`PATCH`, `DELETE`, `activate`, `deactivate`, `acknowledge`, `shelve`, `unshelve`) | 32 req/s |

Back off on `429`.

## Response envelope

Single objects come back under `data.item`:

```json theme={null}
{ "ok": 1, "data": { "item": { "id": "…" } } }
```

Lists come back under `data.items`, with pagination in `meta`:

```json theme={null}
{
  "ok": 1,
  "data": { "items": [ … ] },
  "meta": {
    "current_page": 1,
    "items_per_page": 25,
    "total_items": 26,
    "total_pages": 2
  }
}
```

Every list route accepts:

| Query parameter | Default | Description |
| - | - | - |
| `page` | `1` | 1-based page number |
| `limit` | `25` | Page size, max `100`. `page_size` is accepted as an alias. |
| `ids` | — | Comma-separated UUIDs. Returns only those items. |

> [!NOTE]
>
> `total_items` is not an exact count. When a page comes back full, it reports one more than the items seen so far, so `total_pages` tells you another page exists. Keep paging until a page returns fewer than `limit` items.

Delete routes return `{ "ok": 1 }` with no `data`.

### Errors

Errors use this envelope with an HTTP 4xx or 5xx status:

```json theme={null}
{
  "ok": 0,
  "message": "shelved_until must be in the future",
  "detail": "…"
}
```

| Status | When |
| - | - |
| `400` | Body fails to parse, has an unknown field, misses a required field, or fails validation. Also an invalid query filter, a `PATCH` without `_keys`, or a query the dataspace check rejects. |
| `401` | Missing or invalid token |
| `403` | You lack the permission for the route, or the rule's author cannot read the dataspace named in `query_spec.sql` |
| `404` | The item does not exist in this Project, or you cannot read it |
| `409` | A definition with the same `key` already exists in the Project, or a create with the same `Idempotency-Key` is still in progress |
| `503` | The definition's query could not be checked because the dataspace query service is unavailable. The definition is not saved. Retry. |
| `504` | The request timed out. The change may still have been applied. Read the item before you retry. |

Request bodies are strict. A field that is not listed for the route returns `400`.

***

## Definitions

### List definitions

```
GET /core/api/v1/projects/{project_id}/alarm-definitions
```

**Permission:** `can_read_assets` on the Project

| Query parameter | Description |
| - | - |
| `search` | Case-insensitive substring match on `key` or `name` |
| `is_active` | `true` or `false` |
| `asset_id` | Definitions bound to this asset |
| `asset_type_id` | Definitions bound to this asset type |
| `source_dataspace_id` | Definitions reading this dataspace |
| `page`, `limit`, `ids` | See [Response envelope](#response-envelope) |

Results are sorted by `key`.

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-definitions?is_active=true&search=boiler"
```

**Response:** `data.items[]` of [definition objects](#definition-object), plus `meta`.

***

### Create a definition

```
POST /core/api/v1/projects/{project_id}/alarm-definitions
```

**Permission:** `can_manage_assets` on the Project

A new definition is active immediately.

```bash Threshold theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "key": "boiler-overtemp",
    "name": "Boiler over temperature",
    "kind": "threshold",
    "priority": 2,
    "source_dataspace_id": "8b0f6d0e-2a43-4c55-9b1f-6a2d1e7c9a10",
    "query_spec": {
      "kind": "sql",
      "sql": "SELECT device_id, avg(temperature) AS value FROM ds_read_3f9c2a71 WHERE event_ts >= now() - INTERVAL 5 MINUTE GROUP BY device_id"
    },
    "condition_cel": "event.value > 80.0",
    "resolve_cel": "event.value < 70.0",
    "labels_by": ["device_id"],
    "eval_interval_seconds": 60,
    "for_seconds": 120,
    "keep_firing_for_seconds": 120,
    "on_no_data": "keep_firing",
    "response_procedure": "Check the burner and the feed-water pump."
  }' \
  "$API/projects/$PROJECT_ID/alarm-definitions"
```

```bash Device offline theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "device-offline",
    "name": "Device offline",
    "kind": "staleness",
    "condition_cel": "true",
    "staleness_window_seconds": 1200,
    "on_no_data": "resolve",
    "response_procedure": "Check the device power and its gateway link."
  }' \
  "$API/projects/$PROJECT_ID/alarm-definitions"
```

`condition_cel` is required for every kind. `staleness` rules do not evaluate it, so send `"true"`.

**Request fields:**

| Field | Type | Required | Description |
| - | - | - | - |
| `key` | string | Yes | Identifier, unique within the Project |
| `name` | string | Yes | Display name |
| `condition_cel` | string | Yes | CEL expression that fires the alarm. For `threshold`, it runs against each result row as `event`. |
| `kind` | string | No | `event` (default), `threshold`, or `staleness` |
| `description` | string | No | Free text |
| `priority` | integer | No | Default `3`. Lower numbers are more urgent. |
| `consequence` | string | No | What happens if nobody responds |
| `response_procedure` | string | No | What the operator should do |
| `safety_classification` | string | No | Free-text classification |
| `asset_id` | UUID | No | Bind the definition to one asset |
| `asset_type_id` | UUID | No | Bind the definition to an asset type |
| `source_dataspace_id` | UUID | `threshold`: Yes | Dataspace the rule reads |
| `query_spec` | object | `threshold`: Yes | `{ "kind": "sql", "sql": "SELECT …" }`. See [Query spec](/alarms/kinds-and-conditions#query-spec). |
| `resolve_cel` | string | No | CEL expression that resolves the alarm |
| `clear_cel` | string | No | CEL clear expression for `event` rules |
| `labels_by` | string\[] | No | Result columns that split a `threshold` rule into one instance per distinct value, for example `["device_id"]` |
| `eval_interval_seconds` | integer | No | How often the rule runs. Default `60` when unset. Not used by `staleness` rules. |
| `eval_delay_seconds` | integer | No | Default `0`. Extra wait for late-arriving data. |
| `for_seconds` | integer | No | Default `0`. How long the condition must hold before the alarm fires. |
| `keep_firing_for_seconds` | integer | No | Default `0`. How long the resolve condition must hold before the alarm resolves. |
| `on_delay_seconds` | integer | No | Default `0`. Fire delay used when `for_seconds` is `0`. |
| `off_delay_seconds` | integer | No | Default `0`. Resolve delay used when `keep_firing_for_seconds` is `0`. |
| `deadband` | number | No | Hysteresis band for clearing `event` rules |
| `staleness_window_seconds` | integer | `staleness`: Yes | Must be greater than `0`. How long a device must be offline before the alarm fires. |
| `on_no_data` | string | No | `resolve`, `keep_firing` (default), or `alert`. What happens to an open instance when the query returns no row for it. |
| `latching` | boolean | No | Default `false`. When `true`, a firing instance moves to `latched` instead of `resolved` when it clears, and stays open. |
| `re_alarm_delay_seconds` | integer | No | Default `300`. Minimum gap before the same alarm can open again. |
| `shelve_max_hours` | integer | No | Default `24`. Longest an operator can shelve an instance of this alarm. |
| `repeat_interval_seconds` | integer | No | Default `0` (off). Re-notify interval while the instance is firing and unacknowledged. |
| `escalation_after_seconds` | integer | No | Default `0` (off). Escalate after the instance has been unacknowledged this long. |
| `receivers` | string\[] | No | Receivers recorded on the alarm event |
| `escalation_receivers` | string\[] | No | Receivers added to the alarm event on escalation |
| `auto_create_wo` | boolean | No | Default `false`. Open a work order ticket when the alarm fires. |
| `wo_workflow_def_id` | UUID | No | Ticket workflow for the work order |
| `wo_dedup_key_cel` | string | No | CEL expression for the work order dedup key |
| `wo_cooldown` | string | No | Default `24h`. Minimum time between work orders. Go duration (`24h`) or ISO 8601 (`PT24H`). |
| `labels_cel`, `annotations_cel` | string | No | Stored with the definition. Not evaluated today. |
| `metadata` | object | No | Free-form JSON. Default `{}`. |

Validation rules for `kind`, `query_spec`, the relative time window in the SQL, and `on_no_data` are covered in [Kinds and conditions](/alarms/kinds-and-conditions). Re-notify and escalation are covered in [Notifications](/alarms/notifications).

**Response:** `data.item` — the new [definition object](#definition-object).

> [!NOTE]
>
> The Console's **Test** button on a condition runs a dry run that has no public endpoint yet. When you create or update a definition through the API, the query is still checked against the dataspace before the definition is saved.

***

### Get a definition

```
GET /core/api/v1/projects/{project_id}/alarm-definitions/{alarm_def_id}
```

**Permission:** `can_read_assets` on the Project

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-definitions/$ALARM_DEF_ID"
```

#### Definition object

Every field from the create table comes back, plus:

| Field | Type | Description |
| - | - | - |
| `id` | UUID | Definition ID |
| `org_id`, `project_id` | UUID | Scope |
| `is_active` | boolean | Whether the rule is evaluated |
| `created_by` | string | Subject ID of the author |
| `created_at`, `updated_at` | timestamp | RFC 3339 |

Unset optional fields come back as `null`. `wo_cooldown` comes back as a duration string such as `"24h0m0s"`.

```json theme={null}
{
  "ok": 1,
  "data": {
    "item": {
      "id": "5d2c8f3e-7b1a-4e9c-a0f2-3c6d8e1b4a77",
      "project_id": "c1a7e0b2-…",
      "org_id": "9e4f2d10-…",
      "asset_id": null,
      "asset_type_id": null,
      "key": "boiler-overtemp",
      "name": "Boiler over temperature",
      "description": null,
      "priority": 2,
      "consequence": null,
      "response_procedure": "Check the burner and the feed-water pump.",
      "safety_classification": null,
      "condition_cel": "event.value > 80.0",
      "clear_cel": null,
      "source_dataspace_id": "8b0f6d0e-2a43-4c55-9b1f-6a2d1e7c9a10",
      "deadband": null,
      "on_delay_seconds": 0,
      "off_delay_seconds": 0,
      "re_alarm_delay_seconds": 300,
      "shelve_max_hours": 24,
      "auto_create_wo": false,
      "wo_workflow_def_id": null,
      "wo_dedup_key_cel": null,
      "wo_cooldown": "24h0m0s",
      "is_active": true,
      "metadata": {},
      "created_by": "user-subject-id",
      "created_at": "2026-10-05T09:12:40.118Z",
      "updated_at": "2026-10-05T09:12:40.118Z",
      "kind": "threshold",
      "query_spec": {
        "kind": "sql",
        "sql": "SELECT device_id, avg(temperature) AS value FROM ds_read_3f9c2a71 WHERE event_ts >= now() - INTERVAL 5 MINUTE GROUP BY device_id"
      },
      "resolve_cel": "event.value < 70.0",
      "for_seconds": 120,
      "keep_firing_for_seconds": 120,
      "eval_delay_seconds": 0,
      "eval_interval_seconds": 60,
      "staleness_window_seconds": null,
      "labels_by": ["device_id"],
      "labels_cel": null,
      "annotations_cel": null,
      "latching": false,
      "on_no_data": "keep_firing",
      "repeat_interval_seconds": 0,
      "escalation_after_seconds": 0,
      "escalation_receivers": null,
      "receivers": null
    }
  }
}
```

***

### Update a definition

```
PATCH /core/api/v1/projects/{project_id}/alarm-definitions/{alarm_def_id}
```

**Permission:** `can_manage_assets` on the Project

Send the fields to change, plus `_keys`: an array naming each field to apply. Only fields listed in `_keys` are changed. Fields in the body but not in `_keys` are ignored. A `PATCH` without `_keys` returns `400`.

You can update any field from the [create table](#create-a-definition). The merged result is validated the same way as a create. The query is checked against the dataspace again only when `kind` or `query_spec` is in `_keys`.

```bash theme={null}
curl -sS -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "condition_cel": "event.value > 85.0",
    "for_seconds": 300,
    "_keys": ["condition_cel", "for_seconds"]
  }' \
  "$API/projects/$PROJECT_ID/alarm-definitions/$ALARM_DEF_ID"
```

**Response:** `data.item` — the updated [definition object](#definition-object).

***

### Delete a definition

```
DELETE /core/api/v1/projects/{project_id}/alarm-definitions/{alarm_def_id}
```

**Permission:** `can_manage_assets` on the Project

```bash theme={null}
curl -sS -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-definitions/$ALARM_DEF_ID"
```

**Response:** `{ "ok": 1 }`

> [!TIP]
>
> To stop a rule but keep its history, [deactivate](#activate-or-deactivate-a-definition) it instead.

***

### Activate or deactivate a definition

```
POST /core/api/v1/projects/{project_id}/alarm-definitions/{alarm_def_id}/activate
POST /core/api/v1/projects/{project_id}/alarm-definitions/{alarm_def_id}/deactivate
```

**Permission:** `can_manage_assets` on the Project

No request body. A deactivated rule is not evaluated. Activating a rule also clears any automatic pause the platform applied after the rule repeatedly failed to evaluate.

```bash theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-definitions/$ALARM_DEF_ID/deactivate"
```

**Response:** `data.item` — the [definition object](#definition-object) with the new `is_active`.

***

## Instances

An instance is one occurrence of an alarm. A `threshold` rule with `labels_by` has one instance per distinct label value. A `staleness` rule has one instance per offline device.

### List instances

```
GET /core/api/v1/projects/{project_id}/alarm-instances
```

**Permission:** `can_read_assets` on the Project, or `can_read` on the instance's asset

| Query parameter | Description |
| - | - |
| `process_state` | `normal` or `active` |
| `ack_state` | `unacknowledged` or `acknowledged` |
| `alarm_def_id` | Instances of one definition |
| `asset_id` | Instances on one asset |
| `page`, `limit`, `ids` | See [Response envelope](#response-envelope) |

Results are sorted newest first by `opened_at`.

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-instances?alarm_def_id=$ALARM_DEF_ID&ack_state=unacknowledged"
```

**Response:** `data.items[]` of [instance objects](#instance-object), plus `meta`.

***

### List active instances

```
GET /core/api/v1/projects/{project_id}/alarm-instances/active
```

**Permission:** same as [List instances](#list-instances)

Returns only instances with `process_state` `active`. Accepts `ack_state`, `alarm_def_id`, `asset_id`, `page`, `limit`, and `ids`.

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-instances/active?ack_state=unacknowledged"
```

***

### Get an instance

```
GET /core/api/v1/projects/{project_id}/alarm-instances/{alarm_instance_id}
```

**Permission:** same as [List instances](#list-instances)

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-instances/$ALARM_INSTANCE_ID"
```

#### Instance object

| Field | Type | Description |
| - | - | - |
| `id` | UUID | Instance ID |
| `alarm_def_id` | UUID | Definition that raised it |
| `asset_id` | UUID or `null` | Asset, when the definition is bound to one |
| `org_id`, `project_id` | UUID | Scope |
| `process_state` | string | `active` or `normal` |
| `ack_state` | string | `unacknowledged` or `acknowledged` |
| `condition_state` | string | `pending`, `firing`, `resolved`, or `latched` |
| `labels` | object | Identity of this instance, for example `{ "device_id": "…" }` |
| `labelset_hash` | string | Stable key for `labels` |
| `trigger_value` | number or `null` | Numeric `value` from the row that fired the alarm, when present |
| `trigger_event` | object or `null` | Data that triggered the alarm |
| `opened_at` | timestamp | When the instance opened |
| `active_since` | timestamp or `null` | When the condition started holding |
| `clear_since` | timestamp or `null` | When the resolve condition started holding |
| `cleared_at` | timestamp or `null` | When the process state returned to `normal` |
| `closed_at` | timestamp or `null` | When the instance closed |
| `acknowledged_at`, `acknowledged_by` | timestamp, string | Who acknowledged it, and when |
| `shelved_at`, `shelved_until`, `shelved_by`, `shelve_reason` | timestamp, timestamp, string, string | Shelving details. `null` when not shelved. |
| `last_eval_at` | timestamp or `null` | Last evaluation that touched this instance |
| `last_transition_seq` | integer | Sequence number of the latest state change |
| `renotify_count` | integer | Re-notifications sent |
| `escalation_tier` | integer | `0`, or `1` once escalated |
| `linked_ticket_ids` | UUID\[] or `null` | Linked tickets, such as auto-created work orders |
| `metadata` | object | Free-form JSON |
| `created_at`, `updated_at` | timestamp | RFC 3339 |

```json theme={null}
{
  "ok": 1,
  "data": {
    "item": {
      "id": "a3f1c9d2-6e4b-4b8a-9c11-0d7e2f5b8c34",
      "alarm_def_id": "5d2c8f3e-7b1a-4e9c-a0f2-3c6d8e1b4a77",
      "asset_id": null,
      "project_id": "c1a7e0b2-…",
      "org_id": "9e4f2d10-…",
      "process_state": "active",
      "ack_state": "unacknowledged",
      "opened_at": "2026-10-05T10:04:00.512Z",
      "acknowledged_at": null,
      "acknowledged_by": null,
      "cleared_at": null,
      "shelved_at": null,
      "shelved_until": null,
      "shelved_by": null,
      "shelve_reason": null,
      "trigger_value": 86.4,
      "trigger_event": { "device_id": "boiler-02", "value": 86.4 },
      "linked_ticket_ids": null,
      "metadata": {},
      "created_at": "2026-10-05T10:04:00.512Z",
      "updated_at": "2026-10-05T10:04:00.512Z",
      "condition_state": "firing",
      "labelset_hash": "6b1f…",
      "labels": { "device_id": "boiler-02" },
      "last_transition_seq": 2,
      "last_eval_at": "2026-10-05T10:04:00.000Z",
      "active_since": "2026-10-05T10:02:00.000Z",
      "clear_since": null,
      "closed_at": null,
      "renotify_count": 0,
      "escalation_tier": 0
    }
  }
}
```

The two-axis lifecycle and what each state means are covered in [Alarms overview](/alarms/overview).

***

### Acknowledge an instance

```
POST /core/api/v1/projects/{project_id}/alarm-instances/{alarm_instance_id}/acknowledge
```

**Permission:** Project member

No request body. The caller is recorded as `acknowledged_by`. Acknowledging stops re-notification for the instance.

```bash theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-instances/$ALARM_INSTANCE_ID/acknowledge"
```

**Response:** `data.item` — the [instance object](#instance-object) with `ack_state` `acknowledged`.

***

### Shelve an instance

```
POST /core/api/v1/projects/{project_id}/alarm-instances/{alarm_instance_id}/shelve
```

**Permission:** Project member

Shelving pauses an active instance until `shelved_until`. It unshelves automatically at that time.

| Field | Type | Required | Description |
| - | - | - | - |
| `shelved_until` | string | Yes | RFC 3339 timestamp. Must be in the future and no later than `shelve_max_hours` from now (default 24 hours). |
| `shelve_reason` | string | No | Why you shelved it |

```bash theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "shelved_until": "2026-10-05T14:00:00Z",
    "shelve_reason": "Burner maintenance in progress"
  }' \
  "$API/projects/$PROJECT_ID/alarm-instances/$ALARM_INSTANCE_ID/shelve"
```

Returns `400` when the instance is not `active`, when `shelved_until` is in the past, or when it exceeds `shelve_max_hours`.

**Response:** `data.item` — the [instance object](#instance-object) with the shelving fields set.

***

### Unshelve an instance

```
POST /core/api/v1/projects/{project_id}/alarm-instances/{alarm_instance_id}/unshelve
```

**Permission:** Project member

No request body. Returns `400` `alarm instance is not shelved` when the instance is not shelved.

```bash theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-instances/$ALARM_INSTANCE_ID/unshelve"
```

**Response:** `data.item` — the [instance object](#instance-object).

→ Operator guide: [Silences and shelving](/alarms/silences-and-shelving)

***

## Matchers

Silences and inhibitions select alarm instances by their `labels` with an array of matchers. An instance matches when every matcher in the array holds.

```json theme={null}
[
  { "name": "device_id", "op": "=~", "value": "boiler-.*" },
  { "name": "site", "op": "!=", "value": "lab" }
]
```

| `op` | Matches when the label value |
| - | - |
| `=` | Equals `value` |
| `!=` | Does not equal `value` |
| `=~` | Fully matches the regular expression in `value` |
| `!~` | Does not fully match the regular expression in `value` |

`name` must not be empty. A missing label counts as an empty string. An invalid `op` or regular expression returns `400`.

> [!WARNING]
>
> An empty array `[]` matches every alarm in the Project.

***

## Silences

A silence mutes notifications for matching alarms between `starts_at` and `ends_at`. The alarms still fire and show on the Board.

### List silences

```
GET /core/api/v1/projects/{project_id}/alarm-silences
```

**Permission:** `can_read_assets` on the Project

Query parameters: `is_active` (`true` or `false`), `page`, `limit`, `ids`. Results are sorted by `starts_at`, newest first.

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-silences?is_active=true"
```

***

### Create a silence

```
POST /core/api/v1/projects/{project_id}/alarm-silences
```

**Permission:** `can_manage_assets` on the Project

| Field | Type | Required | Description |
| - | - | - | - |
| `matchers` | array | Yes | [Matchers](#matchers) |
| `starts_at` | string | Yes | RFC 3339 timestamp |
| `ends_at` | string | Yes | RFC 3339 timestamp, after `starts_at` |
| `reason` | string | Yes | Must not be blank |
| `approved_by` | string | No | Who approved the silence |
| `is_active` | boolean | No | Default `true` |
| `metadata` | object | No | Free-form JSON |

```bash theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "matchers": [
      { "name": "device_id", "op": "=", "value": "boiler-02" }
    ],
    "starts_at": "2026-10-05T12:00:00Z",
    "ends_at": "2026-10-05T16:00:00Z",
    "reason": "Planned boiler service"
  }' \
  "$API/projects/$PROJECT_ID/alarm-silences"
```

**Response** (`data.item`):

| Field | Type | Description |
| - | - | - |
| `id` | UUID | Silence ID |
| `org_id`, `project_id` | UUID | Scope |
| `matchers` | array | As sent |
| `starts_at`, `ends_at` | timestamp | Window |
| `reason` | string | Reason |
| `approved_by` | string or `null` | Approver |
| `created_by` | string | Subject ID of the creator |
| `is_active` | boolean | Whether the silence applies |
| `metadata` | object | Free-form JSON |
| `created_at`, `updated_at` | timestamp | RFC 3339 |

```json theme={null}
{
  "ok": 1,
  "data": {
    "item": {
      "id": "e7b2a1c4-0d3f-4a6e-8b9c-1f2e3d4c5b6a",
      "org_id": "9e4f2d10-…",
      "project_id": "c1a7e0b2-…",
      "matchers": [{ "name": "device_id", "op": "=", "value": "boiler-02" }],
      "starts_at": "2026-10-05T12:00:00Z",
      "ends_at": "2026-10-05T16:00:00Z",
      "reason": "Planned boiler service",
      "approved_by": null,
      "created_by": "user-subject-id",
      "is_active": true,
      "metadata": {},
      "created_at": "2026-10-05T10:30:11.204Z",
      "updated_at": "2026-10-05T10:30:11.204Z"
    }
  }
}
```

***

### Get, update, or delete a silence

```
GET    /core/api/v1/projects/{project_id}/alarm-silences/{silence_id}
PATCH  /core/api/v1/projects/{project_id}/alarm-silences/{silence_id}
DELETE /core/api/v1/projects/{project_id}/alarm-silences/{silence_id}
```

**Permission:** `GET` needs `can_read_assets`. `PATCH` and `DELETE` need `can_manage_assets`.

`PATCH` accepts any create field plus the required `_keys` array. To end a silence early, turn it off:

```bash theme={null}
curl -sS -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false, "_keys": ["is_active"] }' \
  "$API/projects/$PROJECT_ID/alarm-silences/$SILENCE_ID"
```

`GET` and `PATCH` return the silence in `data.item`. `DELETE` returns `{ "ok": 1 }`.

***

## Inhibitions

An inhibition mutes notifications for alarms that match `target_matchers` while an alarm that matches `source_matchers` is firing. Use `equal_labels` to require that source and target share the same values for those labels.

### List inhibitions

```
GET /core/api/v1/projects/{project_id}/alarm-inhibitions
```

**Permission:** `can_read_assets` on the Project

Query parameters: `is_active` (`true` or `false`), `page`, `limit`, `ids`. Results are sorted by `created_at`, newest first.

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  "$API/projects/$PROJECT_ID/alarm-inhibitions"
```

***

### Create an inhibition

```
POST /core/api/v1/projects/{project_id}/alarm-inhibitions
```

**Permission:** `can_manage_assets` on the Project

| Field | Type | Required | Description |
| - | - | - | - |
| `source_matchers` | array | Yes | [Matchers](#matchers) for the firing alarm that inhibits |
| `target_matchers` | array | Yes | [Matchers](#matchers) for the alarms to mute |
| `equal_labels` | string\[] | No | Labels that must have the same value on source and target. Default `[]`. |
| `reason` | string | No | Why the rule exists |
| `is_active` | boolean | No | Default `true` |
| `metadata` | object | No | Free-form JSON |

```bash theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "source_matchers": [
      { "name": "device_id", "op": "=~", "value": "gateway-.*" }
    ],
    "target_matchers": [
      { "name": "device_id", "op": "=~", "value": "sensor-.*" }
    ],
    "equal_labels": ["site"],
    "reason": "A gateway outage takes its sensors offline"
  }' \
  "$API/projects/$PROJECT_ID/alarm-inhibitions"
```

**Response** (`data.item`):

| Field | Type | Description |
| - | - | - |
| `id` | UUID | Inhibition ID |
| `org_id`, `project_id` | UUID | Scope |
| `source_matchers`, `target_matchers` | array | As sent |
| `equal_labels` | string\[] | Shared labels |
| `is_active` | boolean | Whether the rule applies |
| `reason` | string or `null` | Reason |
| `created_by` | string | Subject ID of the creator |
| `metadata` | object | Free-form JSON |
| `created_at`, `updated_at` | timestamp | RFC 3339 |

```json theme={null}
{
  "ok": 1,
  "data": {
    "item": {
      "id": "0c9d8e7f-6a5b-4c3d-2e1f-0a9b8c7d6e5f",
      "org_id": "9e4f2d10-…",
      "project_id": "c1a7e0b2-…",
      "source_matchers": [{ "name": "device_id", "op": "=~", "value": "gateway-.*" }],
      "target_matchers": [{ "name": "device_id", "op": "=~", "value": "sensor-.*" }],
      "equal_labels": ["site"],
      "is_active": true,
      "reason": "A gateway outage takes its sensors offline",
      "created_by": "user-subject-id",
      "metadata": {},
      "created_at": "2026-10-05T10:41:02.877Z",
      "updated_at": "2026-10-05T10:41:02.877Z"
    }
  }
}
```

***

### Get, update, or delete an inhibition

```
GET    /core/api/v1/projects/{project_id}/alarm-inhibitions/{inhibition_id}
PATCH  /core/api/v1/projects/{project_id}/alarm-inhibitions/{inhibition_id}
DELETE /core/api/v1/projects/{project_id}/alarm-inhibitions/{inhibition_id}
```

**Permission:** `GET` needs `can_read_assets`. `PATCH` and `DELETE` need `can_manage_assets`.

`PATCH` accepts any create field plus the required `_keys` array:

```bash theme={null}
curl -sS -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{ "equal_labels": ["site", "line"], "_keys": ["equal_labels"] }' \
  "$API/projects/$PROJECT_ID/alarm-inhibitions/$INHIBITION_ID"
```

`GET` and `PATCH` return the inhibition in `data.item`. `DELETE` returns `{ "ok": 1 }`.

***

## Notification events

Alarm state changes can be delivered through notification subscriptions. Subscribe to these event keys:

| Event key | Sent when |
| - | - |
| `alarm.alarm_instance.activated.v1` | An instance becomes active (firing) |
| `alarm.alarm_instance.cleared.v1` | An instance returns to normal |
| `alarm.alarm_instance.acknowledged.v1` | An operator acknowledges an instance |

Silences and inhibitions mute these notifications for matching alarms. → Full guide: [Notifications](/alarms/notifications)

## Related

> **[Alarms overview](/alarms/overview)**
>
> Concepts, kinds, lifecycle, and priority

> **[Kinds and conditions](/alarms/kinds-and-conditions)**
>
> Event, threshold, and staleness rules, SQL, CEL, and timing

> **[Silences and shelving](/alarms/silences-and-shelving)**
>
> Shelve instances, silence and inhibit notifications

> **[Troubleshooting](/alarms/troubleshooting)**
>
> When alarms do not fire, resolve, or notify


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