The Notakey appliance is usually described as an authentication server: a
login happens, a push request lands on the phone, the user approves. But the
same delivery channel accepts a second message type, a plain
notification, and that turns the appliance into something more general:
a push-messaging channel to every phone enrolled in your service, callable
from any script with a single curl.
any script ──HTTPS──▶ appliance API ──push──▶ enrolled phone
(cron, monitoring, /notify Notakey Authenticator
home automation…)
Unlike an authentication request, a notification doesn’t ask for approval: it just appears on the phone, addressed to a username, and is delivered to all of the user’s enrolled devices at once. That makes it a drop-in replacement for the places you’d otherwise wire up SMS (per-message cost, spoofable sender) or email (ignored until Monday): job monitoring, on-call pings, employee announcements.
Requirements
- A running Notakey appliance (cloud or on-premise) with a service and at least one user with an onboarded phone. If you already run VPN 2FA or SSO, you have all of this: notifications go through the same service your logins use, or a separate one if you want different branding.
- API access credentials (a client ID and a client secret), created in the dashboard under Access credentials (Step 1 below).
curlandjqon anything that can reach the appliance. That’s the whole dependency list.
Step 1 — Create API access credentials
In the dashboard, open Access credentials and create a credential for your scripts. Two things matter here:
- Scopes. Grant
urn:notakey:notify. That is the scope this guide needs.urn:notakey:authexists too, for creating authentication (approve/deny) requests; add it only if the same credential will do both. A notify-only credential can’t be abused to trigger login approvals, so keep the scope list as short as the job allows. - You receive a client ID and a client secret. The secret authorizes messaging everyone in the service, so treat it like a root password.
Step 2 — Exchange it for a token, and keep the token fresh
The notify call itself doesn’t use the client secret. It uses a bearer access token obtained through the standard OAuth 2.0 client-credentials grant. Access tokens are deliberately short-lived: plan to renew about once an hour, or your scripts will start failing with authorization errors at some point after they’ve been running fine for 59 minutes.
So token renewal is a cron job, not a setup step:
#!/bin/bash
# /usr/local/bin/ntk-token-renew — cron: 0 * * * *
curl -s -X POST https://ntk.example.com/api/token \
-d 'grant_type=client_credentials&client_id=<client-id>&client_secret=<client-secret>&scope=urn:notakey:notify' \
| jq -r .access_token > /etc/notakey/api.token
The script embeds the client secret and writes the token, so lock both down:
chmod 700 /usr/local/bin/ntk-token-renew # root-only, holds the secret
install -m 600 /dev/null /etc/notakey/api.token
If you also granted urn:notakey:auth, request it as a separate token
into a separate file (same call, scope=urn:notakey:auth) rather than one
token with both scopes; the notification scripts then never hold a token
that could create login approvals.
Step 3 — One reusable notify script
Everything else in this guide calls this one script. It takes a username, a title and a message body:
#!/bin/bash
# /usr/local/bin/ntk-notify <username> <title> <description>
token=$(cat /etc/notakey/api.token)
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: Bearer $token" \
-d '{ "username": "'"$1"'", "title": "'"$2"'", "description": "'"$3"'", "type": "notification" }' \
"https://ntk.example.com/api/v3/services/<service-id>/notify"
Replace ntk.example.com with your appliance address and <service-id>
with the service UUID from the dashboard. Test it:
ntk-notify j.doe "Test" "If you can read this on your phone, the channel works."
The push arrives in the Notakey Authenticator app, the same app the user already has for logins, so there is nothing new to install, explain, or get past mobile-device management.
Step 4 — Call it from anything
Once the channel is a one-liner, it collects use cases. Some that run in production today:
A backup job that reports back. The classic silent failure: backups that stopped working months ago and nobody noticed. Two lines at the end of the job fix that:
if ! /usr/local/bin/backup-run; then
ntk-notify admin "Backup FAILED" "Nightly backup on $(hostname) exited with an error — check /var/log/backup.log"
fi
Disk space, certificate expiry, anything cron can check:
#!/bin/bash
# cron: 0 8 * * *
used=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if [ "$used" -gt 90 ]; then
ntk-notify admin "Disk warning" "Root filesystem on $(hostname) is at ${used}% — time to clean up."
fi
An announcement to every employee. The API addresses one username per call, so a company-wide message is a loop over your user list (maintenance windows, office closures, incident notices):
while read -r user; do
ntk-notify "$user" "IT maintenance" "VPN unavailable Saturday 06:00–08:00 — planned appliance upgrade."
done < users.txt
Electricity spot prices, twice every two hours. Not everything worth pushing is an outage. This one runs from cron and tells the user what the next hour of electricity will cost, handy for deciding when to run the dishwasher:
#!/bin/bash
# cron: 5 * * * * — sends on even hours, 06–23 only
hour=$(( $(date +%H) + 1 ))
next=$(( hour + 1 ))
if [ "$hour" -gt 5 ] && [ "$hour" -lt 24 ] && (( $(date +%H) % 2 == 0 )); then
ntk-notify "$1" "Electricity" \
"Next hour: $(spot_price "$hour")€/kWh. After that: $(spot_price "$next")€/kWh."
fi
(spot_price is whatever fetches your market data: an exchange API, a
cached CSV. The point is the delivery, not the source.)
Two documented extras worth knowing for recurring messages like this one:
adding a fingerprint field to the payload makes a repeat send with the
same fingerprint update the existing message instead of stacking a new
one, so a price ticker stays one notification, not forty a day. And
"type": "sms" sends the same message as an SMS instead, if the user has a
mobile number defined: a fallback channel for someone whose phone lost the
app.
The washing machine is done. A smart plug watching power draw, a Home Assistant automation, a script polling a meter — whatever detects the end of the cycle, the notification is the same one-liner. Silly example, real habit: once sending a push costs nothing, you notify about everything.
What this channel is (and is not) good for
Compared to SMS, the recipient side is much stronger: an SMS arrives on whatever handset currently holds the SIM, from a sender name anyone can fake, while a Notakey notification lands only in the Authenticator app on devices that completed cryptographic enrollment in your service. Nobody can subscribe themselves to your alerts, and nobody can spoof the sender.
One honest limitation, straight from the
API documentation: the notification
body itself is not end-to-end encrypted: like any mobile push, it is
relayed through Apple’s and Google’s push services and is readable by apps
with notification access on the device. So treat the message body like a
pager message, not like a sealed envelope: “Backup failed on host X” is
fine; a password or an API key is not. When you need to deliver something
genuinely secret, use an authentication request (urn:notakey:auth)
instead. There the push is only a knock on the door, and the payload is
fetched by the app over its certificate-authenticated channel after the
user responds.
Where this fits
If the appliance already protects your VPN or applications, notifications are free capability you’re not using: same service, same app, same API credentials, one extra endpoint. And if you’ve been paying an SMS gateway for internal alerts, this replaces it for every recipient who carries an enrolled phone.
Related guides
- VPN 2FA with RADIUS — the complete auth-proxy setup: the authentication side of the same appliance
- Linux SSH 2FA with pam_radius: phone approvals for the servers your scripts run on
- API documentation: the full REST surface, including device queries and authentication requests