Notification channels

Six channels, and a straight answer about what fires them.

Email, Slack, Discord, Microsoft Teams, ntfy and signed webhooks are all built, all in production, and all have a test-send button in the dashboard. What triggers them today is narrower than you would expect, so that is the first thing on this page rather than the last.

EmailSlackDiscordMicrosoft TeamsntfyGeneric webhook
Read this first

Uptime checks do not notify anyone yet.

Two things dispatch to these channels in production. A failing website check is not one of them. If that is a dealbreaker, better you learn it here than three weeks in.

Uptime → notification dispatch

The six notification channels are built, tested and in production — but the only thing that currently triggers them is a Watch Owl server-metric alert rule. A failing HTTP, ping or TCP check opens an incident on your status page and notifies nobody. This is the single biggest gap in the product and it is what we are working on right now.

Send Test, from the dashboard

Dispatches

Every integration has a Send Test button that performs a real delivery against the real channel. It is the fastest way to prove a route works after a URL change, a secret rotation or a channel rename.

Watch Owl server-metric alert rules

Dispatches

A threshold rule over host metrics reported by the Watch Owl agent — CPU, memory, disk, network, patch and reboot state, offline detection. When a rule fires or resolves, StatusOwl dispatches to every integration that rule routes to.

A failing HTTP, ping or TCP check

Does not dispatch

A failing uptime check records the state change and opens an incident on any status page the monitor is attached to. It sends nothing to email, Slack, Discord, Teams, ntfy or a webhook. The notify-on-down settings save and persist, but no service consumes them yet.

The channels

All six are real, and all six can be tested before you need them.

Each integration is its own row in the dashboard with its own settings and on/off switch. Create as many as you like — one per channel, per team, per audience.

Direct delivery
Shipped

Email

The simplest channel to wire up: no OAuth, no webhook URL, no API key. Enter one or more addresses and StatusOwl delivers plain-text mail through Resend from a verified sending domain with SPF, DKIM and DMARC in place. Sending is rate-limited, and the from-address is chosen by category so operational mail does not share a reputation with billing or account mail.

Setup
Addresses only
Fan-out
Many per integration
Set up Email
Incoming webhook
Shipped

Slack

Posts into one channel through a Slack incoming webhook — no workspace-wide app install. StatusOwl sends a hybrid text + blocks payload so the message renders on both modern and legacy clients. Server-metric messages carry the rule name, host, metric and current value, the threshold that tripped and a timestamp, coloured red on fire and green on resolve.

Endpoint
hooks.slack.com
Scope
One channel each
Set up Slack
Incoming webhook
Shipped

Discord

Also a plain incoming webhook — no bot, no application to approve. Messages arrive as colour-coded embeds, red when a rule fires and green when it resolves, listing the rule name, hostname, the metric and its current value, the threshold, and the mount or interface the reading came from. Add one integration per channel you want to reach.

Endpoint
discord.com/api
Format
Rich embed
Set up Discord
Connector webhook
Shipped

Microsoft Teams

Delivered as themed MessageCard payloads through an Incoming Webhook connector scoped to a single Teams channel. The summary carries the rule and the host; the body lists metric, current value, threshold and timestamp; an action button opens the host detail back in the dashboard. Tenants whose admins have disabled connectors can use the Workflows “post to a channel” template instead.

Format
MessageCard
Fallback
Workflows URL
Set up Microsoft Teams
Push topic
Shipped

ntfy

Publishes to a topic on ntfy.sh or your own self-hosted server, subscribed from the phone or desktop app. Priority (15) and tags are set per integration, so one rule can push to the top of the notification tray while another stays quiet. Private topics can carry a bearer token, and the server URL is checked against private and loopback ranges when you save it and again on every send.

Server
ntfy.sh or self-hosted
Auth
Optional bearer token
Set up ntfy
Signed JSON
Shipped

Generic webhook

POST or PUT JSON to any HTTPS endpoint you control. Supply a shared secret and every request is signed with HMAC-SHA256 over the raw body. Delivery retries up to 3 times with 500ms then 1s backoff on network errors and non-2xx responses, through an SSRF-guarded client that refuses private, link-local and cloud-metadata targets.

Signature
HMAC-SHA256
Retries
3 attempts
Set up Generic webhook
Server-metric alert rules

The one thing that fires these channels today.

Install Watch Owl on a host and it reports CPU, memory, per-mount disk, per-interface network, host metadata and OS patch state. A rule is a metric, a comparison, how long the condition has to hold, and the integrations it routes to.

CPU %Memory %Disk % per mountNetwork RXNetwork TXReboot requiredPending updatesSecurity updatesHost offline

The evaluator runs every 60sec. A rule that fires again inside its cooldown does not re-notify, and the resolve message is dispatched to the same channels as the fire.

