> ## 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.

# Silences, inhibitions, and shelving

> Quiet nuisance alarms, planned maintenance, and cascading failures with shelving, label-matched silences, and inhibition rules.

Golain gives you four ways to quiet alarms. They differ in **what** they quiet and **for how long**:

* **Shelve** parks one alarm instance until a time you choose.
* A **silence** holds back notifications for every alarm whose labels match, for a time window.
* An **inhibition** holds back notifications for target alarms while a related source alarm is firing.
* **Deactivating** a definition stops the rule from being evaluated at all.

## Compare the options

| | Shelve | Silence | Inhibition | Deactivate definition |
| - | - | - | - | - |
| **Scope** | One alarm instance | Every instance in the Project whose labels match the matchers | Target instances that match, while a matching source instance is firing | Every instance of one definition |
| **What is suppressed** | Evaluation of that instance (no state changes) and its re-notify reminders | Notifications only. Instances still open, change state, and show on the **Board**. | Notifications only. Instances still open, change state, and show on the **Board**. | Evaluation of the rule |
| **How it ends** | Automatically at `shelved_until`, or when you unshelve | Automatically at `ends_at`, or when you disable or delete it | When no matching source is firing, or when you disable or delete the rule | When you activate the definition again |
| **Time limit** | The definition's `shelve_max_hours` (default `24`) | None — you set `starts_at` and `ends_at` | None | None |
| **Where** | **Alarms** → **Board**, or the instance page | **Alarms** → **Silences** | **Alarms** → **Silences** | **Alarms** → **Definitions** |
| **Permission** | Access to the Project's alarms | `can_manage_assets` on the Project | `can_manage_assets` on the Project | `can_manage_assets` on the Project |

> [!NOTE]
>
> Silences and inhibitions never stop an alarm from opening. The alarm opens, its history is recorded, and it appears on the **Board** — only the notification is held back. Use shelving when you want an instance to stop changing state.

***

## Shelve an alarm instance

Shelve an instance when you know about the problem and are working on it, and you do not want reminders or state changes in the meantime.

While an instance is shelved:

* Golain does not evaluate it, so it does not fire, resolve, or change state.
* Re-notify reminders pause. They resume on the next interval after the shelf ends. See [Notifications](/alarms/notifications).
* The instance stays open and appears on the **Board** with a **Shelved** badge.
* Its acknowledgement state does not change. An unacknowledged alarm stays unacknowledged.

You can shelve only an **open, alarming** instance — one that is pending or firing. Closed or resolved instances return `only active alarm instances can be shelved`.

### Shelve from the Console

**1. Open the alarm**

Go to **Alarms** → **Board**. Click **Shelve** on the alarm's row, or open the instance and click **Shelve** in the header.

**2. Pick a duration**

In **Shelve this alarm**, choose **30 min**, **1 h**, **4 h**, **End of shift**, or **Custom** (minutes). Presets longer than the definition's limit are hidden. The dialog shows **This alarm allows up to …** with the limit.

**3. Add a reason**

Enter a **Reason** so the next person knows what is being done. It is recorded on the instance with your name.

**4. Confirm**

Click **Shelve for …**. If the alarm is still unacknowledged, you can click **Acknowledge and shelve** instead to do both.

To end a shelf early, click **Unshelve** on the **Board** row or the instance page.

### Shelve with the API

```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=…
export INSTANCE_ID=…    # Alarm instance UUID
```

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

| Field | Type | Required | Description |
| - | - | - | - |
| `shelved_until` | string | Yes | RFC 3339 timestamp, for example `2026-10-05T18:00:00Z`. Must be in the future and no later than now plus the definition's `shelve_max_hours`. |
| `shelve_reason` | string | No | Free-text reason stored on the instance |

```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-05T18:00:00Z",
    "shelve_reason": "Replacing the pressure transmitter on line 2"
  }' \
  "$API/projects/$PROJECT_ID/alarm-instances/$INSTANCE_ID/shelve"
```

The response is the updated instance under `data.item`, with `shelved_at`, `shelved_until`, `shelved_by`, and `shelve_reason` set.

To unshelve, send an empty `POST`:

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

Unshelving clears all four shelve fields. Unshelving an instance that is not shelved returns `alarm instance is not shelved`.

### Shelve limit and auto-unshelve

