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

# Notifications

> Personal email preferences, the in-app inbox, and org-level Slack / generic webhook destinations for cluster, member, and billing events.

Radar Cloud has three notification surfaces:

* **In-app inbox** - the bell icon in the top nav. Free, per-user, automatic.
* **Personal email** - opt-in toggles per event type, scoped to the events that concern you (e.g. `cluster.disconnected` for a cluster you own).
* **Org destinations** - Slack and HMAC-signed webhooks for org-wide subscriptions. Owner-only.

## Event kinds

| Kind                        | Source                                                                                     | Default surfaces                                   |
| --------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------- |
| `cluster.disconnected`      | Tunnel drops past the flap-short threshold                                                 | Inbox + email (opt-in) + org destinations          |
| `cluster.reconnected`       | Tunnel comes back                                                                          | Inbox + org destinations                           |
| `org.member.invited`        | Invite sent                                                                                | Inbox + org destinations                           |
| `org.member.joined`         | User joined (invite or auto-join)                                                          | Inbox + org destinations                           |
| `billing.payment_failed`    | Stripe webhook                                                                             | Inbox (owners) + email (owners) + org destinations |
| `billing.free_tier_cap_hit` | 4th cluster create attempted on Free                                                       | Inbox (owners) + org destinations                  |
| `issue.opened`              | A live issue matches an [alert rule](/docs/cloud/alerts) and persists past its minimum duration | Inbox + rule's destinations                        |
| `issue.resolved`            | A matched issue clears past its resolve grace                                              | Inbox + rule's destinations                        |

Members don't see billing events in their inbox - that lane is owner-scoped.

The cluster, member, and billing events above are built-in and routed by per-destination kind filters. **Issue alerts work differently** - they're driven by [notification rules](/docs/cloud/alerts) you define over the Issues stream, with their own filtering, timing, and a durable [Alerts](/docs/cloud/alerts) history. The `issue.opened` / `issue.resolved` rows here are just where those alerts land as inbox and destination deliveries.

## In-app inbox

The bell icon shows unread count. Click it to open the inbox tray. Read state is per-user-per-event: marking read on your laptop syncs across your sessions but doesn't affect anyone else.

The inbox doesn't expire on its own today; deletion is manual. (Archive / snooze is on the roadmap.)

## Personal email preferences

**Settings → Preferences → Notifications**.

| Toggle                       | Default     | What it does                                               |
| ---------------------------- | ----------- | ---------------------------------------------------------- |
| Notify on cluster disconnect | off         | Email on `cluster.disconnected` for clusters you connected |
| Notify on payment failure    | on (owners) | Email on `billing.payment_failed`                          |

## Org destinations (Slack / webhook)

**Settings → Organization → Notifications → Add destination** (owner-only).

### Slack

We use Slack's incoming-webhook model. Click **Add destination → Slack**, paste a Slack webhook URL, and pick which event kinds the destination subscribes to. Test with the **Send test message** button.

The webhook URL is sealed at rest (NaCl `secretbox`, key from the control plane's `HUB_NOTIFY_KEY` env var). Owners only see a redacted view (`https://hooks.slack.com/services/T***/B***/...`) after creation.

### Generic webhook

For anything that isn't Slack - PagerDuty event API, Statuspage, your own incident tool. Click **Add destination → Webhook**:

* **URL** - the endpoint we POST to.
* **HMAC secret** (optional) - if set, every request includes `X-Radar-Signature: sha256=<hex>` over the raw body.
* **Event kinds** - the lane filter (empty = subscribe to everything).

Body shape:

```json theme={null}
{
  "event_kind": "cluster.disconnected",
  "org_id": "org_01HF...",
  "occurred_at": "2026-04-22T12:34:56Z",
  "data": {
    "cluster_id": "cl_01HF...",
    "cluster_name": "prod-us-east-1",
    "last_connected_at": "2026-04-22T12:30:00Z",
    "downtime_seconds": 296
  }
}
```

`data` is per-event-kind. Schemas live alongside the event-kind table on the [Audit log](/docs/cloud/audit-log) page.

### Verifying signatures

```bash theme={null}
echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET"
```

Match against the `X-Radar-Signature: sha256=<hex>` header. If they don't match, drop the request.

### Delivery

* Best-effort. Failed deliveries retry on a fixed backoff (3 attempts) and then drop.
* Each delivery records a row in `notification_deliveries` so you can see latency and outcome in the **Deliveries** tab of the destination.
* Strict per-destination kind filtering. If a destination subscribes to `["cluster.disconnected"]`, it never receives `cluster.reconnected`.

## Subscribing to specific clusters

Today the personal toggles are global ("notify me on any cluster disconnect"). Per-cluster filtering is on the roadmap - until then, use a webhook destination with kind filter + your own routing logic.

## See also

* [Audit log](/docs/cloud/audit-log) - the underlying event taxonomy.
* [Cluster token rotation](/docs/cloud/cluster-token) - rotating a token also fires a `cluster.token_rotated` event.
