Skip to content
DX Spots on Your Phone: Wiring HamAlert to ntfy

HamAlert is a great free service that watches DX cluster spots, RBN, PSK Reporter, SOTAwatch and more, and can trigger a notification whenever a callsign, band, mode or other pattern of interest shows up. One of its notification options is a plain URL Notification (GET, POST form, or POST JSON), which makes it trivial to wire into ntfy, a lightweight self-hosted push notification service.

Here’s how I set it up to get DX spots pushed straight to my phone.

Why a dedicated user and topic

If you’re self-hosting ntfy, it’s tempting to just reuse your existing topics and admin credentials for every integration. Don’t. Any service that has your ntfy credentials could publish to (or, depending on permissions, read) any topic on your server. Instead, create a dedicated user with write-only access to a single topic, scoped to exactly what HamAlert needs and nothing else.

Creating the user and topic

Assuming ntfy is running in Kubernetes (mine runs in a namespace called ntfy), creating a scoped user takes two commands:

kubectl exec -n ntfy deploy/ntfy -- env NTFY_PASSWORD="<a-strong-password>" ntfy user add hamalert
kubectl exec -n ntfy deploy/ntfy -- ntfy access hamalert hamalert wo

The first command creates a regular user named hamalert with the password passed via environment variable. The second grants that user write-only (wo) access to a topic also named hamalert. Write-only means this user can publish notifications to the topic, but can’t subscribe to it or read anything back, and has no access to any other topic on the server.

You can confirm the grant:

$ kubectl exec -n ntfy deploy/ntfy -- ntfy access hamalert
user hamalert (role: user, tier: none)
- write-only access to topic hamalert

Configuring HamAlert

In HamAlert, create (or edit) a trigger, and under notification methods add a URL Notification. You have three format options: GET, POST (Form) or POST (JSON). Any of them works with ntfy.

The simplest and most portable option is a GET request with credentials embedded in the URL and the message as query parameters:

https://hamalert:<password>@ntfy.YOURDOMAIN/hamalert?title={title}&message={fullCallsign}+{frequency}MHz+{mode}

If HamAlert lets you set custom headers, POST (JSON) is cleaner, since ntfy has a native JSON publish format with dedicated title, message, priority and tags fields:

  • URL: https://ntfy.YOURDOMAIN/
  • Header: Authorization: Basic <base64 of hamalert:password>
  • Body:
{
  "topic": "hamalert",
  "title": "{title}",
  "message": "{fullCallsign} on {frequency} MHz ({mode}) - {comment}",
  "tags": ["radio"],
  "priority": 3
}

HamAlert substitutes the {placeholder} variables at send time. My own message template:

{fullCallsign} spotted on {band} ({frequency} MHz, {mode}) by {spotter} at {time}Z

HamAlert exposes a long list of variables you can mix into title/message/comment, covering callsign, frequency, band, mode, spotter, DXCC/CQ zone/continent info, SOTA/POTA/WWFF/IOTA references, SNR, QSL status and more, so you can build a notification as terse or as detailed as you like.

Result

Once the trigger fires, a push notification shows up on my phone (via the ntfy app subscribed to the hamalert topic) with the callsign, band and mode of whatever spot matched my trigger, no polling, no email, just an instant push, and the credentials HamAlert holds can’t do anything beyond posting to that one topic.

Last updated on • Eduardo Fortes