DumbMonit notifications
This guide explains how to receive DumbMonit alerts on the service of your choice. It requires no programming skills: each section says where to find the address or token to copy, and what to type into the form.
Contents
Section titled “Contents”- How it works
- Creating and testing a channel
- Settings or secrets: the difference
- Self-signed certificates
- Acknowledge or snooze from the notification
- The custom channel
- Supported channels
- Troubleshooting
How it works
Section titled “How it works”A channel is a destination: a Discord channel, a phone number, a mailbox. You create as many as you want, and your alert rules choose which ones to notify.
Three principles apply to every channel:
- Alerts are grouped. One message per monitor and per cycle, even if five disks fill up at the same time. Without this, you would mute notifications within a week.
- A broken channel never blocks another one. The failure is recorded in the channel’s “last error” column, and delivery continues to the others.
- A service that does not answer is abandoned after 15 seconds. A notification that arrives a minute late is no longer useful, and the monitoring loop must not wait behind an unreachable server.
Creating and testing a channel
Section titled “Creating and testing a channel”- Open Settings → Notifications → Add channel (
/settings/notifications). - Give it a meaningful name (“Discord — family”, “SMS — on-call”). This is the name you will pick later in your alert rules.
- Choose the channel type, then fill in the fields described below.
- Save, then click Send test.
The test message takes exactly the same path as a real alert: same request, same token, same formatting. If it arrives, the channel works. If it fails, the error message names the setting to fix.
A token is never shown again. Once saved, it is encrypted in the database and the interface only says “configured”. To edit a channel without retyping its token, simply leave the field empty.
Settings or secrets: the difference
Section titled “Settings or secrets: the difference”Every channel has two groups of fields.
| Settings | Secrets | |
|---|---|---|
| Content | server address, channel name, topic | tokens, passwords, webhook URLs |
| Storage | plain text | encrypted |
| Read back by the interface | yes | never |
The interface refuses to save a sensitive field among the settings, and tells you so. This is not a formality: settings are returned as-is by the API, so a token stored there would be published every time the page is opened.
The tables in the Supported channels section say, for each channel, what goes where.
Self-signed certificates
Section titled “Self-signed certificates”By default DumbMonit refuses any server whose certificate it cannot verify. For a
self-hosted service served with a self-signed certificate or one from a private
authority, the channels for Matrix, Mattermost, Rocket.Chat, Zulip, ntfy, Gotify,
Bark, Apprise, Home Assistant, Signal and the custom webhook have an Accept an
unverifiable certificate setting (insecure_tls, a boolean, off by default).
Turning it on keeps the connection encrypted but stops checking the identity of the server, so use it only for a server you control, on a network you trust. When a certificate is the reason for a failure, the error shown for the channel names it (for example “invalid peer certificate: UnknownIssuer”).
Acknowledge or snooze from the notification
Section titled “Acknowledge or snooze from the notification”When a public URL is set (Settings → Notifications → Notification policy, or
DUMBMONIT_PUBLIC_URL), every alert message carries three actions:
- Dismiss: “got it”. The alert leaves the active list and nothing more is sent for it, not even its resolution, until the condition clears; if it comes back later, it alerts again. It comes first, so it is the one kept where only two buttons fit (Web Push in most browsers).
- Acknowledge: no more reminders until the alert resolves. The resolution is still announced.
- Snooze 1 h: no reminders for one hour; they resume afterwards if the alert is still active. A snooze never shortens a longer acknowledgement.
How they show up depends on the channel:
| Channel | Shown as |
|---|---|
| ntfy, Telegram, Web Push | Buttons under the notification |
| Discord, Slack, email and every other channel | Links at the end of the message |
| Custom webhook | Links in {{message}}, and the {{dismiss_url}} / {{ack_url}} / {{snooze_url}} variables |
| PagerDuty, Opsgenie, Grafana OnCall, SIGNL4 | Nothing: acknowledge in the service itself |
| Jira, GLPI, Zammad, GitHub Issues, GitLab Issues | Nothing: the acknowledgement is added to the ticket as a comment |
| Vonage, Telnyx, ASPSMS (SMS) | Nothing: a signed link would cost several SMS segments |
Telegram only accepts buttons pointing to an https address with a domain name;
with an IP address or a plain http URL, the actions stay as links in the text.
Web Push only shows the buttons on devices of operators and admins: a viewer sees
the alert, not a way to silence it.
A link opens a short confirmation page in DumbMonit: nothing happens until you press the button. Mail clients, chat apps and antivirus scanners open links to build previews; they only load the page. No sign-in is needed: the link itself is the permission, and only for that gesture, on the alerts of that message (that firing: if an alert resolves and fires again, the old link no longer covers it), once, for 24 hours. The action is recorded in the audit log, and the alert shows “acknowledged by via notification link” (or “dismissed by via notification link”).
Security, in short: each link is signed with a key derived from the instance secret
(changing the secret invalidates every link), carries no session, and travels after
the # of the address, which browsers never send to a server, so it appears in no
access or proxy log. A forged, altered or expired link gets the same answer as any
other invalid link, and repeated attempts from one address are slowed down.
Turn Acknowledge and snooze from notifications off in the notification policy to stop adding them; this also invalidates the links already sent. Clearing the public URL has the same effect.
The custom channel
Section titled “The custom channel”The webhook channel is DumbMonit’s escape hatch. You describe the whole HTTP
request — method, address, headers, content type and body —, which lets you plug in
almost any service, including an internal API DumbMonit will never know about.
Without any particular configuration, it already sends a structured JSON payload that a script can use:
{ "source": "dumbmonit", "target": "nas", "rule": "Disk full", "severity": "warning", "status": "firing", "title": "nas — Disk full", "text": "⚠️ Warning · Disk full — /data — 95 % (threshold > 90 %)", "value": 95.0, "threshold": 90.0, "unit": "%", "fingerprint": "nas/disk-full", "count": 1, "at": "2026-09-01T14:12:05+00:00", "link": "https://dumbmonit.home/targets/42"}link points to the monitor page, built on the public_url of the
notification policy (or DUMBMONIT_PUBLIC_URL), or on the channel’s
base_url. A message about several monitors, and the test message, link to
the instance itself. Without any public URL, link is null.
Custom channel settings
Section titled “Custom channel settings”| Setting | Required | Default | Role |
|---|---|---|---|
url | yes (here or as a secret) | — | Address to call. May contain variables. |
method | no | POST | POST, PUT or GET. |
content_type | no | json | json, form or text. Also determines how variables are escaped. |
body_template | no | — | Body template. When absent, the payload above is sent. |
base_url | no | — | Public address of your DumbMonit, so that {{link}} gives a clickable link. |
username | no | — | Username for HTTP “Basic” authentication. Requires the password secret. |
| Secret | Role |
|---|---|
url | Use it instead of the url setting if the address already contains a token. |
token | Sent as an Authorization: Bearer … header, unless your template uses {{token}}. |
password | Password for “Basic” authentication. |
headers | Extra headers, as a JSON object {"X-Api-Key": "value"}. Stored encrypted like the token and never shown again, so API keys and Authorization headers belong here. |
headers is a secret. Channels saved by 0.1.0-alpha.6 or earlier, which kept them in
the settings, have them moved to the encrypted secrets at the first start,
and the API no longer returns them. Like every secret, they are kept when you edit the
channel without typing them again; sending secrets replaces the whole set.
DumbMonit follows a redirect only to the same host (or from http to https on that
host): a channel’s headers and token never go anywhere but the address you entered.
Available variables
Section titled “Available variables”A variable is written between double braces: {{title}}. Spaces around the name
are tolerated ({{ title }}). Single braces in a JSON body stay literal, so you can
write your JSON normally.
| Variable | What it contains | Example |
|---|---|---|
{{title}} | Full message title | nas — Disk full |
{{message}} | Plain-text body, one line per alert | ⚠️ Warning · Disk full — /data — 95 % (threshold > 90 %) |
{{message_markdown}} | The same body in Markdown | **nas — Disk full**\n• ⚠️ … |
{{rule}} | Name of the alert rule | Disk full |
{{target}} | Name of the monitor | nas |
{{target_id}} | Numeric identifier of the monitor | 42 |
{{severity}} | Technical severity | info, warning or critical |
{{severity_fr}} | Severity as a readable word | information, avertissement, critique |
{{status}} | Technical status | firing or resolved |
{{status_fr}} | Status as a readable word | en cours or résolu |
{{value}} | Measured value, unit included | 95 % |
{{value_raw}} | Bare value, usable as a JSON number | 95 |
{{threshold}} | Crossed threshold, unit included | 90 % |
{{threshold_raw}} | Bare threshold, usable as a JSON number | 90 |
{{unit}} | Unit of the value | % |
{{operator}} | Threshold comparison | > |
{{count}} | Number of alerts grouped in this message | 3 |
{{fingerprint}} | Stable identifier of the alert | nas/disk-full |
{{timestamp}} | ISO 8601 timestamp | 2026-09-01T14:12:05+00:00 |
{{timestamp_unix}} | Timestamp in seconds since 1970 | 1788358325 |
{{date}} | Readable timestamp, in UTC | 2026-09-01 14:12 UTC |
{{priority}} | Priority from 1 (low) to 5 (high) | 4 |
{{color}} | Accent colour, without hash | D98A00 |
{{color_hex}} | Accent colour, with hash | #D98A00 |
{{emoji}} | Status pictogram | 🔴, ⚠️, ℹ️ or ✅ |
{{link}} | Link to the monitor in DumbMonit | https://dumbmonit.home/targets/42 |
{{source}} | Always dumbmonit | dumbmonit |
{{token}} | The channel’s token secret | (your token) |
{{ack_url}} | Signed link that acknowledges the alert until it resolves, empty if action links are off | https://dumbmonit.home/alert-action#… |
{{snooze_url}} | Signed link that snoozes the alert for one hour, empty if action links are off | https://dumbmonit.home/alert-action#… |
{{dismiss_url}} | Signed link that dismisses the alert (hidden and silent until it resolves), empty if action links are off | https://dumbmonit.home/alert-action#… |
Two useful remarks:
- Some variables can be empty. An availability alert (“host unreachable”) has
neither a value nor a threshold:
{{value}}and{{value_raw}}then render an empty string, nevernullorNaN. If you place them as a JSON number, handle that case — or use"{{value}}"between quotes. {{link}}stays empty until you fill inbase_url. A wrong link would waste more time than an alert without one.
You never need to escape anything yourself. Depending on the chosen
content_type, DumbMonit automatically protects substituted values: quotes and line
breaks escaped in JSON, percent-encoding in a form, no transformation in plain text.
A rule named "System" disk full will not break your JSON template.
Example 1 — an internal JSON API
Section titled “Example 1 — an internal JSON API”A home-grown application exposes POST /incidents and expects a token in a custom
header.
Settings
{ "url": "https://api.internal.example.org/incidents", "method": "POST", "content_type": "json", "base_url": "https://dumbmonit.home", "body_template": "{\"title\": \"{{title}}\", \"device\": \"{{target}}\", \"severity\": \"{{severity}}\", \"state\": \"{{status}}\", \"value\": {{value_raw}}, \"threshold\": {{threshold_raw}}, \"detail\": \"{{message}}\", \"link\": \"{{link}}\", \"seen_at\": \"{{timestamp}}\"}"}Secrets
{ "token": "your-application-token", "headers": "{\"X-Application\": \"dumbmonit\"}"}The token is sent automatically as an Authorization: Bearer … header. Here is what
the application receives:
{ "title": "nas — Disk full", "device": "nas", "severity": "warning", "state": "firing", "value": 95, "threshold": 90, "detail": "⚠️ Warning · Disk full — /data — 95 % (threshold > 90 %)", "link": "https://dumbmonit.home/targets/42", "seen_at": "2026-09-01T14:12:05+00:00"}Example 2 — a service that expects a form
Section titled “Example 2 — a service that expects a form”Many SMS gateways and older APIs expect application/x-www-form-urlencoded rather
than JSON, with the token in the body.
Settings
{ "url": "https://gateway.example.org/api/send", "method": "POST", "content_type": "form", "body_template": "key={{token}}&recipient=0612345678&subject={{title}}&text={{emoji}} {{message}}&priority={{priority}}"}Secrets
{ "token": "gateway-key" }Since the template already uses {{token}}, DumbMonit does not add an
Authorization header: some services reject a request that carries two
authentications. Values are encoded automatically, spaces becoming + and accented
characters their percent-encoded equivalent.
Example 3 — a plain-text SMS through a carrier
Section titled “Example 3 — a plain-text SMS through a carrier”Some services, such as a carrier’s SMS API or a home-made automation, want a single line of text.
Settings
{ "url": "https://automation.home/alert", "method": "POST", "content_type": "text", "body_template": "{{emoji}} {{severity}} on {{target}}: {{rule}} ({{value}}, threshold {{operator}} {{threshold}}) — {{date}}"}The message sent, without any escaping:
⚠️ warning on nas: Disk full (95 %, threshold > 90 %) — 01/09/2026 14:12 UTCExample 4 — a GET request without a body
Section titled “Example 4 — a GET request without a body”When the service only accepts URL parameters, put the variables in url and choose
the GET method. A GET request has no body: body_template is then rejected, so
that nobody believes it does something.
Settings
{ "url": "https://sms.example.org/send?user=noe&text={{title}} — {{value}}", "method": "GET"}Secrets
{ "token": "api-key" }Only the substituted values are encoded; the rest of the address is copied as-is. The request actually sent looks like:
GET https://sms.example.org/send?user=noe&text=nas%20%E2%80%94%20Disk%20full%20%E2%80%94%2095%20%25Common template mistakes
Section titled “Common template mistakes”A faulty template is rejected when saved, while you are looking at the form, not on the night the incident happens.
| What you write | What happens |
|---|---|
{{severite}} | Rejected: unknown variable “severite”, followed by the list of valid names. The correct name is severity. |
{"t": "{{title"} | Rejected: “unclosed braces”. The }} is missing. |
{{}} or {{ }} | Rejected: a variable without a name means nothing. |
{"a": {"b": 1}} | Accepted. Single braces are literal: write your JSON normally. |
{{token}} without a token secret | Rejected: the template asks for a token the channel does not have. |
method: GET with body_template | Rejected, with the alternative: put the variables in url. |
method: DELETE | Rejected: only POST, PUT and GET are accepted. |
Supported channels
Section titled “Supported channels”Discord
Section titled “Discord”In Discord: Channel settings → Integrations → Webhooks → New webhook, then Copy webhook URL. This URL contains the token: it goes into the secrets.
- Secrets —
webhook_url
On https://api.slack.com/apps, create an app, enable Incoming Webhooks, then
Add New Webhook to Workspace and pick the channel. Copy the
https://hooks.slack.com/services/… URL.
- Secrets —
webhook_url
Telegram
Section titled “Telegram”You need two things: the bot token (secret) and the chat ID (chat_id)
where it must write. The first takes a minute to get; the second depends on where
you want to receive the alerts.
1. Create the bot and get its token
Section titled “1. Create the bot and get its token”-
In Telegram, search for @BotFather and click Start.
-
Send
/newbot, then answer the questions (a name, then a username that must end withbot). -
BotFather ends with a message like this:
Done! Congratulations on your new bot. You will find it at t.me/my_homelab_bot.Use this token to access the HTTP API:63xxxxxx71:AAFoxxxxn0hwA-2TVSxxxNf4cThe line
63xxxxxx71:AAFoxxxxn0hwA-2TVSxxxNf4cis the bot token. Do not share it: it gives full control of the bot.
In the examples below, replace <TOKEN> with this token. Note the bot prefix
glued in front of it in URLs: https://api.telegram.org/bot<TOKEN>/….
2. Find the chat_id
Section titled “2. Find the chat_id”Private chat (the bot writes to you directly)
-
Open your bot (
t.me/my_homelab_bot) and click Start, or send it any message. Without this, it will never be able to write to you. -
Open in a browser:
https://api.telegram.org/bot<TOKEN>/getUpdates -
You get a JSON document; look for
"chat":{"ok":true,"result":[{"update_id":83xxxxx35,"message":{"message_id":2643,"from":{…},"chat":{"id":21xxxxx38,"first_name":"…","type":"private"},"date":1703062972,"text":"/start"}}]}The
chat_idis the value ofchat.id: here21xxxxx38(a positive number).If
resultis empty ("result":[]), send the bot another message and reload the page.
Channel
-
Add the bot as an administrator of the channel (Manage channel → Administrators → Add).
-
Post a message in the channel.
-
Open
https://api.telegram.org/bot<TOKEN>/getUpdates: the response contains achannel_postblock with"chat":{"id":-1001xxxxxx062,"type":"channel"}.The
chat_idis that negative number,-100included:-1001xxxxxx062.
Group
The easiest way goes through the desktop app:
- Add the bot to the group.
- Send a message in the group, right-click it → Copy message link. You get
https://t.me/c/194xxxx987/13(orhttps://t.me/c/194xxxx987/11/13if the group has topics). - The number right after
/c/is the group’s internal identifier:194xxxx987. Thechat_idto enter is that number prefixed with-100:-100194xxxx987.
Without the desktop app, the getUpdates method above works too: message.chat.id
is directly the right number (already negative). If the bot does not see the group’s
messages, ask BotFather for /setprivacy → Disable, then remove the bot from the
group and add it again.
Group topic (groups in “Topics” mode)
The chat_id is the group’s. In addition, fill in Group topic
(message_thread_id) with the middle number of the copied link: in
https://t.me/c/194xxxx987/11/13, the topic is 11. Without this setting, the
message lands in the General thread.
3. Check before saving
Section titled “3. Check before saving”Open in the browser:
https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAT_ID>&text=test123(add &message_thread_id=11 for a topic). If test123 arrives, the token and the
chat_id are right: enter them in DumbMonit and click Test.
- Settings —
chat_id(required),message_thread_id(topic, optional),api_base(defaulthttps://api.telegram.org, change it only to go through a relay) - Secrets —
bot_token
“Telegram cannot find this chat” — for a private chat, the bot has never received a message from you: send it
/start. For a group or a channel, the-100prefix is probably missing, or the bot has not been added to it.“thread not found” — the
message_thread_iddoes not match any topic of the group, or the group is not in “Topics” mode.
Microsoft Teams
Section titled “Microsoft Teams”Warning: the method has changed. The old “Office 365 connectors” were retired in
May 2026; an outlook.office.com/webhook/… URL no longer works.
The current procedure:
- In the Teams channel, ⋯ → Workflows.
- Choose the “Post to a channel when a webhook request is received” template.
- Confirm the steps, then copy the HTTPS POST URL shown at the end.
- Secrets —
webhook_url
DumbMonit sends a wrapped Adaptive Card, the only format these flows display correctly. If you get an error mentioning Power Automate, the URL probably comes from an old connector.
Matrix
Section titled “Matrix”- Create an account dedicated to DumbMonit on your homeserver, and invite it to the room. An account that is not a member cannot write.
- Get its access token. In Element: Settings → Help & About → Advanced → Access token.
- Get the room’s internal identifier — not its display name. In Element:
Room settings → Advanced → Internal room ID. It starts with
!(for example!aBcDeF:matrix.org).
- Settings —
server_url(for examplehttps://matrix.org),room_id - Secrets —
token
Mattermost
Section titled “Mattermost”Integrations → Incoming Webhooks → Add. Choose the default channel, then copy the URL.
- Settings —
channel(optional, to write somewhere other than the default channel),username(optional, defaultDumbMonit) - Secrets —
webhook_url
Rocket.Chat
Section titled “Rocket.Chat”Administration → Integrations → New integration → Incoming webhook. Choose the room, enable the integration, then copy the Webhook URL.
- Settings —
channel(optional),alias(optional, defaultDumbMonit) - Secrets —
webhook_url
Google Chat
Section titled “Google Chat”In the Google Chat space: space name → Apps & integrations → Webhooks → Add webhook. Copy the URL, which already contains the key and the token.
- Secrets —
webhook_url
- Create a bot: Personal settings → Bots → Add a new bot, type “Generic”. Note its email address and its API key.
- Subscribe the bot to the target stream, otherwise sending will be refused.
- Settings —
server_url(for examplehttps://your-org.zulipchat.com),email(the bot’s address),stream(the stream name),topic(optional, defaultDumbMonit) - Secrets —
api_key
The simplest channel of all: pick a topic name that is hard to guess, then subscribe to it from the ntfy app.
- Settings —
topic,server_url(optional, defaulthttps://ntfy.sh) - Secrets —
token(only if your server requires authentication)
On the public instance, anyone who knows a topic’s name can read it. Pick a long, random name, or host your own server.
Gotify
Section titled “Gotify”In the Gotify interface: Apps → Create Application. Copy the token shown.
- Settings —
server_url(for examplehttps://gotify.home) - Secrets —
token
Pushover
Section titled “Pushover”- On https://pushover.net, your User Key is shown right on the home page.
- Then create an application: Create an Application/API Token. You get an application token.
- Settings —
priority(optional, from -2 to 2),sound(optional),retryandexpire(only forpriority: 2) - Secrets —
token(application token),user_key(user key)
Without a priority setting, DumbMonit chooses according to severity: -1 for an
advisory or a resolution, 0 for a warning, 1 for a critical alert. An advisory
therefore does not make the phone ring.
Priority
2(“emergency”) repeats the notification until it is acknowledged. DumbMonit then addsretryandexpireautomatically, without which Pushover would reject the message.
Pushbullet
Section titled “Pushbullet”On https://www.pushbullet.com/#settings/account, click Create Access Token.
- Settings —
device_iden(optional; without it, all your devices are notified) - Secrets —
token
Bark (iOS)
Section titled “Bark (iOS)”Open the Bark app on your iPhone: it shows a URL containing your device key, the long string of characters after the domain name.
- Settings —
server_url(optional, defaulthttps://api.day.app),group(optional),level(optional),sound(optional) - Secrets —
token(the device key)
Without a level setting, DumbMonit chooses: passive for an advisory, active
for a warning, timeSensitive for a critical alert. The critical level, which
breaks through “Do Not Disturb”, is never chosen automatically — set it explicitly if
you want it.
Web Push
Section titled “Web Push”Native notifications on your phones and computers, straight from your own DumbMonit, with no app to install and no third-party account: the browser (installed as an app or not) receives the alerts even with every tab closed.
- On each phone or computer, open DumbMonit and go to Settings → Push notifications → Enable on this device, then Send a test. On iPhone and iPad, first add DumbMonit to the Home Screen. The step-by-step guide is in Push notifications (PWA).
- Create a Web Push channel here. It has no secret: its recipients are the devices subscribed in step 1.
- Settings —
users(optional; one account per line, empty: every subscribed device of every account),urgency(highby default,normalorlow),ttl(seconds the push service keeps the message for a device that is off, default86400),contact(optionalmailto:orhttps:address given to push services; empty:DUMBMONIT_PUBLIC_URLwhen it is HTTPS, otherwise the project page) - Secrets — none. The server’s VAPID key pair is generated at first start and stored encrypted; the private key never leaves the server.
The notification title is the severity and the monitor (Critical · nas01), the
body a short summary; a tap opens the Alerts page. DumbMonit needs outbound
HTTPS to the push services (fcm.googleapis.com, *.notify.windows.com,
updates.push.services.mozilla.com, *.push.apple.com); messages are
end-to-end encrypted, those services cannot read them. A subscription the push
service reports as gone (HTTP 404 or 410, for example after the app was
removed) is deleted automatically.
Apprise
Section titled “Apprise”Apprise is a gateway: with a single configuration it gives access to dozens of
services DumbMonit does not implement itself. Deploy the caronc/apprise container,
then choose one of the two modes.
Recommended mode — configuration stored in Apprise. Create a configuration in the Apprise interface, note its key, and put nothing else here.
- Settings —
server_url,config_key
Direct mode — DumbMonit provides the destinations. Apprise URLs often contain a
password (mailto://user:password@…): they therefore go into the secrets.
- Settings —
server_url - Secrets —
urls(one or more Apprise URLs, separated by commas)
Optional common settings: tag (to target only part of a configuration’s
destinations) and format (text by default, or markdown).
If the test answers “Apprise has no configuration under this config_key”, the key matches nothing. DumbMonit reports it instead of pretending the message was sent.
Home Assistant
Section titled “Home Assistant”- In Home Assistant: your profile → Security → Long-lived access tokens → Create token. Copy it, it will not be shown again.
- Choose the service to call. By default, DumbMonit creates a persistent notification on the dashboard.
- Settings —
server_url(for examplehttp://homeassistant.local:8123),service(optional, defaultpersistent_notification.create),target(optional),data(optional, object merged into the call) - Secrets —
token
A few useful services:
service | Effect |
|---|---|
persistent_notification.create | Banner in the Home Assistant interface |
notify.notify | All configured notification targets |
notify.mobile_app_<device> | The mobile app of one specific device |
light.turn_on | Turns on a light — use data for the entity and the colour |
The data setting is merged last: it can therefore override the title or the
message DumbMonit prepared.
Email (SMTP)
Section titled “Email (SMTP)”The only channel that depends on no third-party service.
- Settings —
host,from,to(list of addresses),security(starttlsby default, ortls, ornone),port(derived fromsecurity: 587, 465 or 25),username(optional) - Secrets —
password
With Gmail, an account password does not work: create an app password in the security settings of your Google account.
Signal
Section titled “Signal”Goes through a
signal-cli-rest-api instance
that you host and where you first register your number.
- Settings —
server_url(for examplehttp://signal.home:8080),number(the registered sender number),recipients(list of numbers or group identifiers),username(optional) - Secrets —
password(optional, if your instance is protected)
All numbers are written in international format, +33612345678.
SMS via Twilio
Section titled “SMS via Twilio”On the Twilio console, the home page shows your Account SID and your Auth Token. Then buy a number that can send SMS.
- Settings —
account_sid,from(your Twilio number) ormessaging_service_sid,to(list of recipients) - Secrets —
token(the Auth Token)
The SMS body is truncated at 300 characters: beyond that, Twilio splits it into segments billed separately.
On a trial account, Twilio only sends to numbers you have verified.
WhatsApp (Cloud API)
Section titled “WhatsApp (Cloud API)”Uses the WhatsApp Business Cloud API from Meta. In the Meta developer console, create
an app of type Business, add the WhatsApp product and note the Phone number
ID under API Setup. Create a permanent token for a system user with the
whatsapp_business_messaging permission.
- Settings —
phone_number_id,to(list of numbers in international format) - Secrets —
token
Meta only allows free text within 24 hours of the recipient’s last message to your business number. Have each recipient write “hello” to the number first. Sending outside that window needs an approved template, which this channel does not use.
WhatsApp (CallMeBot)
Section titled “WhatsApp (CallMeBot)”A free gateway for notifying yourself, with no Meta account. Add the CallMeBot contact
to your phone, send it I allow callmebot to send me messages, and it answers with an
API key.
- Settings —
phone(your number in international format) - Secrets —
api_key
CallMeBot is a third-party service with no guarantee of availability: use the Cloud API for anything critical.
Threema Gateway
Section titled “Threema Gateway”On gateway.threema.ch, create a Basic Gateway ID
(eight characters starting with *) and note its secret.
- Settings —
from(the Gateway ID),to(list of eight-character Threema IDs) - Secrets —
secret
The message is encrypted by the Threema server (basic mode), which is enough for an alert and needs no key pair. Each message consumes a Gateway credit.
On developer.webex.com create a Bot (My Apps → Create a Bot), copy its token, then add the bot to the space. The room ID is returned by the List Rooms API call.
- Settings —
room_id - Secrets —
token(the bot token)
The message is sent in Markdown.
SMS via Vonage
Section titled “SMS via Vonage”The API key and secret are on the Vonage API dashboard.
- Settings —
from(a number or alphanumeric name),to(list) - Secrets —
api_key,api_secret
SMS via Telnyx
Section titled “SMS via Telnyx”Create an API key (v2) in the Mission Control Portal and buy a number with SMS enabled.
- Settings —
from(the Telnyx number),to(list, E.164 format with the+) - Secrets —
api_key
SMS via ASPSMS
Section titled “SMS via ASPSMS”- Settings —
userkey,originator(11 characters at most),to(list) - Secrets —
password(the API password)
The three SMS channels send at most 300 characters, without emoji, on a single line. They carry no acknowledge/snooze link — a signed link would cost several segments — use a chat channel for that.
PagerDuty
Section titled “PagerDuty”In PagerDuty: Services → your service → Integrations → Add integration, and choose Events API v2. Copy the Integration Key.
- Settings —
region(euif your account is hosted in Europe),source(optional, defaultdumbmonit) - Secrets —
token(the Integration Key)
An alert opens an incident; its resolution closes it automatically. Matching is
done on the alert’s fingerprint, prefixed with dumbmonit-, so a reminder never opens
a second incident.
A key of another type (“Events API v1”, “REST API”) gets a terse refusal. Check that the integration is indeed of type Events API v2.
Opsgenie
Section titled “Opsgenie”Teams → your team → Integrations → Add integration → API. Copy the API key.
- Settings —
region(eufor a European account),responders(list of team names, optional),tags(list, optional) - Secrets —
api_key
As with PagerDuty, a resolution closes the alert, identified by its alias.
Atlassian has set the end of service for Opsgenie on 5 April 2027. This channel stays for existing installations; for a new one, prefer PagerDuty or a custom channel pointing to Jira Service Management.
Grafana OnCall
Section titled “Grafana OnCall”In Grafana OnCall: Integrations → New integration → Webhook, then copy the HTTP endpoint.
- Secrets —
webhook_url
An alert opens an alert group (state: alerting) and its resolution resolves it
(state: ok), matched on a dumbmonit- identifier per device. Acknowledge from
OnCall itself.
SIGNL4
Section titled “SIGNL4”In SIGNL4, copy the team webhook from Teams → Team settings: it is
https://connect.signl4.com/webhook/ followed by your team secret.
- Secrets —
webhook_url
An alert reaches the team’s phones (X-S4-Status: new) and its resolution closes it
(resolved), matched on a per-device external ID.
DumbMonit on-call
Section titled “DumbMonit on-call”Pages the person on call, then escalates step by step until someone acknowledges. It has a single setting, the escalation policy to run, and no secret. Schedules, contacts and policies are described in On-call; the channel goes through the usual routing (labels, minimum severity, channels chosen per rule).
Automatic tickets (Jira, GLPI, Zammad, GitHub, GitLab)
Section titled “Automatic tickets (Jira, GLPI, Zammad, GitHub, GitLab)”These five channels do not send a message: they keep one ticket per alert.
- When an alert opens, the ticket is created: title, description with a link to the device in DumbMonit (set the public address in the notification policy), and a priority taken from the severity.
- The ticket reference is remembered per alert and per channel, so a reminder or a retry never opens a second one while the first is open.
- Notable events are added as comments: an escalation, an alert nobody acknowledged in time, a new finding, and an acknowledgement by someone in DumbMonit.
- When the alert resolves, DumbMonit comments and closes the ticket (transition, status or state configured on the channel).
- Reopen if the alert returns within (hours) — if the same alert comes back in that delay, the closed ticket is reopened with a comment instead of opening a new one. Empty or 0: a new ticket every time. If the ticket cannot be reopened (missing transition, deleted), a new one is created.
- The ticket link appears on the alert, in the alert details.
- Failures show in the channel’s “last error”. A temporary failure (service down, 429) is retried with an increasing delay; a permanent one (bad token, unknown project) is reported and not retried.
- Send test creates a test ticket then closes it right away. If it cannot be closed, the error says so and names the ticket to close by hand.
- Acknowledge and snooze links are not added to these channels.
Tokens are encrypted and never shown again. Create a dedicated account or token with only the rights it needs.
Works with Jira Cloud and Jira Data Center.
-
Cloud: create an API token at https://id.atlassian.com/manage-profile/security/api-tokens and give the account e-mail.
-
Data Center: Profile → Personal Access Tokens; no e-mail needed.
-
Settings —
server_url,email(Cloud only; leave empty on Data Center),project(the project key),issue_type(defaultTask),labels(list),priority(mapsets Highest, Medium or Low from the severity;nonefor projects without a priority field),close_transition(defaultDone),reopen_transition(defaultReopen),reopen_hours -
Secrets —
api_token
Transitions are matched by name (of the transition or of its target status, case insensitive) among those available from the issue’s current status. If the name does not exist, the error lists the available ones.
Enable the REST API in Setup → General → API, create an API client, and copy the user’s API token from My settings → Remote access keys.
- Settings —
server_url(the GLPI address;/apirest.phpis added),entity_id,category_id(numbers, optional),close_status(solvedorclosed),reopen_hours - Secrets —
user_token,app_token(the API client’s application token, if it requires one)
The ticket is an incident; urgency, impact and priority follow the severity. Escalations are added as follow-ups. Reopening sets the status back to New.
Zammad
Section titled “Zammad”Create an access token in Profile → Token Access with the ticket.agent
permission.
- Settings —
server_url,group,customer(e-mail or login),close_state(defaultclosed),reopen_state(defaultopen),reopen_hours - Secrets —
token
Tickets are created with an internal note; the priority is Low, Normal or High depending on the severity.
GitHub Issues
Section titled “GitHub Issues”Create a fine-grained token (Settings → Developer settings) limited to the repository, with the Issues: read and write permission.
- Settings —
repository(owner/name),labels(list; they must exist),api_url(GitHub Enterprise Server only, for examplehttps://ghe.example.org/api/v3),reopen_hours - Secrets —
token
Resolution closes the issue as completed.
GitLab Issues
Section titled “GitLab Issues”Create a project access token (or a personal one) with the api scope.
- Settings —
server_url,project(numeric id orgroup/project),labels(list),reopen_hours - Secrets —
token
Custom webhook
Section titled “Custom webhook”See The custom channel above.
Troubleshooting
Section titled “Troubleshooting”The test fails immediately, with “missing setting …”. A required field is empty. The message names the exact setting.
The test fails with “no answer within 15 s”. The target server is unreachable from DumbMonit. If it is a service on your local network, check that the DumbMonit container can reach it — this is frequently a Docker networking problem, not a channel configuration problem.
The test fails with a certificate error. Your server uses a self-signed or private-CA certificate. See Self-signed certificates.
The test succeeds, but I receive nothing. The service accepted the message and sent it elsewhere: wrong channel, wrong ntfy topic, wrong Pushbullet device. Check the destination setting.
The error message contains ***.
This is intended: your token was removed from the message before it was recorded.
No secret ever appears in the logs, in the “last error” column, or in an API
response.
Where can I see a channel’s last error? In the channel list: each row shows the date of the last successful delivery and, if any, the error of the last failure.