Skip to main content
PathMon

Chapter 18 of 26

Notification Destinations

Updated Read the full guide

What a destination is

A destination is an endpoint that outgoing notifications are sent to: an SMTP mailbox, an HTTP webhook URL, an ntfy topic, or the built-in Internal Alerts destination that records alerts inside PatchMon itself.

Destinations are the "where" half of the notifications pipeline. The "what goes there" half is handled by event rules, covered in Notification Routes and Delivery Log.

Destinations live under Reporting → Destinations in the web UI.

Channel types

PatchMon 2.0 ships four destination channel types. The list is fixed in the server code:

Channel Value What it does
Webhook webhook HTTP POST of a JSON payload to any URL. Generic by default; Discord and Slack webhook URLs are auto-detected and formatted with the appropriate rich payload.
Email email SMTP delivery to one or more recipients, with HTML body and an optional attachment for scheduled reports.
ntfy ntfy Push notification via ntfy.sh or a self-hosted ntfy server.
Internal Alerts internal Built-in destination that drops events into the Alerts tab. You cannot create or delete this one; it is created automatically and can only be enabled or disabled.

Discord is a webhook, not a channel type. To post alerts to a Discord channel, create a Webhook destination with the Discord webhook URL. The separate Settings → Discord Authentication area is only for Discord OAuth2 sign-in; it is unrelated to notifications.

Permissions

Creating, editing, testing, and deleting destinations requires the can_manage_notifications permission. Admins and superadmins bypass the check. Users without the permission do not see the Destinations tab at all.

Creating a destination

  1. Open Reporting → Destinations.
  2. Click Add destination.
  3. Pick a channel type (Webhook, Email, or ntfy) and click Next.
  4. Give the destination a Display name: this is what appears in the event rules picker, the delivery log, and scheduled report selectors.
  5. Fill in the channel-specific configuration (see below).
  6. Leave Enabled on (default) or turn it off to save the configuration without sending anything yet.
  7. Click Create.

A successfully-created destination shows up in the destinations table with its channel icon, display name, and enabled switch.

Webhook

Pick this for generic JSON webhooks, Discord, or Slack. Discord and Slack URLs are auto-detected and sent rich payloads; other URLs receive a generic JSON body.

Field Required Notes
Webhook URL Yes Full HTTPS URL. Discord: https://discord.com/api/webhooks/.... Slack: https://hooks.slack.com/services/.... Generic: any endpoint that accepts POST with Content-Type: application/json.
Signing secret No Optional HMAC secret. When set, each webhook is signed with SHA-256 over the payload; the signature is sent in a header so the receiver can verify authenticity.

Email (SMTP)

Field Required Notes
SMTP host Yes e.g. smtp.example.com, smtp.sendgrid.net.
SMTP port No Defaults to 587. Use 465 for implicit TLS, 25 for unencrypted relay (avoid).
Username No SMTP auth user. Leave blank if your relay does not require it.
Password No SMTP auth password. Stored encrypted.
From Yes Envelope + header From address, e.g. patchmon@example.com. Must be accepted by the relay.
To Yes Comma-separated list of recipients.
TLS mode Yes Choose how the SMTP transport secures the connection. See TLS modes below. Defaults to STARTTLS for new destinations.
TLS modes

PatchMon offers four TLS modes on every email destination. Pick the one your relay actually supports rather than leaving it on Auto, so a misconfigured server fails closed instead of silently downgrading to plaintext.

  • STARTTLS (recommended). PatchMon connects in plaintext on the SMTP port (typically 587) and then requires the server to advertise STARTTLS. The connection is upgraded to TLS before any credentials or message body are sent. If the server does not advertise STARTTLS, PatchMon refuses to send and reports the failure. This is the right choice for the vast majority of modern relays (Microsoft 365, Google Workspace, SendGrid, Postmark, Mailgun, Amazon SES on port 587, and most on-prem mail servers).
  • Implicit TLS / SSL. PatchMon opens a TLS connection from the very first byte, with no plaintext handshake. The default port for this mode is 465. Use it when your relay only accepts TLS on a dedicated port and does not support STARTTLS. Some legacy or appliance-based servers only offer this mode.
  • None (insecure). Cleartext SMTP, no TLS at any stage. PatchMon refuses to send if a username or password is set on the destination, because it would otherwise leak credentials onto the wire. Use only for trusted internal relays on a private network where TLS is genuinely unavailable.
  • Auto. Legacy opportunistic mode kept for backward compatibility. PatchMon tries STARTTLS first and falls back to implicit TLS on the same host and port if STARTTLS is not advertised. Existing destinations that were saved before the explicit modes were added load as Auto so they keep working unchanged. Open the destination, pick STARTTLS or Implicit TLS / SSL explicitly once you have confirmed which one your relay supports, and save. New destinations should not be configured as Auto.

