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

# Configure outbound webhooks

> Send incident, maintenance, metric, agent, SLO, and SLA Ledger alerts to your own HTTPS endpoint as signed JSON.

Outbound webhooks deliver Observer alerts to an HTTPS endpoint as
JSON. They are the integration path for paging tools, ticketing
systems, and chat tools that Observer has no native integration for.

Webhooks are created on the **Alerts** page as a destination with the
**Generic webhook** channel, alongside the native channels (Slack,
Discord, Microsoft Teams, Telegram, PagerDuty, Jira, Splunk On-Call,
Grafana IRM, and email). Alert destinations require a Starter plan or
higher, and the number of destinations is capped per plan.

## Available event types

### Incidents

| Type | Console label | Trigger |
| - | - | - |
| `incident.published` | Incident published | An incident is published and becomes visible on the public page. |
| `incident.message_added` | Incident update posted | A new update is posted to a published incident. |
| `incident.resolved` | Incident resolved | An incident is resolved. |

### Maintenance

| Type | Console label | Trigger |
| - | - | - |
| `maintenance.scheduled` | Maintenance scheduled | A maintenance window is published. |
| `maintenance.starting_soon` | Maintenance starting soon | A scheduled maintenance window is about to start. |
| `maintenance.completed` | Maintenance completed | A maintenance window completes. |

### Metrics

| Type | Console label | Trigger |
| - | - | - |
| `metric.status_changed` | Metric status changed | A metric's status flips once its confirmation time has passed. |
| `metric.no_data` | Metric stopped reporting | A metric enters `no_data` because no sample could be collected. |

### Agent health (org-wide destinations only)

| Type | Console label | Trigger |
| - | - | - |
| `agent.offline` | Agent stopped reporting | No heartbeat from the agent for 180 seconds. The check runs every 2 minutes, so the alert arrives 3 to 5 minutes after the last heartbeat. |
| `agent.lag_high` | Agent falling behind | An agent's reporting lag is high. |
| `agent.uptime_degraded` | Agent uptime dropped | An agent's reporting uptime drops. |
| `agent.duplicate_key` | Two agents sharing one key | Two agent processes report with the same key. |

### Hosted checks (org-wide destinations only)

| Type | Console label | Trigger |
| - | - | - |
| `check.unmeasurable` | Blocked by bot protection | Every probe location gets a bot-protection challenge page instead of the site, so the hosted check cannot be measured. A second event with status `cleared` follows when a real result arrives. |

### SLOs (org-wide destinations only)

| Type | Console label | Trigger |
| - | - | - |
| `slo.burn_started` | SLO started using budget fast | An SLO starts burning its error budget. |
| `slo.burn_resolved` | SLO budget use back to normal | The burn ends. |

### SLA Ledger (org-wide destinations only)

| Type | Console label | Trigger |
| - | - | - |
| `sla_statement.ready_for_review` | Statement ready for review | A monthly SLA statement is ready for review. |
| `sla_statement.delivered` | Statement delivered | A statement is delivered. |

### Digest

`alert.digest` is sent once a day to destinations that use
**Daily digest instead of instant delivery**. It summarizes the
events that were batched for that destination.

## Create a generic webhook

**1. Add the destination**

In the console, open **Alerts**, then **Add destination**, and choose
**Generic webhook** under **Channel**. Under **Connection**, set:

* **Endpoint URL**: an `https://` URL on a publicly reachable host
  that accepts POST requests with a JSON body.
