---
title: "Update an observability integration"
method: PUT
path: "/api/v1/observability/integrations/{integration}"
tags: ["Observability integrations"]
---

# Update an observability integration

`PUT /api/v1/observability/integrations/{integration}`

## Request

- Base URL: `https://wardenpoint.com` (the document declares no server; this is the origin it was published from)
- URL: `https://wardenpoint.com/api/v1/observability/integrations/{integration}`
- Auth: API key in header `X-API-Key`

Updates an integration. `name`, `type` and `preset` are required on every call — this endpoint is a replace, not a merge, for those three. Fields with a stored value that you omit entirely are kept (`is_active`, `hmac_enabled`, `auto_cancel_on_recovery`, `accumulation_rules`, `field_mapping`, `inbox_owner_group_uuid`, `inbox_digest_interval`, the health-check fields, the email settings); sending them as null clears them. Changing `health_check_url` or `health_check_mode` resets the probe state so a stale up/down does not linger. The secret is untouched and never returned here; replacing it is a dashboard action (see the `secret` field of the integration schema). PATCH is accepted on the same path with identical semantics.

## Path parameters

- `integration` string, uuid, required

## Request body

- ObservabilityIntegrationRequest
  - `name` string, required: Display name.
  - `type` 'generic_webhook' | 'grafana' | 'prometheus_alertmanager' | 'datadog' | 'cloudwatch' | 'zabbix' | 'uptime_kuma' | 'sentry' | 'kibana' | 'github' | 'gitlab' | 'email', required: Payload shape the integration parses. `generic_webhook` accepts a flat JSON object with `external_alert_id`, `title`, `severity`, `status`, `source_url` (and can be re-pointed with `field_mapping`). `grafana` and `prometheus_alertmanager` accept the native Alertmanager webhook body. `datadog`, `cloudwatch` (SNS), `zabbix`, `uptime_kuma`, `sentry`, `kibana`, `github`, `gitlab` accept those tools' native webhook bodies (`gitlab` parses merge request events only and is available when the operator has enabled the GitLab integration). `email` has no webhook at all — it receives mail at `inbound_email_address` and is only available when the operator has enabled email-to-alert.
  - `preset` 'notify_every_alert' | 'burst_protection' | 'critical_only' | 'digest_only' | 'custom_rules', required: Starting accumulation policy, expanded into `accumulation_rules` on save. `notify_every_alert` — every occurrence notifies. `burst_protection` — notify below 5-in-10-minutes, escalate at the threshold, stay quiet after. `critical_only` — ignore everything but critical, escalate immediately. `digest_only` — stay quiet until 5 occurrences in 10 minutes, then one message. `custom_rules` — take `accumulation_rules` from the request instead. Send `notify_every_alert` if you want the very first alert to be delivered.
  - `is_active` boolean: Defaults to true on create — an integration created over the API is live immediately. Send false explicitly to create it switched off; while off the alert endpoint answers 403. Omitting it on update keeps the stored value.
  - `recipient_group_uuid` string, uuid, nullable: Group to page for alerts of this integration. ON CREATE this is not just a stored field: it also creates a real, visible routing rule named "Catch-all (migrated default)" that matches every alert and points at the group, so the integration pages somebody from the first alert on. That rule is listed by `GET /observability/integrations/{integration}/routing-rules` like any other and can be edited or deleted. It is always kept LAST, and every rule you add afterwards is inserted above it — a narrower rule added later still wins. ON UPDATE the field only re-points the group; no rule is created, and if the catch-all still exists it keeps pointing where it did. Omit or send null to create no rule at all, in which case unmatched alerts stay in the triage inbox and nobody is paged. The dashboard-only sentinel `__self__` is rejected here — an API key has no user to build a personal group from.
  - `hmac_enabled` boolean: Turn on signed requests. Doing so INVALIDATES the plain-secret header path — see the create endpoint description for the signature scheme. Defaults to false on create; omitting it on update keeps the stored value, so a partial update never quietly stops verifying signatures.
  - `auto_cancel_on_recovery` boolean: Cancel an in-flight escalation when the matching alert resolves. Defaults to true on create; omitting it on update keeps the stored value.
  - `accumulation_rules` AccumulationRule[], nullable: Explicit thresholds, replacing whatever `preset` would have produced. Send with `preset: custom_rules` when you want full control. On update: omitting the field while repeating the stored `preset` keeps the stored thresholds; sending a different `preset` regenerates them from that preset.
    - `threshold_count` integer: Occurrences needed to reach `threshold_action`.
    - `window_minutes` integer: Length of the counting window.
    - `below_threshold_action` 'suppress' | 'message' | 'escalate': Applied to occurrences before the threshold.
    - `threshold_action` 'suppress' | 'message' | 'escalate': Applied to the occurrence that hits the threshold.
    - `after_threshold_action` 'suppress' | 'message' | 'escalate': Applied to further occurrences in the same window — usually `suppress`, so one incident pages once.
    - `severities` string[]: Restrict the rule to these severities. Empty means any.
    - `statuses` string[]: Restrict the rule to these statuses. Empty means any.
  - `field_mapping` object, nullable: Generic webhook only: canonical field to dot-path in your JSON, e.g. `{"title": "alert.name", "severity": "labels.level"}`. Unmapped fields fall back to the default key chain. Omitting the field on update keeps the stored mapping; send the paths you want to drop as empty strings to clear it.
  - `inbox_owner_group_uuid` string, uuid, nullable: Group owning the triage inbox. Omitting the field on update keeps the stored value; sending null clears it.
  - `inbox_digest_interval` integer, nullable: Minutes between inbox digests. 0 disables.
  - `email_correlation` 'smart' | 'per_email', nullable: Email type only: `smart` collapses a firing message and its later recovery into one alert by normalised subject; `per_email` makes every message a standalone alert. Defaults to `smart`.
  - `email_sender_allowlist` string[], nullable: Email type only: allowed sender addresses or domains. Sending [] clears the list, omitting the field keeps it.
  - `health_check_mode` 'off' | 'active' | 'heartbeat', nullable: `off`, `active` (we GET `health_check_url`) or `heartbeat` (your tool calls us and silence means down).
  - `health_check_url` string, uri, nullable: Target of the active probe. Must resolve to a public address — private and loopback targets are rejected.
  - `health_heartbeat_grace_seconds` integer, nullable: Heartbeat mode: silence tolerated before the integration is called down. 30..86400, default 180.

