Notifications
Slack
One message per Situation, edited in place, with a thread that records each material change.
AlertINT posts to one channel. Each Situation gets one message, kept current, plus a thread for what changed. Quiet Situations post nothing.
See every state as a Slack example in the Situation workflow (the Slack ↗ buttons).
Set up#
Needs one bot token with one scope.
-
Create an app: https://api.slack.com/apps → Create New App → From scratch → name it AlertINT.
-
Add the scope: OAuth & Permissions → Bot Token Scopes → add
chat:write. That's all AlertINT needs; it never reads the channel. -
Install: Install to Workspace → Allow, then copy the Bot User OAuth Token (
xoxb-…). -
Invite the bot: in your channel, run
/invite @AlertINT. -
Store the token as an environment variable, never in
config.yaml:SLACK_BOT_TOKEN=xoxb-... -
Enable it:
notify: slack: enabled: true bot_token_env: SLACK_BOT_TOKEN # the variable's name, not the token channel: "#alerts" # name or ID
What you'll see#
One Situation, one message, one thread.
| Part | Shows | Changes |
|---|---|---|
| Message | The current picture: what's happening, why it matters, what AlertINT checked, who acts next and by when, and the MCP handle | Edited in place |
| Thread | One reply per material change | Replies are never edited |
| 🧪 DRILL banner | On every part of a drill Situation | Never on real alerts |
What earns a thread reply: first publication, a new or changed Finding, a change in who acts next, Confirming recovery, a refire during it, Recovered, Recovery unconfirmed, and operator notes or verdicts.
What doesn't: routine checks, retries, time passing, and a plain re-fire of an active Situation.
After a terminal end: a later firing opens a new message, linked to the old one. The old message never reopens.
The status line#
The message opens with the current phase, then the full chain with that phase marked:
🔴 Correlating · checkout — …
Observed · ▸ Correlating · Investigating · Monitoring · Confirming recovery · Recovered
| Phase | Means |
|---|---|
| Observed | Alerts seen; no investigation yet |
| Correlating | Collecting related alerts. The message says when investigation starts. |
| Investigating | Triage is running, or waiting to retry |
| Monitoring | Analysis is done; AlertINT is watching the alerts |
| Confirming recovery | All alerts cleared; waiting out the grace period |
| Recovered | Alerts stayed clear. Tracking ended. |
| Recovery unconfirmed | Replaces Recovered when recovery couldn't be established. Tracking ended. |
| Colour | When |
|---|---|
| 🔴 | Alerts firing, or recovery unconfirmed |
| 🔵 | Nothing firing, not yet recovered |
| 🟢 | Recovered |
Below the chain: the Finding, any "expected" note, what happens next and by when (in each reader's timezone), and the alert counts (firing · resolved · unobserved).
When the channel is interrupted#
Editing the message or adding a thread reply never pings anyone. A new main-channel post happens only for:
- the first publication worth posting
- a newly crossed criticality or a newly urgent Situation
- a hand-off: AlertINT was working on it, now a person must act
- a changed required action, after the repage cooldown (
situations.slack.repage_cooldown_seconds, default 15 min)
notify.slack.min_severity sets the floor for these posts. It compares
against the Situation's interruption priority (critical / high /
medium / low), not the alert's severity. critical always passes. A
withheld post is still recorded, and the Situation stays visible over MCP.
When Slack is down#
Nothing is dropped. Every message is saved before it is sent.
| Failure | What AlertINT does |
|---|---|
Timeouts, 5xx, rate limits |
Retries without limit, backing off from 5 s to 5 min, honouring Slack's Retry-After |
| Bad token, lost channel, missing scope | Holds messages until the config is fixed and AlertINT restarts |
| Slack rejects the message outright | Marks it failed, visible over MCP; later replies wait behind it |
After 5 minutes of continuous failure, AlertINT records a delivery gap. When Slack answers again, it posts, in order:
- one
AlertINT systemnotice: how long the gap lasted, how many Situations and messages were delayed - each affected Situation's latest message
- every missed thread reply, in order, with old calls to action marked as delayed
Messages not tied to a Situation#
Two AlertINT system messages cover the installation itself:
| Message | Posted when | Example |
|---|---|---|
| LLM health | The LLM has been unhealthy for health.broadcast_after_seconds (default 5 min) |
⚠️ AlertINT system · LLM unavailable for 5m. New Incident triage is retrying; correlation may be delayed. |
| Delivery gap notice | Slack recovers after a gap | Posted once per gap, before the replay |
The LLM health message is edited in place as the state changes, and once more
on recovery (✅ AlertINT system · LLM recovered after 12m.). One message per
outage, never one per Incident. See
Reliability.
Configure#
Under notify.slack:
| Field | Default | Does |
|---|---|---|
enabled |
false |
Turns Slack on |
bot_token_env |
— | Name of the variable holding the xoxb- token |
channel |
— | Channel name or ID |
min_severity |
low |
Lowest interruption priority that may post to the channel. low posts everything. |
recurrence_mode |
change-gated |
change-gated: one quiet thread reply at ×5, ×10, ×25, ×50, ×100, then every ×100 recurrences. off: the count updates in the message only. |
Limits#
- Rare duplicates. If Slack accepts a message but the answer is lost, AlertINT retries, and a duplicate can appear. AlertINT never reads the channel, so it can't tell. A duplicate is preferred over a missing message.
- Don't delete a Situation's message by hand. Its later updates can't be delivered, and there is no command to repair it.
- Slack is not the record. State, history, and delivery status live in AlertINT. Read them over MCP (
alertint_get_situation,alertint_get_delivery_state). stdout also emits onesituation.transitionJSON line per change; deduplicate bytransition_id.