---
title: "Beszel"
description: "Send Beszel server alerts to Uptimeify Incident Management through a Shoutrrr generic webhook, with host, link, severity and automatic recovery for every Beszel alert type."
---

[Beszel](https://beszel.dev) notifies through [Shoutrrr](https://beszel.dev/guide/notifications/) URLs. Its generic webhook sends only a title and a message, with no status field and no alert id. The Beszel preset reads everything it needs from the title: whether the alert fires or recovers, which system it is about, and which earlier alert a recovery closes. Checked against the alert texts of Beszel 0.21.

## 1. Create the alert source in Uptimeify

Go to **Incident Management → Alert Sources → New**, pick the **Beszel** preset, name it, and create it. The success screen shows a ready-made **notification URL for Beszel**:

```
generic://<your-domain>/api/im/ingest/<token>?template=json
```

It is shown once and never displayed again in cleartext. If you lose it, rotate the token from the source's **Settings** tab and build the URL again from the new ingest URL: replace `https://` with `generic://` and append `?template=json`.

`template=json` is required. Without it Beszel sends plain text instead of `{ "title", "message" }`, and the preset has nothing to read.

## 2. Add the URL in Beszel

1. In Beszel, open **Settings → Notifications**.
2. Under **Webhook / Push notifications**, add the URL from step 1 and save. **Test URL** sends a test notification. It arrives as an open alert titled `Test Alert`; resolve it by hand.
3. In the systems table, open the bell icon of each system and enable the alerts you want (status, CPU, memory, disk, and so on).

Beszel sends one notification when an alert fires and one when it recovers. Both reach the same Uptimeify alert, so the recovery resolves it.

## Supported alert types

| Beszel alert | Firing title | Recovery title |
|---|---|---|
| CPU, memory, disk, bandwidth, temperature, load, GPU | `web-01 CPU above threshold` | `web-01 CPU below threshold` |
| Battery (fires when low) | `web-01 battery below threshold` | `web-01 battery above threshold` |
| Status (connection) | `Connection to web-01 is down 🔴` | `Connection to web-01 is up ✅` |
| Container health | `Unhealthy container db on web-01 🔴` or `3 unhealthy containers on web-01 🔴` | `web-01 containers are healthy ✅` |
| systemd services | `Failed services on web-01 🔴` | `Services recovered on web-01 ✅` |
| Network monitor (packet loss) | `Network monitor loss on web-01: 1.1.1.1:53` | `Network monitor recovered on web-01: 1.1.1.1:53` |
| SMART | `SMART failure on web-01: /dev/sda 🔴` | none |
| ZFS pool | `Storage pool DEGRADED on web-01: tank` | none |

Beszel sends no recovery for SMART and ZFS. Those alerts stay open until you resolve them in Uptimeify. Several unhealthy containers on one system share one alert, because Beszel's recovery also covers the whole system.

## Default field mapping

| Beszel field | Normalized field |
|---|---|
| `title` | Title, verbatim |
| `title` | Status: `below threshold` (battery: `above threshold`), `is up`, `recovered` and `containers are healthy` resolve the alert, everything else keeps it open |
| `title` | Host: the Beszel system name, also when it contains spaces |
| `title` | Dedup key: system plus alert type, the same for the firing and the recovery notification |
| `message` | Link back to the system in Beszel (the URL Beszel appends to every message) |
| `message` | Custom field `details`, the message without the link (for example `CPU averaged 91.20% for the previous 10 minutes.`) |

### Severity map

| Beszel alert | Uptimeify severity |
|---|---|
| SMART failure, ZFS pool `FAULTED`, `OFFLINE`, `UNAVAIL`, `REMOVED` or `SUSPENDED` | `sev1` |
| Connection down, failed services, ZFS pool `DEGRADED` | `sev2` |
| SMART warning | `sev3` |
| Everything else | The source's default severity |

## Sample payload

```json
{
  "title": "backend1 CPU above threshold",
  "message": "CPU averaged 91.20% for the previous 10 minutes.\n\nhttps://beszel.example.com/system/x8k2m4p9q1w7e3r"
}
```

You can send this exact payload against your source's mapping from the **Payload Mapping** tab in the dashboard to verify the setup before enabling alerts in Beszel.

## Beszel by email instead

If Beszel can only reach you by email (SMTP under **Settings → Notifications**), use an [Email source](/api/incident-management/alert-sources/email) instead. Beszel's email subject is the same title, and the body is the message followed by the link. Set these `emailSelectors` through the [Alert Sources API](/api/incident-management/alert-sources-api) to get the same behavior as the webhook preset:

```json
{
  "emailSelectors": {
    "title": "subject",
    "severity": "subject =~ /^SMART (failure) on / || subject =~ /^Storage pool (FAULTED|OFFLINE|UNAVAIL|REMOVED|SUSPENDED) on / || subject =~ /^Connection to .+ is (down)\\b/ || subject =~ /^(Failed) services on / || subject =~ /^Storage pool (DEGRADED) on / || subject =~ /^SMART (warning) on /",
    "status": "subject =~ / (battery (?:above|below)) threshold/ || subject =~ / (above|below) threshold/ || subject =~ /^Connection to .+ is (down|up)\\b/ || subject =~ /^Network monitor (loss|recovered) on / || subject =~ /^Services (recovered) on / || subject =~ /containers are (healthy)\\b/",
    "host": "subject =~ /^(.+?) (?:\\d+m load|\\S+) (?:above|below) threshold/ || subject =~ /^Connection to (.+) is (?:down|up)\\b/ || subject =~ /^Network monitor (?:loss|recovered) on (.+?): / || subject =~ /^(?:Failed services|Services recovered) on (.+) \\S+$/ || subject =~ /^(?:Unhealthy container \\S+|\\d+ unhealthy containers) on (.+) \\S+$/ || subject =~ /^(.+) containers are healthy\\b/ || subject =~ /^SMART \\S+ on (.+?): / || subject =~ /^Storage pool \\S+ on (.+?): /",
    "dedupKey": "subject =~ /^(.+) (?:above|below) threshold/ || subject =~ /^(Connection to .+) is (?:down|up)\\b/ || subject =~ /^Network monitor (?:loss|recovered) (on .+)$/ || subject =~ /^(?:Failed services|Services recovered) (on .+) \\S+$/ || subject =~ /^(?:Unhealthy container \\S+|\\d+ unhealthy containers) on (.+) \\S+$/ || subject =~ /^(.+) containers are healthy\\b/ || subject =~ /^(SMART \\S+ on .+?) \\S+$/ || subject =~ /^(Storage pool .+)$/",
    "severityMap": { "failure": "sev1", "FAULTED": "sev1", "OFFLINE": "sev1", "UNAVAIL": "sev1", "REMOVED": "sev1", "SUSPENDED": "sev1", "down": "sev2", "Failed": "sev2", "DEGRADED": "sev2", "warning": "sev3" },
    "resolveValues": ["below", "battery above", "up", "recovered", "healthy"],
    "custom": { "details": "body =~ /^(.*?)\\s*https?:\\/\\/\\S+\\s*$/s || body" }
  }
}
```

The webhook preset is the better choice where it works: it also stores the Beszel link as the incident's link.
