What's Changed
Shoutrrr v0.24.0 adds Pushover emergency priority, OAuth2 client credentials for SMTP, and Jira Service Management support for OpsGenie URLs. It also checks service URLs and configs more strictly, so more mistakes fail before anything is sent.
Pushover
- A URL with
priority=2now sends an emergency notification. Pushover repeats it every 15 minutes for an hour until someone acknowledges it. The newretryandexpirekeys change those durations. Earlier releases sent priority 2 at normal priority. - A priority outside -2 to 2 is rejected, and a send with an empty message fails before the request.
SMTP
- The new
scopeskey gets access tokens with the OAuth2 client credentials grant, usingclientid,clientsecret, andtokenurlwithout a refresh token. This is the app-only sign-in Microsoft 365 offers before Exchange Online turns off Basic auth for SMTP AUTH at the end of December 2026. The new Microsoft 365 email guide walks through both OAuth2 methods. - A config built in code that leaves
AuthorEncryptionunset now infers the auth method and usesAutoencryption, as a URL without those keys does. TheAuth*andEnc*constants keep their names but have new numeric values, which matters only to code that stores the numbers.
OpsGenie
- A URL with the host
api.atlassian.comsends to Jira Service Management's alert API. Atlassian ends OpsGenie support on April 5, 2027, and the OpsGenie page explains the move. Every other host keeps sending to OpsGenie. - A priority outside
P1toP5is rejected, and a lower-case priority is sent in upper case. - A
visibleToentry that is not a team or a user fails when the URL is read. - A send with neither a title nor a message fails before the request.
Teams
- The webhook URL is checked when the service URL is read, so
shoutrrr verifyreports a bad one. - The webhook URL must be an
httpsPower Automate or Azure Logic Apps workflow URL with asigsignature. Retiredwebhook.office.comconnector URLs, unsigned URLs, andlogic.azure.deURLs are rejected. - Unknown query keys are rejected. A webhook URL that is not percent-encoded in
hostnow fails instead of losing its signature.
MQTT
disabletlsalone decides TLS, and the URL scheme only sets its default.mqtt://...?disabletls=noconnects with TLS, andmqtts://...?disabletls=yesconnects in plaintext, to port 1883 when the URL has no port.- A config built in code uses TLS on port 8883 unless
DisableTLSis set.
Matrix
- A
roomssend param without#or!gets the#prefix, as a room in the URL does. - A room ID without the
!prefix fails its send. - A homeserver given as an IPv6 address with port 443 or 80 written out registers one new device on its next login.
URL Validation
Service URLs that break these rules are now rejected when they are read.
- Bark, Generic, Google Chat, Gotify, Matrix, and Mattermost reject an unbracketed IPv6 host, a port without a host, and a port outside 1 to 65535.
- ntfy, PagerDuty, Rocket.Chat, Signal, and XMPP reject a port outside 1 to 65535, including
:0. - A token or ID that is sent as one path segment can't be
.or..or contain/. This covers the Discord token, the Rocket.Chat tokens, the IFTTT webhook ID and event names, the Notifiarr API key, and the Mattermost token. - The server paths of Bark, Gotify, Google Chat, and Home Assistant and the ntfy topic can't contain a
.or..segment. - Notifiarr rejects query keys that are not Notifiarr settings, in service URLs and custom webhook URLs.
Lists
- Empty elements are dropped from every list setting, so
a,reads asa. Twilio no longer texts an empty number, and a Pushbullet URL ending in/no longer pushes to every device as well.
Send Checks
- A config built in code with an undefined enum value fails before any request. This covers the ntfy priority, the Telegram parse mode, the Signalgrid type, the Signal text mode, the SMTP auth method and encryption, and the MQTT QoS.
Commits
Features
- feat(pushover): support emergency priority by @nicholas-fedor in #2101
- feat(smtp): get OAuth2 access tokens with the client credentials grant by @nicholas-fedor in #2160
- feat(opsgenie): send to jira service management for host api.atlassian.com by @nicholas-fedor in #2165
Fixes
- fix(matrix): keep each room in its own request path segment by @nicholas-fedor in #2048
- fix: keep config values in their request path segments by @nicholas-fedor in #2052
- fix: keep slashes inside path values that services write one per segment by @nicholas-fedor in #2054
- fix: drop empty elements from list settings by @nicholas-fedor in #2058
- fix: reject undefined enum values in configs built in code by @nicholas-fedor in #2060
- fix: apply one URL port rule across services by @nicholas-fedor in #2064
- fix: check url host syntax in every service by @nicholas-fedor in #2066
- fix(mqtt): select tls from the disabletls setting by @nicholas-fedor in #2094
- fix(smtp): match url defaults in configs built in code by @nicholas-fedor in #2099
- fix(opsgenie): check alert fields before sending by @nicholas-fedor in #2106
- fix(notifiarr): reject query keys that are not settings by @nicholas-fedor in #2111
- fix(matrix): keep one device id for an ipv6 homeserver by @nicholas-fedor in #2113
- fix(matrix): match the shared not initialized error by @nicholas-fedor in #2115
- fix: stop config error texts pointing at the URL by @nicholas-fedor in #2121
- fix(format): write markdown URL parts as uppercase placeholders by @nicholas-fedor in #2129
- fix(format): name each field's URL part in the generated config docs by @nicholas-fedor in #2135
- fix(playground): set fields by name through the format package by @nicholas-fedor in #2141
- fix(generators): let the format package parse prompted answers by @nicholas-fedor in #2143
- fix(teams): check the webhook URL when the service URL is read by @nicholas-fedor in #2154
- fix(teams): reject retired workflow domains and unsigned webhook URLs by @nicholas-fedor in #2158
Dependency Updates
- fix(deps): update module github.com/spf13/pflag to v1.1.0 by @renovate[bot] in #2083
- chore(deps): update module golang.org/x/sync to v0.25.0 by @renovate[bot] in #2095
- fix(deps): update module golang.org/x/oauth2 to v0.38.0 by @renovate[bot] in #2096
- chore(deps): update module golang.org/x/mod to v0.43.0 by @renovate[bot] in #2103
- chore(deps): update module golang.org/x/text to v0.44.0 by @renovate[bot] in #2104
- fix(deps): update module golang.org/x/sys to v0.50.0 by @renovate[bot] in #2108
- fix(deps): update module golang.org/x/term to v0.48.0 by @renovate[bot] in #2109
- chore(deps): update go toolchain directive to v1.27.4 by @renovate[bot] in #2149
- chore(deps): update module golang.org/x/crypto to v0.59.0 by @renovate[bot] in #2167
Full Changelog: v0.23.0...v0.24.0