Notifications Configuration
SolidPing supports multiple notification channels to alert you when incidents occur.
Supported Channels
| Channel | Status | Configuration |
|---|---|---|
| Slack | Available | OAuth integration |
| Microsoft Teams (bot) | Available | Azure Bot / Bot Framework (two-way) |
| Microsoft Teams (webhook) | Available | Teams Workflow webhook (one-way) |
| Discord | Available | Bot (OAuth install, two-way) or webhook (one-way) |
| Available | SMTP | |
| Webhooks | Available | HTTP POST |
| Google Chat | Available | Webhook |
| Mattermost | Available | Webhook |
| ntfy | Available | HTTP push |
| Gotify | Available | HTTP push |
| Matrix | Available | Client-Server API |
| Zulip | Available | Bot API |
| PagerDuty | Available | Events API v2 |
| Pushover | Available | API integration |
| Web Push | Available | Browser push (VAPID) |
| SMS / Voice | Available | SMS & Voice (server-provided by default, per-organization Twilio as an override) |
| Available | Meta WhatsApp Business Cloud API (instance-level) | |
| Telegram | Available | Telegram Bot API (instance-level bot, free per message) |
Email (SMTP)
Configure email notifications via SMTP.
Environment Variables
SP_EMAIL_ENABLED=true
SP_EMAIL_HOST=smtp.example.com
SP_EMAIL_PORT=587
SP_EMAIL_USERNAME=noreply@example.com
SP_EMAIL_PASSWORD=your-smtp-password
SP_EMAIL_FROM=noreply@example.com
SP_EMAIL_FROMNAME=SolidPing
SP_EMAIL_AUTHTYPE=login
SP_EMAIL_PROTOCOL=starttls
SP_EMAIL_INSECURESKIPVERIFY=false
Configuration File
email:
enabled: true
host: smtp.example.com
port: 587
username: noreply@example.com
password: your-smtp-password
from: noreply@example.com
from_name: SolidPing
auth_type: login
protocol: starttls
insecure_skip_verify: false
Auth Types
| Type | Description |
|---|---|
plain | PLAIN authentication |
login | LOGIN authentication (default) |
cram-md5 | CRAM-MD5 authentication |
Protocol Options
| Protocol | Description |
|---|---|
none | No encryption |
starttls | STARTTLS (default) |
ssl | SSL/TLS |
Provider Examples
Gmail
SP_EMAIL_HOST=smtp.gmail.com
SP_EMAIL_PORT=587
SP_EMAIL_USERNAME=your-email@gmail.com
SP_EMAIL_PASSWORD=your-app-password # Use App Password, not your account password
SP_EMAIL_AUTHTYPE=login
Gmail requires an App Password when 2FA is enabled. Generate one at https://myaccount.google.com/apppasswords
SendGrid
SP_EMAIL_HOST=smtp.sendgrid.net
SP_EMAIL_PORT=587
SP_EMAIL_USERNAME=apikey
SP_EMAIL_PASSWORD=your-sendgrid-api-key
SP_EMAIL_AUTHTYPE=login
Amazon SES
SP_EMAIL_HOST=email-smtp.us-east-1.amazonaws.com
SP_EMAIL_PORT=587
SP_EMAIL_USERNAME=your-ses-smtp-username
SP_EMAIL_PASSWORD=your-ses-smtp-password
SP_EMAIL_AUTHTYPE=login
Mailgun
SP_EMAIL_HOST=smtp.mailgun.org
SP_EMAIL_PORT=587
SP_EMAIL_USERNAME=postmaster@your-domain.mailgun.org
SP_EMAIL_PASSWORD=your-mailgun-smtp-password
SP_EMAIL_AUTHTYPE=login
Slack
Slack integration uses OAuth for secure access.
Environment Variables
SP_SLACK_APP_ID=A0XXXXXXXXX
SP_SLACK_CLIENT_ID=1234567890.1234567890123
SP_SLACK_CLIENT_SECRET=your-client-secret
SP_SLACK_SIGNING_SECRET=your-signing-secret
Configuration File
slack:
app_id: A0XXXXXXXXX
client_id: "1234567890.1234567890123"
client_secret: your-client-secret
signing_secret: your-signing-secret
Setting Up a Slack App
- Go to https://api.slack.com/apps
- Click "Create New App" → "From scratch"
- Name it "SolidPing" and select your workspace
- Go to "OAuth & Permissions" and add scopes:
chat:writechat:write.publicchannels:readim:history— required for direct-message capture
- Go to "Basic Information" to get your credentials
- Install the app to your workspace
The reference manifests in wiki/slack/ are the authoritative scope list; the
five above are the minimum for alerting plus DM capture.
Slash commands
| Command | What it does |
|---|---|
/check <url> | Creates an HTTP check for the URL. |
/comment [#42] <text> | Adds a comment to an incident's timeline. |
/comment is the explicit way to add a comment from Slack. A slash command
posts nothing visible in the channel, so SolidPing answers with an ephemeral
confirmation and then fans the comment out to every channel attached to the
failing check — including the incident's own Slack thread, so the channel that
typed it still sees it.
Slack's payload for a slash command does not include thread_ts, so the
command cannot tell which thread it was typed in. The incident is resolved from
the channel instead:
- An explicit
#42always wins. - Otherwise, if the channel has exactly one active incident thread, that is the target.
- Otherwise you get an ephemeral error listing the candidates — SolidPing never guesses which incident a note belongs to.
Capturing thread replies (comment_ingestion)
Each Slack integration has a "Capture every thread reply as a comment"
toggle on its edit page, stored as comment_ingestion:
| Value | Behavior |
|---|---|
explicit (default) | Only /comment creates an incident comment. Triage chatter in the thread — "lunch?", "who is on call?" — stays chatter. |
all | Every human reply in a tracked incident thread is saved to the incident timeline. |
An integration created before this setting existed carries no value and is
therefore treated as explicit, so no workspace keeps over-capturing after an
upgrade. Bot-authored messages (including SolidPing's own thread replies) are
never ingested in either mode.
Direct messages and the support inbox
A direct message to the SolidPing bot is captured in the instance
support inbox instead of being dropped. Channel
messages and @SolidPing mentions are unaffected — only channel_type: "im" is
captured.
This needs the im:history bot scope, and Slack does not grant new scopes to
an existing install. Every workspace that connected before SolidPing started
requesting it keeps its old grant, and Slack simply never delivers message.im
to us — so DMs to the bot go nowhere and the inbox looks empty rather than
broken.
Reinstalling the app is the whole fix, and it changes nothing else about the integration. SolidPing surfaces the state in three places so it does not have to be discovered from a silence:
- the integration's edit page shows a "Direct messages are not being captured" banner with a Reinstall Slack app button;
- the server logs one warning at boot naming how many workspaces still owe a reinstall;
- the
solidping_support_dm_unavailable{channel="slack"}gauge reports the same number for alerting.
Discord needs no equivalent step: its DM support is a gateway intent, not a user-granted scope.
Notification Format
Slack notifications include:
- Service name and URL
- Status change (Up → Down, Down → Up)
- Error details (for failures)
- Direct link to the check in SolidPing
Microsoft Teams
There are two Teams integrations, and they are independent — pick either, or use both:
| Integration type | What it does | What it needs |
|---|---|---|
msteams | One-way: posts Adaptive Cards into a channel | A Teams Workflow URL. No server configuration, works behind a firewall. |
msteams-bot | Two-way: alerts plus @SolidPing commands, and incident cards that update in place | An Entra ID app + Azure Bot, and a publicly reachable HTTPS endpoint. |
Microsoft Teams (webhook) — the zero-infra option
- In Teams, open Workflows → "Post to a channel when a webhook request is received"
- Finish the wizard and copy the workflow URL it gives you
- Paste it into a
msteamsintegration in SolidPing
The legacy "Incoming Webhook" Office 365 connector is retired by Microsoft and will not work.
Microsoft Teams (bot) — the Slack-grade option
The Bot Framework has no Socket-Mode equivalent: Microsoft's servers push activities to your instance, they never dial out from it. Your SolidPing instance must therefore be reachable from the public internet over HTTPS at:
https://<your-base-url>/api/v1/integrations/msteams/messages
A self-hosted instance behind a firewall or on a private network cannot use
the Teams bot. Use the msteams webhook integration instead — it only makes
outbound requests.
1. Register the Entra app and Azure Bot
- In the Azure portal, create an Azure Bot resource (multi-tenant for a shared deployment, single-tenant for your own).
- Note the Microsoft App ID and create a client secret.
- Set the bot's messaging endpoint to
https://<your-base-url>/api/v1/integrations/msteams/messages. - Enable the Microsoft Teams channel on the bot.
2. Configure SolidPing
SP_MSTEAMS_ENABLED=true
SP_MSTEAMS_APP_ID=00000000-0000-0000-0000-000000000000
SP_MSTEAMS_APP_SECRET=your-client-secret
# Optional: restrict inbound activities to a single Microsoft 365 tenant.
SP_MSTEAMS_TENANT_ID=11111111-1111-1111-1111-111111111111
msteams:
enabled: true
app_id: 00000000-0000-0000-0000-000000000000
app_secret: your-client-secret
tenant_id: "" # empty = multi-tenant
msteams.enabled defaults to false — the bot stays off until you turn it
on, precisely because of the public-endpoint requirement above.
3. Install the app in Teams and link your tenant
-
In SolidPing, create a Microsoft Teams (bot) integration and open it.
-
Click Download Teams app package — the zip is generated with your instance's app ID and URL already filled in, so nothing has to be edited.
-
In Teams: Apps → Manage your apps → Upload a custom app, pick the zip, and add SolidPing to a team.
-
Back in SolidPing, click Connect Microsoft Teams. You get a one-time link code.
-
In a Teams channel the bot was added to, send:
@SolidPing link ABCDE-FGHIJThe bot confirms in the channel, and the integration is connected.
Every channel you add the bot to becomes a selectable notification destination; the first one becomes the default.
A Microsoft 365 tenant ID is a semi-public identifier, not a secret, so simply typing one into a form proves nothing — anyone could claim any tenant, and then receive that tenant's channel names and post into its channels. Bot Framework has no OAuth redirect that could carry your SolidPing organization through the install the way Slack's does, so the link code fills that gap: SolidPing issues it to a signed-in admin of one organization, and it can only be redeemed from inside a real, Microsoft-signed message. Quoting the code back is what proves both sides are the same actor.
The code is single-use and expires after 30 minutes. A tenant can be linked to exactly one SolidPing organization. The command must be run in a team channel, not a private chat, so the link is visible to your team.
The link code proves that whoever redeems it holds a code issued to a signed-in SolidPing admin — it does not prove that the person redeeming it is an owner of the Teams team. Microsoft's Bot Connector does not tell a bot the sender's role, and determining it would require Microsoft Graph permissions and admin consent that this integration deliberately does not request. In practice that means any member of a team the bot has been added to could redeem a leaked code.
Treat a link code like a password: generate it when you are ready to use it, paste it once, and let it expire otherwise.
If a tenant ends up linked to the wrong organization, the tenant's own admin can recover without SolidPing support: remove the SolidPing app from the tenant in Teams, then reinstall it and link again with a code from the correct organization. Uninstalling releases the tenant's claim — it is the one lever the party who actually controls the tenant always holds. (An admin of the holding organization can also simply delete the integration.)
Publishing to the Teams store is out of scope — custom app upload ("sideloading") is the supported path, which your Teams admin may need to allow in the Teams admin center.
What the bot can do
- Posts an Adaptive Card when an incident opens, updates that same card when the incident escalates or resolves, and replies under it so a channel shows one incident as one grouped conversation.
- Answers
@SolidPingcommands in a channel:@SolidPing help@SolidPing checks add <url>/checks list/checks rm <slug>@SolidPing results -check <slug>@SolidPing incidents list [-check <slug>]@SolidPing config default-channel— makes the current channel the default notification target (Teams has no cross-team channel reference, so the command is scoped to the team it is issued in)@SolidPing link <code>— connects this Microsoft 365 tenant to a SolidPing organization (see step 5 above)
- Stops routing when the app is removed from the tenant, and resumes on reinstall without losing the org's notification wiring.
Personal-scope direct messages are not supported yet.
Security
Every inbound activity is authenticated before anything is acted on. The Bot
Connector JWT is verified against Microsoft's published JWKS, and the issuer,
audience (your app ID), signing algorithm, validity window and serviceurl
binding are all checked — the serviceurl check is mandatory, so a captured
token cannot be replayed with a different body to redirect SolidPing's outbound
calls.
Microsoft's Connector-to-Bot token carries no tenant claim, so tenant identity
comes from the activity body — which is trustworthy precisely because the
request carrying it passed that signature check. Ownership of a tenant is a
separate question, and is what the link code establishes. When
msteams.tenant_id is set, activities from any other tenant are rejected.
Notification destinations are held to the same standard. A Teams conversation ID is discoverable (it appears in "Get link to channel" URLs), so SolidPing only accepts a destination the bot has actually been added to — enforced when the channel is picked in the dashboard, when a per-check override is set, and again at send time. Naming a conversation ID you happen to know is not enough to make SolidPing post there.
Known limitations
- Managed-identity bots are not supported. Outbound tokens are minted with
a client secret, against the multi-tenant endpoint by default or your own
tenant's endpoint when
msteams.tenant_idis set. A bot registered with a user-assigned managed identity takes its token from the instance metadata endpoint instead, which is a different credential model. - Teams store publication is out of scope — custom app upload only.
- The link command cannot verify the sender is a team owner — see the caution above for the reasoning and the recovery path.
Discord
Discord comes in two flavours, and an organization may use either.
| Bot | Webhook | |
|---|---|---|
| Setup | Install the SolidPing bot into your server | Paste a webhook URL |
| Alerts | Rich embed | Rich embed |
| One thread per incident | Yes | No — every event is a new post |
| Resolve edits the original message | Yes | No |
| Acknowledge button | Yes | No |
| On-call @-mentions | Yes | No |
| Direct message to one person | Yes | No |
| Slash + mention commands | Yes | No |
| Thread replies → incident comments | Yes (needs the Gateway) | No |
| Needs instance-level configuration | Yes | No |
The bot is the recommended mode. The webhook mode is unchanged and still fully supported: an integration created before the bot existed keeps working exactly as it did, with no migration, and on an instance where no Discord bot is configured at all.
Setting up the bot (organization admins)
- Open the Discord integration in SolidPing and press Install Discord bot.
- Approve the install for your server in Discord.
- Back in SolidPing, pick the channel alerts should go to.
- Optionally turn on Mention the on-call person and Capture every thread reply as a comment.
SolidPing requests only the permissions it uses: View Channel, Send Messages, Embed Links, Read Message History, Create Public Threads, Send Messages in Threads and Manage Threads. Manage Threads is what lets SolidPing re-open a thread Discord auto-archived, so a long incident's "resolved" notice still lands in the incident's own thread instead of vanishing.
Direct messages to one person
Discord can page an individual, not just a channel. There are two separate things with the same name, and they are configured in different places:
| Personal DM contact | DM destination | |
|---|---|---|
| Who sets it up | The member, under Account → Notifications | An org admin, on the Discord integration |
| What it is | A notification route the escalation policy pages | A channel destination that happens to be a DM |
| What arrives | Only the incidents that page you | Every alert the integration sends |
| Needs an org Discord integration | No | Yes |
Connecting your own Discord (members)
Under Account → Notifications, the Discord row offers one of two things:
- Connect Discord — you have signed in with Discord before, so SolidPing already knows your account and binds it in one click;
- Link Discord — you have not, so it sends you through Discord once and brings you back.
There is deliberately no field to type a Discord user id into, and the API rejects one. A Discord user id is public — anyone can copy a stranger's out of a Discord client in two clicks — so accepting a typed one would let any user point our incident DMs at somebody else's account, indefinitely, with nothing telling that person where the messages came from. Both connect paths are bindings Discord itself attested, which is why neither needs a verification code.
A DM contact goes through the instance bot, the way Telegram does, so it needs no Discord integration in your organization. It does need the instance to have a configured bot — the row is simply absent otherwise.
Pages that arrive as a DM carry the same Acknowledge button a channel alert does, and pressing it acknowledges the incident exactly as it would in a channel. Only someone whose Discord account is connected to a member of that incident's organization can do so.
Routing a check's alerts to one person (admins)
On a bot-installed Discord integration the destination picker has Channel and
Direct message tabs. The DM tab lists the organization's members whose
Discord account SolidPing can already resolve — an admin mapping, a personal
Discord contact, or a Discord sign-in. It is not the server's member list:
reading that needs the privileged GUILD_MEMBERS intent, and it would tell us
who is in the server without telling us which SolidPing account any of them is.
A DM destination behaves like a channel one with two differences, both forced by Discord: a DM cannot host a thread, so follow-ups post as plain messages that link back to the original instead of threading under it; and the on-call mention is skipped, because a DM already has exactly one reader.
When Discord refuses a DM
Discord will not let a bot DM someone who has direct messages from server members switched off, who has blocked it, or who shares no server with it. That is not something an operator can configure around — it is the recipient's own privacy setting.
Use the Test button to find out. On Account → Notifications it reports the refusal in as many words ("open your DMs for server members, or join the server the bot is in") rather than a generic failure, and the DM tab reports it at pick time rather than during your first real incident.
When a refusal happens during real paging, SolidPing treats it as this route cannot be used and falls through to the member's next notification route. It is not counted as a delivery and not reported as an outage.
Instance-level configuration (operators)
The bot is a single Discord application per SolidPing instance. Configure it under Server → Discord:
| Setting | Env var | Purpose |
|---|---|---|
| Client ID | SP_DISCORD_CLIENT_ID | Identifies the application during install |
| Client secret | SP_DISCORD_CLIENT_SECRET | Completes the install token exchange |
| Bot token | SP_DISCORD_BOT_TOKEN | Authenticates every outbound call |
| Public key | SP_DISCORD_PUBLIC_KEY | Verifies Discord's signed interaction requests |
| Gateway enabled | SP_DISCORD_GATEWAY_ENABLED | Turns on the inbound WebSocket |
Three things are worth understanding before you turn the bot on:
-
The bot needs all four values, and it fails closed without them. Discord login runs on the client id and secret alone, so it is entirely possible to have sign-in working and no bot at all. When the bot token or the public key is missing, SolidPing does not mount the install routes and the dashboard does not offer the install button — the channel is simply not offered, rather than dead-ending at Discord. One warning naming the missing variables is logged at boot.
-
The public key is not optional. Discord probes the interactions endpoint with deliberately invalid signatures and deactivates it if those probes are not rejected. With no public key configured, SolidPing rejects every interaction rather than trusting it — so buttons and slash commands do nothing until the key is set.
-
The Gateway needs the privileged
MESSAGE_CONTENTintent. Buttons and slash commands arrive over HTTPS, but Discord has no HTTP event subscription for ordinary messages — everything a human types (a thread reply that should become an incident comment, an@SolidPing checks list) only arrives over the Gateway. Enable the intent under Bot → Privileged Gateway Intents in the Discord Developer Portal. Discord grants it freely while your application is in fewer than 100 servers and requires a review above that, so request it early if you expect to grow. Without the intent the bot connects and looks healthy, but every message arrives with empty content and it silently ignores everything.
Full operator setup notes live in wiki/discord/README.md in the SolidPing
repository.
Commands
Available as slash commands (/solidping …) and as bot mentions
(@SolidPing …):
| Command | What it does |
|---|---|
checks list | List monitored checks |
checks add <url> | Start monitoring a URL |
checks rm <slug> | Stop monitoring a check |
incidents list [check] | Recent incidents |
comment [#N] <text> | Add a note to an incident — inside an incident thread the number is optional |
config default-channel #channel | Change where alerts go |
help | Command reference |
Setting up a webhook instead
- In your Discord server, go to Server Settings → Integrations
- Click "Webhooks" → "New Webhook"
- Name it "SolidPing" and select the channel
- Copy the Webhook URL
- Add the webhook URL in SolidPing's integration settings
Webhook URL format:
https://discord.com/api/webhooks/{webhook.id}/{webhook.token}
Google Chat
Google Chat notifications use incoming webhooks.
Setting Up Google Chat Webhooks
- In Google Chat, open the space where you want notifications
- Click the space name → "Apps & integrations" → "Manage webhooks"
- Click "Add webhook", name it "SolidPing", and copy the URL
- Add the webhook URL in SolidPing's integration settings
Mattermost
Mattermost notifications use incoming webhooks, similar to Slack.
Setting Up Mattermost Webhooks
- In Mattermost, go to Main Menu → Integrations → Incoming Webhooks
- Click "Add Incoming Webhook"
- Select the channel and fill in the details
- Copy the webhook URL
- Add the webhook URL in SolidPing's integration settings
ntfy
ntfy is a simple HTTP-based pub/sub notification service. SolidPing can push notifications to any ntfy topic.
Configuration
Add a ntfy connection in SolidPing with:
- Server URL:
https://ntfy.sh(or your self-hosted instance) - Topic: Your topic name (e.g.,
solidping-alerts)
Example
# Subscribe to notifications
ntfy subscribe solidping-alerts
Gotify
Gotify is a simple self-hosted push notification server, popular alongside ntfy in the homelab/self-hosted crowd. SolidPing posts alerts to a Gotify application via its HTTP API.
Configuration
- In your Gotify web UI, go to Apps and create a new application for SolidPing. Copy the generated application token.
- Add a Gotify connection in SolidPing with:
- Server URL: your Gotify instance's base URL (e.g.
https://gotify.example.com) — Gotify is always self-hosted, there's no default. - Application token: the token from step 1. Treated as a secret — stored encrypted, never shown again after you save it.
- Priority (optional): the default Gotify priority (0-10) for alerts, default
5. A resolved incident always sends at a low priority (2) so a recovery doesn't re-buzz your phone the way a page does.
- Server URL: your Gotify instance's base URL (e.g.
Notifications carry a click-through link to the incident when SolidPing's base URL is configured, so tapping the push notification opens the incident directly.
Matrix
Matrix is an open, decentralized chat protocol — the standard for self-hosted chat (Element, Synapse, Conduit, beeper). SolidPing sends notifications as a dedicated bot user posting into a room, via the Matrix Client-Server API — no bridge or generic webhook needed.
Configuration
Add a Matrix connection in SolidPing with:
- Homeserver URL: the base URL of the bot account's homeserver (e.g.
https://matrix.org, or your self-hosted Synapse/Conduit instance). A trailing slash is fine. - Access token: the bot/dedicated user's access token. Treated as a secret — stored encrypted, never shown again after you save it.
- Room: the room ID (
!abcdef:matrix.org) or alias (#alerts:matrix.org) to post into. The bot account must already be invited to and have joined the room — SolidPing does not auto-join.
Creating a bot user and access token
- Register a dedicated account for SolidPing on your homeserver (don't reuse a personal account — the access token can post as that user indefinitely).
- Get an access token for that account, either:
- In Element: sign in as the bot user, then go to Settings → Help & About → Advanced → Access Token, or
- Via the API:
POST /_matrix/client/v3/loginwith the bot's credentials, which returns anaccess_tokenin the response.
- Find the room ID: in Element, open the room, go to Room settings → Advanced, and copy
the internal room ID (starts with
!). A room alias (starts with#) also works — SolidPing resolves it to the current room ID on every send. - Invite the bot account to the room and accept the invite as the bot (join it) — SolidPing only posts into rooms the bot is already a member of.
Limitations
- End-to-end encrypted rooms are not supported (posting into one would require a full Matrix crypto SDK). Use an unencrypted room for alerts.
- SolidPing does not auto-join a room on invite — join it manually as the bot account first.
Zulip
Zulip is an open-source chat tool whose threading model — every message lives in a named topic inside a stream — maps naturally onto "one thread per incident". SolidPing posts through Zulip's bot API, using the same topic string for every lifecycle event of an incident (created, escalated, comments, acknowledgment, resolved) so Zulip threads the whole incident together automatically, without any extra configuration.
Configuration
Add a Zulip connection in SolidPing with:
- Site URL: your Zulip realm's base URL (e.g.
https://acme.zulipchat.comfor Zulip Cloud, or your self-hosted realm's URL). - Bot email: the bot's email identity (e.g.
solidping-bot@acme.zulipchat.com). - API key: the bot's API key. Treated as a secret — stored encrypted, never shown again after you save it.
- Stream: the name of the stream (channel) to post into. The bot must already be subscribed to it — SolidPing does not auto-subscribe.
Creating a bot and finding its API key
- In your Zulip organization, go to Settings → Personal → Bots (or, for an organization-wide bot, Organization settings → Bots) and add a new bot — a Generic bot is the right type for posting alerts.
- Zulip shows the bot's email and API key on its bot detail page immediately after creation. If you need the key again later, open the bot's row in Bots and click the key icon to reveal it.
- Subscribe the bot to the stream you want SolidPing to post into — as an organization admin, add the bot's email as a subscriber from Organization settings → Channels (streams), or invite it into the stream the same way you'd invite any other user.
Topic per incident
Each incident's Zulip topic is derived deterministically from the check name and the
incident's short reference, e.g. API health (#42), truncated to Zulip's 60-character
topic limit when needed. Every event for that incident reuses the same string, so the
whole lifecycle — the initial alert, any escalation, comments, an acknowledgment, and the
eventual resolution — reads as one continuous thread in Zulip instead of separate,
unrelated messages.
When a check name is long enough that the full string would exceed 60 characters, the
check name is shortened, never the (#42) reference — that reference is the only part of
the topic that tells two incidents on the same check apart, so it always survives intact.
PagerDuty
PagerDuty integration for incident management and on-call alerting, via the Events API v2 only — no OAuth, no REST API v2, no schedule import.
Configuration
Add a PagerDuty connection in SolidPing with:
- Integration key: The routing key of a PagerDuty Events API v2 integration
Setting Up PagerDuty
- In PagerDuty, open the service that should receive SolidPing's alerts
- Go to Integrations → Add integration
- Choose Events API v2
- Copy the generated integration key
- Add it in SolidPing's integration settings
Behavior
- An incident opening (or reopening) sends a
triggerevent; resolving it sends aresolveevent. Both carry the incident's UID as thededup_key, so they correlate to one PagerDuty incident across its whole lifecycle. - A comment or an escalation sends nothing to PagerDuty: the Events API
v2 has no note/annotation concept, and reusing the
dedup_keyon atriggerwould re-open an already-resolved incident. - Severity (
critical/error/warning/info) is derived from the result that triggered or is currently failing the incident.
Pushover
Pushover delivers real-time notifications to your mobile devices and desktop.
Configuration
Add a Pushover connection in SolidPing with:
- User Key: Your Pushover user key
- API Token: Your Pushover application API token
Setting Up Pushover
- Create an account at https://pushover.net
- Create a new Application/API Token for SolidPing
- Add your user key and API token in SolidPing's integration settings
Web Push
Web Push delivers browser notifications straight to a user's device — no third-party service required. It is typically used as an on-call/escalation contact so an operator gets paged even when the dashboard tab is closed.
Web Push relies on VAPID keys that the server holds. Once configured, each user opts in from their notification settings, and the browser registers a push subscription.
Environment Variables
SP_WEBPUSH_ENABLED=true
SP_WEBPUSH_VAPID_PUBLIC_KEY=your-vapid-public-key
SP_WEBPUSH_VAPID_PRIVATE_KEY=your-vapid-private-key
SP_WEBPUSH_SUBJECT=mailto:admin@example.com # contact URI advertised to push services
Configuration File
webpush:
enabled: true
vapid_public_key: your-vapid-public-key
vapid_private_key: your-vapid-private-key
subject: mailto:admin@example.com
Generate a VAPID key pair with any web-push tooling (e.g. npx web-push generate-vapid-keys).
Keep the private key secret.
Webhooks
Generic webhooks send HTTP POST requests to any URL.
Payload Format
{
"event": "incident.created",
"timestamp": "2024-01-15T10:30:00Z",
"check": {
"uid": "550e8400-e29b-41d4-a716-446655440000",
"name": "API Health Check",
"url": "https://api.example.com/health"
},
"incident": {
"uid": "550e8400-e29b-41d4-a716-446655440001",
"status": "active",
"started_at": "2024-01-15T10:30:00Z"
},
"result": {
"status": "down",
"duration_ms": 5000,
"error": "Connection timeout"
}
}
Event Types
| Event | Description |
|---|---|
incident.created | New incident (check started failing) |
incident.escalated | Incident reached escalation threshold |
incident.resolved | Incident resolved (check recovered) |
Webhook Configuration
In the SolidPing dashboard:
- Go to Settings → Integrations
- Add a new Webhook connection
- Configure:
- URL: Your webhook endpoint
- Method: POST (default)
- Headers: Custom headers (e.g., Authorization)
- Secret: HMAC secret for signature verification
Signature Verification
Webhooks include an X-Signature-256 header containing an HMAC-SHA256 signature:
import hmac
import hashlib
def verify_webhook(payload, signature, secret):
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Incident Thresholds
Configure when notifications are sent per check:
| Setting | Default | Description |
|---|---|---|
incident_threshold | 1 | Failures before creating incident |
escalation_threshold | 3 | Failures before escalation |
recovery_threshold | 1 | Successes before resolving |
Example
incident_threshold: 2 # Notify after 2 consecutive failures
escalation_threshold: 5 # Escalate after 5 consecutive failures
recovery_threshold: 2 # Resolve after 2 consecutive successes
For rotation schedules and multi-step paging (page the next person if nobody acknowledges), see On-Call & Escalation.
Testing Notifications
Test your notification setup:
- Create a check for a known-failing endpoint
- Wait for the incident to trigger
- Verify notifications are received
- Fix the endpoint and verify resolution notification
Or use the API:
# Send test notification (requires auth)
curl -X POST http://localhost:4000/api/v1/orgs/default/connections/{uid}/test \
-H "Authorization: Bearer $TOKEN"