## Response `200`

Updated integration.

- object
  - `data` ObservabilityIntegration: One inbound alert source. Alerts are POSTed to `endpoint_url` (or mailed to `inbound_email_address` for the email type) and turned into notifications according to `accumulation_rules` and the routing rules attached to the integration.
    - `uuid` string, uuid: Stable identifier — every integration endpoint takes this, and it is also the last path segment of `endpoint_url`.
    - `name` string: Display name.
    - `type` 'generic_webhook' | 'grafana' | 'prometheus_alertmanager' | 'datadog' | 'cloudwatch' | 'zabbix' | 'uptime_kuma' | 'sentry' | 'kibana' | 'github' | 'gitlab' | 'email': Which payload shape the integration parses. See the `type` field of the create request for what each one expects.
    - `is_active` boolean: False means the alert endpoint answers 403 and nothing is ingested.
    - `preset` string, nullable: The accumulation preset the integration was created with. Informational — `accumulation_rules` is what actually runs.
    - `endpoint_url` string, uri, nullable: Absolute URL alerts are POSTed to. Null for the email type, which has no webhook. Authenticate with the integration secret, see the create endpoint.
    - `metrics_url` string, uri: Absolute URL exposing this integration's alert counters in Prometheus text format.
    - `inbound_email_address` string, nullable: Managed mailbox alerts may be sent to. Non-null only for the email type.
    - `hmac_enabled` boolean: When true the endpoint stops accepting the bare secret header and requires a signed request instead.
    - `signature_tolerance_seconds` integer: How far the signed timestamp may drift from server time, and how long a signature is remembered as already-used. 300 seconds.
    - `auto_cancel_on_recovery` boolean: When true a `resolved` alert cancels the escalation its `firing` sibling started.
    - `recipient_group_uuid` string, uuid, nullable: The group this integration was created with. It is NOT a silent fallback: an alert no routing rule matched pages nobody and lands in the triage inbox regardless of this field. What it actually does is stand in for a matched rule that has no group of its own. Setting it at create time also materialises a catch-all routing rule pointing at it — see the create request.
    - `recipient_group_name` string, nullable
    - `inbox_owner_group_uuid` string, uuid, nullable: Group that owns the triage inbox for this integration.
    - `inbox_digest_interval` integer: Minutes between inbox digests. 0 disables them.
    - `accumulation_rules` AccumulationRule[]: The integration-wide fallback thresholds, expanded from `preset` at create time. A matching routing rule with its own thresholds overrides these.
      - `threshold_count` integer: Occurrences needed to reach `threshold_action`.
      - `window_minutes` integer: Length of the counting window.
      - `below_threshold_action` 'suppress' | 'message' | 'escalate': Applied to occurrences before the threshold.
      - `threshold_action` 'suppress' | 'message' | 'escalate': Applied to the occurrence that hits the threshold.
      - `after_threshold_action` 'suppress' | 'message' | 'escalate': Applied to further occurrences in the same window — usually `suppress`, so one incident pages once.
      - `severities` string[]: Restrict the rule to these severities. Empty means any.
      - `statuses` string[]: Restrict the rule to these statuses. Empty means any.
    - `field_mapping` object: Generic-webhook only: canonical field name to dot-path in the incoming JSON.
    - `email_correlation` string, nullable: Email type only: how inbound messages are grouped into one alert.
    - `email_sender_allowlist` string[]: Email type only: addresses or domains allowed to send. Empty means any sender.
    - `health_check_mode` 'off' | 'active' | 'heartbeat': off = no liveness check; active = we probe `health_check_url`; heartbeat = we expect the tool to call us.
    - `health_check_url` string, uri, nullable: The address probed in `active` mode. Null in the other two modes, where nothing is probed.
    - `health_heartbeat_grace_seconds` integer: Heartbeat mode: silence tolerated before the integration is called down.
    - `health_status` 'unknown' | 'up' | 'down': Liveness of the alert source itself, not of anything we deliver. `unknown` until the first check, or whenever health checking is off. Crossing into `down` pages the inbox-owner group once at `high` priority, and recovery pages once more — the state in between is silent. It never blocks ingest: a `down` integration whose alerts start arriving again is routed normally.
    - `last_alert_received_at` string, date-time, nullable: When the last alert arrived. Null means the integration has never been used — the first thing to check when nothing is paging.
    - `last_error` string, nullable: Last ingest error, if any.
    - `created_at` string, date-time
    - `updated_at` string, date-time
    - `routing_rules` AlertRoutingRule[]: Present on the single-integration response only, in priority order.
      - `uuid` string, uuid
      - `name` string, nullable: Label for humans. Optional.
      - `position` integer: Priority: 0 is evaluated first. Assigned automatically on create — appended last, except that a rule with no conditions (a catch-all) is never allowed to sit above a rule added after it: the new rule takes the catch-all's slot and the catch-all moves down. Change the order with the reorder endpoint.
      - `recipient_group_uuid` string, uuid, nullable: Group notified when the rule matches. Null means the rule matches but pages nobody — useful as a deliberate mute.
      - `target_kind` 'group' | 'event': Who the rule pages: `group` (a recipient group you name) or `event` (the people the alert itself names — a GitLab merge request lists its reviewers). `event` is accepted only for sources that name people; every other source rejects it with 422. A rule with `event` takes no `recipient_group_uuid`.
      - `addressee_set` 'all' | 'added' | 're_requested' | 'reviewed', nullable: Which of the people named by the event to page. Required when `target_kind` is `event`, otherwise null. `all` — every reviewer; `added` — only those this event added; `re_requested` — only those asked to review again; `reviewed` — only those who already looked at the diff. Anyone the event names but whose login is not bound to a recipient on the integration page is recorded there for binding, and an event where nobody is bound lands in the triage inbox with `addressee_unresolved`.
      - `recipient_group_name` string, nullable
      - `severities` string[]: Severities the rule matches. Empty means any.
      - `statuses` string[]: Statuses the rule matches. Empty means any.
      - `label_matchers` LabelMatcher[]: Label conditions, all of which must hold (AND).
        - `key` string, required: Label name as it arrives in the alert payload.
        - `op` '=' | '!=' | '=~' | '!~', required: Comparison: `=` equals, `!=` differs, `=~` matches the regex, `!~` does not match it.
        - `value` string, required: Value, or regex pattern for the `=~` and `!~` operators.
      - `threshold_count` integer, nullable: Per-rule override of the integration thresholds. Null falls back to the integration — but only while **all five** override fields are null; see below.
      - `window_minutes` integer, nullable: Length of the counting window. Null here does not always mean "inherits": the five override fields are read as one block, so if any sibling is set, this behaves as 1 minute instead of as the integration's window.
      - `below_threshold_action` 'suppress' | 'message' | 'escalate', nullable: What happens to occurrences before the threshold. Null behaves as the integration's default action once the override block is engaged.
      - `threshold_action` 'suppress' | 'message' | 'escalate', nullable: What happens at the threshold — the field that decides whether people are woken and how loudly: `escalate` pages at `critical` and starts the escalation chain, `message` pages at `normal` and starts none, `suppress` records silently. Null behaves as the integration's default action once the override block is engaged.
      - `after_threshold_action` 'suppress' | 'message' | 'escalate', nullable: What happens to further occurrences in the same window. Null behaves as `suppress` once the override block is engaged — not as the integration default — so a rule showing four nulls and one set field is quieter after its first page than it looks.
      - `created_at` string, date-time
      - `updated_at` string, date-time
    - `secret` string: The plain integration secret. Present ONLY in the create response — it is stored hashed and cannot be read back afterwards, and no API endpoint issues a replacement. Losing it means rotating the secret in the dashboard (Integrations → the integration → Rotate secret), which shows the new value to the person doing it. The previous secret keeps being accepted for a grace window after that rotation, so senders can be switched over without dropping alerts.

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks config.write, the integration belongs to another company, or `type: email` was requested while the operator has email-to-alert switched off.
- `404`: No integration with this UUID.
- `422`: Validation failed — see the create endpoint for the same rules.

---

[API](https://skmtc.dev/wardenpoint/apis/wardenpoint-api.md) · [All operations](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/llms.txt) · [OpenAPI document](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/revisions/259ee445728e?raw)
