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.

  1. Create an app: https://api.slack.com/apps → Create New App → From scratch → name it AlertINT.

  2. Add the scope: OAuth & Permissions → Bot Token Scopes → add chat:write. That's all AlertINT needs; it never reads the channel.

  3. Install: Install to Workspace → Allow, then copy the Bot User OAuth Token (xoxb-…).

  4. Invite the bot: in your channel, run /invite @AlertINT.

  5. Store the token as an environment variable, never in config.yaml:

    SLACK_BOT_TOKEN=xoxb-...
    
  6. 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:

  1. one AlertINT system notice: how long the gap lasted, how many Situations and messages were delayed
  2. each affected Situation's latest message
  3. 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 one situation.transition JSON line per change; deduplicate by transition_id.