| Setting | Behavior |
| - | - |
| `shelve_max_hours` on the definition | The furthest ahead you can shelve, in hours from now. Defaults to `24`. Set it when you create or update the definition (**Max shelve** in the Console editor). |
| Request beyond the limit | Rejected with `shelved_until exceeds shelve_max_hours (N)` |
| `shelved_until` in the past or now | Rejected with `shelved_until must be in the future` |
| Shelf expiry | Evaluation resumes as soon as `shelved_until` passes. Shortly after, Golain unshelves the instance automatically and clears the shelve fields — you do not need to do anything. |

***

## Silences

A **silence** holds back notifications for every alarm whose labels match a set of **matchers**, during a time window. Use silences for planned maintenance, commissioning, or any known period when alarms are expected.

### How silences apply

* A silence applies when it is enabled (`is_active` is `true`) and the current time is between `starts_at` and `ends_at`, inclusive.
* Golain checks silences when an alarm **fires** and when it **resolves**. If any active silence matches, that notification is not delivered.
* Golain also checks silences before each **re-notify reminder**. A silenced reminder is skipped, and reminders resume once the silence ends.
* A silence created after an alarm has already fired does not recall the notification that was already sent. It does stop later reminders.
* A new or edited silence can take up to about 15 seconds to take effect.

### Matcher syntax

A matcher set is a JSON array of objects. Every matcher must match (AND).

```json theme={null}
[
  { "name": "site", "op": "=", "value": "pune-2" },
  { "name": "line", "op": "=~", "value": "line-[1-4]" }
]
```

| Field | Description |
| - | - |
| `name` | Label key to test. Must not be empty. |
| `op` | One of the four operators below |
| `value` | String to compare, or a regular expression for `=~` and `!~` |

| Operator | Matches when |
| - | - |
| `=` | The label value equals `value` |
| `!=` | The label value does not equal `value` |
| `=~` | The label value fully matches the regular expression |
| `!~` | The label value does not fully match the regular expression |

Rules to remember:

* **Regular expressions are fully anchored.** `prod` matches only `prod`, not `production`. Use `prod.*` to match both.
* **A missing label counts as an empty string.** `site=""` matches alarms with no `site` label, and `site!=pune-2` also matches alarms with no `site` label.
* **Label values are compared as text.** Numbers and booleans in an instance's labels are matched by their string form, for example `"42"` or `"true"`.
* An invalid operator or regular expression is rejected when you save, with `invalid matchers: …`.

> [!WARNING]
>
> An empty matcher array (`[]`) matches **every** alarm in the Project. The Console requires at least one matcher, but the API accepts an empty array. Double-check API requests before you create a silence.

### Labels you can match on

Matchers test the instance's **labels** — the same key/value pairs returned in the instance's `labels` field and shown under **Labels** on the instance page. They do **not** include the definition's `key`, `name`, `kind`, or `priority`.

| Alarm source | Labels on the instance |
| - | - |
| `threshold` rule with `labels_by` | One label per column listed in `labels_by`, with that row's value |
| `threshold` rule without `labels_by` | No labels |
| `staleness` (device offline) rule | `device_id` |
| `event` rule bound to an asset | `asset_id` |
| `event` rule for the whole Project | `alarm_def_id` |

If you are unsure, the instance's `labels` field is always the source of truth.

> [!TIP]
>
> To target alarms by site, line, or gateway, include those columns in the rule's `labels_by` so they become labels. Before creating a silence, open a matching instance and copy the exact keys and values from its **Labels**. See [Kinds and conditions](/alarms/kinds-and-conditions).

> [!NOTE]
>
> The Console pre-fills new silence and inhibition forms with example matcher rows such as `alarm` or `kind`. Replace them with label keys your instances actually carry, or the silence will not match.

### Manage silences in the Console

**1. Open Silences**

Go to **Alarms** → **Silences**. The page lists **Silences** and **Inhibitions**.

**2. Create a silence**

Click **New silence**. Add one or more **Matchers**, then set **Starts** and **Ends** (the window defaults to now plus four hours).

**3. Add a reason**

Enter a **Reason** (required). Optionally fill **Approved by**.

**4. Save**

Click **Create silence**.

Each silence row shows its matchers, window, reason, author, and a state badge:

| Badge | Meaning |
| - | - |
| **Silencing** | Enabled, and the window is open now |
| **Scheduled** | Enabled, and the window starts in the future |
| **Expired** | The window has ended |
| **Disabled** | `is_active` is `false` |

Use **Edit**, **Disable** / **Enable**, and **Delete** on each row. After you delete a silence, notifications it was holding back resume on the next evaluation.