Port and mode are independent. The port field is just the TCP port to connect to; the TLS mode controls how the connection is secured. The defaults (587 for STARTTLS, 465 for implicit TLS) match the conventional ports, but you can override the port if your relay listens elsewhere.

Send test email

Saved email destinations have a Send test email button next to the standard Test action. Unlike Test, which enqueues a synthetic notification through the worker, Send test email performs a synchronous live SMTP probe directly from the API request and reports the result inline:

  • On success the toast confirms delivery and the recipients should receive a short test message.
  • On failure PatchMon reports which stage of the SMTP exchange failed: validate (the configuration is rejected before any network activity, for example a missing host or a username set with TLS mode None), dial (the TCP connection or implicit TLS handshake could not be established), starttls (the server did not advertise STARTTLS in the chosen mode), auth (the relay rejected the credentials), or send (the relay accepted the session but rejected the recipients or message). The toast includes the underlying error message returned by the relay.

This is the fastest way to diagnose a TLS or auth misconfiguration without trawling through server logs. The probe respects the same can_manage_notifications permission as editing the destination.

ntfy

Field Required Notes
Server URL No Leave empty for https://ntfy.sh. Fill in your own URL for self-hosted ntfy.
Topic Yes ntfy topic name. Subscribe to the same topic on your phone/desktop to receive push notifications.
Access token No ntfy bearer token for protected topics. Alternative to basic auth.
Username / Password No Basic auth. Use instead of access token when your ntfy server is configured for HTTP basic auth.

Internal Alerts

You cannot create this destination; it is seeded automatically with the ID internal-alerts and appears with the Built-in tag. Its sole job is to write events into the in-app Alerts tab. You can:

  • Enable / disable it from the destinations table (disable if you do not want internal alert records at all, for example when you only use external chat or email).
  • Reference it from event rules, same as any other destination.

You cannot delete it. Attempting to delete returns 400 Bad Request: The Internal Alerts destination cannot be deleted. You can disable it instead.

Editing a destination

Click Edit in the destinations table. The modal re-loads the current configuration (secrets included, so you do not have to re-type passwords or tokens) and lets you change any field. Click Save to apply.

The enabled switch is inline in the table; click it to toggle without opening the modal.

Secrets are always encrypted at rest using PatchMon's SESSION_SECRET. When you re-enter a secret and save, the value is re-encrypted. The decrypted value is returned only to operators with can_manage_notifications.

Testing a destination

Use Test in the destinations row to verify the configuration without waiting for a real event:

  1. Click Test next to any enabled non-built-in destination.
  2. PatchMon enqueues a synthetic event with type test, severity informational, and the message "This is a test message from PatchMon notification settings."
  3. A toast confirms the test is enqueued. Actual delivery happens through the notifications worker and takes a second or two.
  4. The Delivery Log updates automatically after about three seconds; look there for the outcome.

Tests do not bypass global rate-limiting. If your destination is already at its per-minute rate cap (60 messages/minute), the test returns 429 Too many notifications; try again shortly.

Failure cases returned by the test endpoint:

HTTP Message Meaning
400 Bad Request Destination is disabled Enable it first.
404 Not Found Destination not found The destination was likely deleted in a parallel tab.
429 Too Many Requests Too many notifications; try again shortly Rate limit hit.
503 Service Unavailable Notifications not configured Background worker or Redis is not running.

Deleting a destination

Click the trash icon in the destinations table. The confirmation dialog warns that the action is permanent. Deleting a destination does not delete the event rules that target it: those routes will be orphaned and should be updated to point at a different destination or removed. Deliveries in the Delivery Log keep their historical destination_id and appear as a raw ID if the name can no longer be resolved.

You cannot delete the internal-alerts destination (see above).

What gets stored

Every destination is a database row with:

  • A stable UUID (id) used by event rules and the delivery log.
  • channel_type from the list above.
  • display_name.
  • enabled boolean.
  • config_encrypted: the JSON configuration, encrypted with SESSION_SECRET so a database dump does not expose SMTP passwords, ntfy tokens, or HMAC secrets.
  • created_at / updated_at timestamps.

The list endpoint never returns the raw config, only a has_secret flag. The decrypted config is fetched on demand from a separate endpoint when the edit modal opens.

Discord sign-in vs Discord webhooks

The two "Discord" areas in PatchMon are independent:

Area Purpose Where
Discord Authentication OAuth2 sign-in, users log in to PatchMon with their Discord account, optionally requiring membership of a server and role. Settings → Discord Authentication
Discord webhook destination Post alerts into a Discord channel via a channel webhook URL. Reporting → Destinations → Add destination → Webhook

Configure them separately. The OAuth2 settings are not required to send alerts to Discord.