Notification channels#

A notification channel is a destination an alert can be sent to. Your organisation can have any number of them, and each check or monitor decides which of them it actually uses.

Adding a channel#

From Organisation settings, under Notification channels:

  • Add ntfy channel — sends a push notification via ntfy to a topic you choose.
  • Add email channel — sends to a single email address.
  • Add webhook channel — sends an HTTP POST with a plain JSON body of your own to any endpoint you control. See Webhook channels below for the payload shape and how to verify it came from tenpm uptime.
  • Add Slack channel — sends to a Slack incoming webhook. See Slack channels below.

Give each channel a name (how it’ll show up when picking channels elsewhere) and its destination — the ntfy topic, email address, or webhook/Slack URL. A channel’s kind is fixed once created; if you need to change one kind to another, add a new one and delete the old one.

Edit a channel from the same page at any time to change its destination, or to pause it without deleting it — a disabled channel is skipped when alerts go out, but keeps its configuration for when you re-enable it.

Whichever kind you add, the target must be a publicly reachable URL — tenpm uptime refuses to save (or send to) a webhook or Slack URL that resolves to a private, loopback, or otherwise internal address, since this server has no more reach into your own network than your browser does.

Webhook channels#

A webhook channel POSTs a plain JSON body to a URL you choose — useful for PagerDuty, a chat tool with its own custom-webhook format, or an endpoint you’ve written yourself. Unlike Slack (below), the body shape is fixed and generic rather than matching any particular service:

{
  "title": "DOWN: Example check",
  "message": "monitor Home: unexpected status 500"
}

(shown pretty-printed here for readability — the actual request body is compact, with no added whitespace, and its fields may come in a different order; see Signing below for why that matters.)

priority and tags fields are added when the alert carries them (these come from the same conventions ntfy uses); a plain down/recovered notification generally won’t. The request is sent with Content-Type: application/json.

Signing#

Every webhook channel has an optional Signing secret field — up to 64 characters, with a Generate button to fill in a random one. When a secret is set, every request is signed and carries the signature as an X-Tenpm-Hmac-Sha256 header: an HMAC-SHA256 digest of the exact request body, base64-encoded. Leave the secret blank to send unsigned requests (no header is sent at all in that case, rather than an empty one).

Verifying the signature confirms two things: the request actually came from tenpm uptime (not something else that happened to discover your endpoint), and the body wasn’t altered in transit. To verify it, compute the same HMAC-SHA256 over the raw bytes of the request body you received — not a re-serialized version, since re-encoding JSON can reorder fields or change whitespace and would break the comparison — using your secret as the key, base64-encode the result, and compare it to the header. Use a constant-time comparison rather than ==/.equals(), so the comparison itself can’t leak information about how much of the signature you got right.

Here’s a Java example, using nothing beyond the standard library:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;

public class TenpmWebhookVerifier {

    /**
     * rawBody must be the exact bytes of the HTTP request body, read
     * before any JSON parsing - re-serializing the parsed JSON and signing
     * that instead will not match.
     */
    public static boolean verifySignature(byte[] rawBody, String secret, String signatureHeader)
            throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] expected = mac.doFinal(rawBody);
        byte[] received = Base64.getDecoder().decode(signatureHeader);

        // Constant-time comparison - MessageDigest.isEqual is safe for this
        // even though it's not primarily a "MAC compare" API.
        return MessageDigest.isEqual(expected, received);
    }
}

Wire it up wherever your HTTP framework hands you the raw request body and the X-Tenpm-Hmac-Sha256 header, and reject the request if verifySignature returns false.

Slack channels#

A Slack channel posts to a Slack incoming webhook — Slack’s own fixed message format, so there’s no payload shape to think about and no signing (Slack’s endpoint has no signature scheme to plug into).

On the Slack side, if you don’t already have an incoming webhook for the channel you want alerts in:

  1. Go to api.slack.com/appsCreate New AppFrom scratch → pick a name and the workspace.
  2. Under Incoming Webhooks, turn the feature on, then Add New Webhook to Workspace and choose the channel.
  3. Copy the URL Slack gives you — it looks like https://hooks.slack.com/services/T000/B000/xxxxxxxx.

On tenpm uptime’s side:

  1. From Organisation settings, click Add Slack channel and paste the webhook URL in.
  2. Save it, then click the channel’s Test button — it sends a canned message through immediately, so you can confirm it’s wired up before waiting on a real check to fail.

Assigning channels to checks#

Notifications for a check going down or recovering are sent to whichever channels are assigned to that check, set on the check’s own edit page under Notify. Only ticked channels are notified.

New checks default to every channel your organisation has, unless that default has been turned off — see below.

Assigning channels to monitors#

Separately, each monitor has its own Notify section on its detail page, for alerts about the monitor itself going offline or coming back — not the checks it runs. A newly enrolled monitor is assigned every channel that exists at the moment it enrolls; add or remove channels for it afterwards from the same page.

Defaults for new checks#

Two toggles on Organisation settings control what a brand-new check starts with:

  • Automatically include all notification channels on new checks — on by default. Turning it off means a new check starts with no channels assigned and you pick them explicitly. This only affects checks created from then on; existing checks keep whatever they already have.
  • Automatically include new shared monitors on non-restricted checks — covered in Managing monitors; it’s the equivalent toggle for the shared monitor fleet rather than notification channels.

Where deliveries show up#

The Notifications page lists every alert actually sent — check, channel, whether it was a down or recovery notification, and whether delivery succeeded — useful for confirming a channel is actually wired up correctly after you add it.