### Manage silences with the API

| Method | Path | Purpose |
| - | - | - |
| `GET` | `/projects/{project_id}/alarm-silences` | List silences. Query: `is_active`, `ids` (comma-separated UUIDs), `page`, `limit` (default `25`, max `100`). |
| `POST` | `/projects/{project_id}/alarm-silences` | Create a silence |
| `GET` | `/projects/{project_id}/alarm-silences/{silence_id}` | Get one silence |
| `PATCH` | `/projects/{project_id}/alarm-silences/{silence_id}` | Update fields listed in `_keys` |
| `DELETE` | `/projects/{project_id}/alarm-silences/{silence_id}` | Delete a silence |

All paths are under `https://api.ilyama.golain.io/core/api/v1`. Reads need `can_read_assets` on the Project. Create, update, and delete need `can_manage_assets`.

**Create body:**

| Field | Type | Required | Description |
| - | - | - | - |
| `matchers` | array | Yes | Matcher set (see [Matcher syntax](#matcher-syntax)) |
| `starts_at` | string | Yes | RFC 3339 start time |
| `ends_at` | string | Yes | RFC 3339 end time. Must be after `starts_at`. |
| `reason` | string | Yes | Why the silence exists. Must not be blank. |
| `approved_by` | string | No | Who approved it |
| `is_active` | boolean | No | Defaults to `true` |
| `metadata` | object | No | Free-form JSON. Defaults to `{}`. |

```bash theme={null}
curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "matchers": [
      { "name": "site", "op": "=", "value": "pune-2" },
      { "name": "line", "op": "=~", "value": "line-[1-4]" }
    ],
    "starts_at": "2026-10-06T02:00:00Z",
    "ends_at": "2026-10-06T06:00:00Z",
    "reason": "Planned oven refractory work, lines 1-4",
    "approved_by": "Shift supervisor"
  }' \
  "$API/projects/$PROJECT_ID/alarm-silences"
```

The response returns the stored silence under `data.item`:

```json theme={null}
{
  "ok": 1,
  "data": {
    "item": {
      "id": "…",
      "matchers": [
        { "name": "site", "op": "=", "value": "pune-2" },
        { "name": "line", "op": "=~", "value": "line-[1-4]" }
      ],
      "starts_at": "2026-10-06T02:00:00Z",
      "ends_at": "2026-10-06T06:00:00Z",
      "reason": "Planned oven refractory work, lines 1-4",
      "approved_by": "Shift supervisor",
      "created_by": "…",
      "is_active": true,
      "metadata": {}
    }
  }
}
```

**Update** sends only the fields you want to change, and lists their names in `_keys` (required). For example, to end a silence early by disabling it:

```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"
```

Updatable keys: `matchers`, `starts_at`, `ends_at`, `reason`, `approved_by`, `is_active`, `metadata`.

**Delete** returns `{"ok": 1}`:

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

***

## Inhibitions

An **inhibition** holds back notifications for **target** alarms while a related **source** alarm is firing. Use it to cut cascade noise — for example, when a gateway goes down, mute the alarms from every sensor behind that gateway.

### How inhibitions apply

When a target alarm fires or resolves, Golain mutes its notification if all of these are true:

1. The inhibition is enabled (`is_active` is `true`).
2. The target alarm's labels match `target_matchers`.
3. At least one other alarm in the Project is currently **firing** and its labels match `source_matchers`.
4. The source and target have the same value for every label in `equal_labels`. A label missing from both counts as equal. If `equal_labels` is empty, any matching source mutes any matching target.

```mermaid theme={null}
flowchart LR
  SRC["Source alarm firing<br/>gateway_id = gw-7"] -->|matches source_matchers| RULE[Inhibition]
  TGT["Target alarm fires<br/>gateway_id = gw-7"] -->|matches target_matchers| RULE
  RULE -->|equal_labels agree| MUTE[Notification held back]
```

Keep these behaviors in mind:

* The source must be **firing** at the moment the target fires or resolves. If the target fires first, its notification is sent.
* An alarm never inhibits itself or a peer through a symmetric rule. If a target also matches the source side, sources that also match the target side are ignored.
* Inhibitions apply to the fire and resolve notifications. They do not pause re-notify reminders — use a silence or shelve the instance for that.
* Inhibitions have no time window. They stay in effect until you disable or delete them.

### Manage inhibitions in the Console

Inhibitions live on the same page as silences.

**1. Open Silences**

Go to **Alarms** → **Silences** and click **New inhibition**.

**2. Set the source**

Under **While a source fires**, add matchers for the alarm that indicates the root cause.

**3. Set the targets**

Under **Mute targets**, add matchers for the alarms to mute.

**4. Join on labels**

In **When these labels match**, enter comma-separated label keys, for example `gateway_id, site`. Leave it empty to let any source mute any target.

**5. Save**

Optionally add a **Reason**, then click **Create inhibition**.

Each inhibition row shows **While**, **Mute**, and **When these match**, with an **Armed** or **Disabled** badge. Use **Edit**, **Disable** / **Enable**, and **Delete** on each row.

### Manage inhibitions with the API

| Method | Path | Purpose |
| - | - | - |
| `GET` | `/projects/{project_id}/alarm-inhibitions` | List inhibitions. Query: `is_active`, `ids`, `page`, `limit`. |
| `POST` | `/projects/{project_id}/alarm-inhibitions` | Create an inhibition |
| `GET` | `/projects/{project_id}/alarm-inhibitions/{inhibition_id}` | Get one inhibition |
| `PATCH` | `/projects/{project_id}/alarm-inhibitions/{inhibition_id}` | Update fields listed in `_keys` |
| `DELETE` | `/projects/{project_id}/alarm-inhibitions/{inhibition_id}` | Delete an inhibition |

Permissions match silences: `can_read_assets` to read, `can_manage_assets` to change.

**Create body:**

| Field | Type | Required | Description |
| - | - | - | - |
| `source_matchers` | array | Yes | Matcher set for the source alarm |
| `target_matchers` | array | Yes | Matcher set for the alarms to mute |
| `equal_labels` | array of strings | No | Label keys that must agree between source and target. Defaults to `[]`. |
| `reason` | string | No | Why the rule exists |
| `is_active` | boolean | No | Defaults to `true` |
| `metadata` | object | No | Free-form JSON. Defaults to `{}`. |

```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_DEVICE_ID"'" }
    ],
    "target_matchers": [
      { "name": "site", "op": "=", "value": "pune-2" }
    ],
    "equal_labels": [],
    "reason": "A gateway down makes every sensor alarm behind it noise"
  }' \
  "$API/projects/$PROJECT_ID/alarm-inhibitions"
```

The response returns the stored rule under `data.item`. Updates use `PATCH` with `_keys`, and the updatable keys are `source_matchers`, `target_matchers`, `equal_labels`, `reason`, `is_active`, and `metadata`. `DELETE` returns `{"ok": 1}`.

In this example, while the gateway's device offline alarm is firing, notifications are muted for alarms labeled `site=pune-2` (for example, a threshold rule with `site` in its `labels_by`).

> [!TIP]
>
> `equal_labels` joins source and target only on label keys they both carry. A device offline alarm carries only `device_id`, so when the source is a device offline rule, scope the targets with matchers, as in the example, rather than `equal_labels`.

***

## Acknowledge versus shelve

Acknowledging and shelving do different jobs. You often want both.

| | Acknowledge | Shelve |
| - | - | - |
| **Means** | "I have seen this alarm." | "Stop evaluating and reminding me about this alarm until a set time." |
| **Ack state** | Sets `ack_state` to `acknowledged` | Unchanged |
| **Re-notify reminders** | Stop for good | Pause until the shelf ends |
| **Evaluation** | Continues. The alarm can still resolve or fire. | Paused until `shelved_until` |
| **Ends** | Does not expire | Automatically at `shelved_until`, or on unshelve |
| **API** | `POST …/alarm-instances/{id}/acknowledge` (no body) | `POST …/alarm-instances/{id}/shelve` |

> [!WARNING]
>
> Shelving does not acknowledge an alarm. An unacknowledged alarm stays counted as unacknowledged on the **Board** and in its history while shelved. Use **Acknowledge and shelve** in the Console, or call both endpoints, when you want both.

## Next steps

* [Notifications](/alarms/notifications) — subscriptions, re-notify reminders, and escalation
* [Console guide](/alarms/console-guide) — the **Board**, **Definitions**, and **Silences** pages
* [Kinds and conditions](/alarms/kinds-and-conditions) — `labels_by` and how instances get their labels
* [API reference](/alarms/api-reference) — every alarm endpoint
* [Troubleshooting](/alarms/troubleshooting) — alarms that do not notify or do not match


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