Rule · prod-db disk near full
Firing
Metric
disk_percent
Scope
prod-db-01.internal
Mount filter
/var/lib/postgresql
Condition
> 90 %
Hold for
5 min
Cooldown
30 min
Evaluated
every 60 sec
Routes to
Slack, ntfy
Alert event history
Fired atRuleValueResolvedSent
2026-05-09 14:03prod-db disk near full94.2 %14:182 / 2
2026-05-08 03:41web-02 memory high91.6 %04:021 / 1
2026-05-06 09:12prod-db reboot requiredtrue3 / 3
Webhook payload

Signed JSON, into whatever you already run.

If the tool your team actually lives in is not one of the five we speak natively, the generic webhook gets you there. Here is exactly what arrives when a server-metric rule fires.

Set a shared secret on the integration and every request carries an X-StatusOwl-Signature header: the lowercase hex digest of HMAC-SHA256 over the exact bytes we sent, prefixed with sha256=. Verify it against the raw request body with a constant-time comparison — a re-serialised JSON object has different whitespace and key order, and will not match.

There is no timestamp header today, so the signature covers integrity rather than freshness. Deduplicate on event_uuid to make your handler idempotent.

POST · ops.acme.com
StatusOwl-Webhook/1.0
Request
POST /hooks/statusowl HTTP/1.1
Host: ops.acme.com
Content-Type: application/json; charset=utf-8
User-Agent: StatusOwl-Webhook/1.0
X-StatusOwl-Signature: sha256=3f1d9c0b7a52e84f6b0d2c9a41e7b8305c6f2a1d
Body
{
  "event_uuid": "de31a4fd-a1b1-4f0e-94a3-9a4d3a3c00bb",
  "rule_uuid": "1f2a3b4c-5d6e-7f80-9a0b-1c2d3e4f5a6b",
  "rule_name": "prod-db disk near full",
  "host_uuid": "abc12300-0001-4000-a000-000000000001",
  "host_hostname": "prod-db-01.internal",
  "metric": "disk_percent",
  "operator": "gt",
  "threshold_numeric": 90,
  "threshold_boolean": null,
  "value": 94.2,
  "mount": "/var/lib/postgresql",
  "interface": null,
  "fired_at": "2026-05-09T14:03:22.184Z",
  "organization_uuid": "3f5b9c2a-1d8e-4f6c-a92d-7b3a8e1c4f02"
}

The same shape is sent on resolution, with a different event_uuid and the reading taken at resolve time. Exactly one of threshold_numeric and threshold_boolean is non-null.

Delivery

How a message gets out, and what happens when it does not.

A test button on every channel

Send Test performs a real delivery, not a simulated one. Use it after every change — a new webhook URL, a rotated secret, a renamed channel — instead of finding out during an incident.

Retries with backoff

Webhook deliveries retry up to 3 times on network errors and non-2xx responses, waiting 500ms and then 1s. Return 200 quickly and process asynchronously.

SSRF-guarded egress

Webhook and ntfy targets are validated when you save them and again at dispatch. RFC1918, link-local, IPv6 ULA, multicast and cloud-metadata addresses are refused, redirects are not followed, and a blocked request fails immediately with no retry.

Dispatch history

Every alert event records when it fired, the reading at the moment it fired, when it resolved, and the delivery status for each integration it was routed to — so you can tell which channel actually took the message.

As many as you need

There is no cap per type. One Discord webhook for on-call and another for engineering, separate email integrations for internal and stakeholder lists. Each has its own settings and on/off switch, and each rule picks an explicit list of integrations.

On every plan

All six channels are included on Free, Starter, Growth, Scale. Notification channels are not a paid axis here — the plan ladder is priced on status pages, monitors and retention.

Questions

The ones worth asking before you rely on this.

Does StatusOwl notify me when my website goes down?
Not yet. A failing HTTP, ping or TCP check records the state change and opens an incident on any status page the monitor is attached to, but it does not dispatch to a notification channel. The only production trigger for the six channels today is a Watch Owl server-metric alert rule. Closing that gap is what we are working on right now.
So what can I be told about today?
Threshold rules over Watch Owl host metrics: CPU, memory, per-mount disk, per-interface network throughput, reboot-required and pending or security updates, and how long a host has been silent. Rules have an operator, a hold duration, a cooldown and a scope, and the evaluator runs every 60 seconds.
Can visitors subscribe to my status page for updates?
No. There is no subscribe form on a status page and no subscriber list in the dashboard. Publishing an incident updates the page; it does not send anything to anyone. Subscribers are on the roadmap and are not built.
How do I verify that a webhook really came from StatusOwl?
Configure a shared secret on the integration. Every request then carries an X-StatusOwl-Signature header holding the hex HMAC-SHA256 digest of the exact bytes we sent, prefixed with sha256=. Compute the digest over the raw request body — not a re-serialised JSON object — and compare it in constant time.

Wire up a channel and test it in a minute.

All six are included on every plan, starting with Free. Add an integration, press Send Test, and confirm the route before you need it.