* **Signing secret** (optional): a shared secret. When set, Observer
  signs every delivery with HMAC-SHA-256 (see
  [Verify the signature](#verify-the-signature)).

Both values are stored encrypted. **Send test** sends one test
message with these details without saving.

**2. Choose alerts and routing**

Under **Alerts**, pick a preset:

* **Paging** (the default): incidents (published, updated, resolved)
  and metrics turning unhealthy.
* **Everything, daily digest**: every alert, batched into one summary
  a day.
* **Custom**: pick the alerts yourself. Custom also has the
  **Daily digest instead of instant delivery** switch.

Agent health, SLO, and SLA Ledger alerts are available only to
org-wide destinations.

Under **Routing**, set **Status page** to
**All status pages (organization-wide)** or a single page. You can also
narrow when the destination fires by **Services**, **Metrics**, and
**Minimum severity**, and choose whether to
**Alert during scheduled maintenance**. Recoveries and resolves always
deliver, so alerts your receiver opened can close.

Select **Add destination** to save.

**3. Send a test**

On the **Alerts** page, open the destination's actions menu and
choose **Send test**. Observer sends a test delivery and reports
whether it succeeded.

## Payload

By default each delivery is a JSON envelope:

```json theme={null}
{
  "event": "incident.published",
  "event_id": "4f7c2a9e-1b3d-4c8e-9a6f-2d5e7b8c9a10",
  "incident_id": "b1e2c3d4-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
  "org_id": "org_2abc...",
  "page": { "id": "e3f4...", "name": "Acme status", "subdomain": "acme" },
  "title": "Checkout API errors",
  "body": "Investigating elevated error rate on checkout.",
  "severity": "major",
  "status": "investigating",
  "url": "https://acme.use.observer/incidents",
  "occurred_at": "2026-06-01T02:14:07.000Z"
}
```

`event` is one of the event types above. `severity` and `status`
depend on the event and may be `null`. See the
[webhook payload reference](https://docs.use.observer/docs/reference/webhook-payloads.md) for
every field.

Each request is a `POST` with `Content-Type: application/json`.
Respond with a 2xx status within 10 seconds.

## Verify the signature

When a signing secret is set, every delivery includes three headers:

```text theme={null}
X-Observer-Signature:        sha256=<hex>
X-Observer-Signature-Legacy: sha256=<hex>
X-Observer-Timestamp:        <unix-millis>
```

The signed material for `X-Observer-Signature` is
`${timestamp}.${rawBody}`. To verify:

1. Read `X-Observer-Timestamp`. Reject the delivery if it is more
   than five minutes from the current time on the receiver. This
   skew window bounds replay attacks against captured deliveries.
2. Compute `HMAC-SHA-256(secret, "${timestamp}.${rawBody}")` and
   hex-encode the digest. Prepend `sha256=`.
3. Compare against `X-Observer-Signature` using a constant-time
   comparison (`crypto.timingSafeEqual` in Node, `hmac.compare_digest`
   in Python, equivalents elsewhere). String equality leaks
   information through timing side channels.

Reject deliveries whose signatures do not match.

> [!NOTE]
>
> **Legacy header**
>
> `X-Observer-Signature-Legacy` carries `HMAC-SHA-256(secret, rawBody)`
> for receivers that have not migrated to the timestamp-prefixed scheme.
> It is still sent for compatibility and will be removed in a future
> release. Use the timestamp-prefixed `X-Observer-Signature` for new
> receivers; receivers relying on `X-Observer-Signature-Legacy` lose
> replay protection.

## Retries and delivery history

A delivery that times out, hits a network error, or receives a 429
or 5xx response is retried with exponential backoff, up to five
attempts. Other 4xx responses are not retried.

**Latest alerts**, at the top of the **Alerts** page above
**Destinations**, shows each delivery's status (**Delivered**,
**Failed**, **Retrying**, or **Skipped**), destination, event, and
time. Open a row to see the
attempt number, HTTP status, error, and the stored payload. Failed
deliveries with a stored payload have a **Redeliver** button that
queues the same payload again.

## Legacy webhook subscriptions

Webhook subscriptions created on the old **Webhooks** settings page
still deliver for organisations that created them. That page now
redirects to **Alerts**, and new webhooks can only be created as
alert destinations. Legacy deliveries keep their original payload
shape; see the legacy section of the
[webhook payload reference](https://docs.use.observer/docs/reference/webhook-payloads.md).


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