Integrations

Slack

Send incident notifications to Slack via Block Kit — one timeline-style thread per incident, every lifecycle event.

Slack

Yorker posts incident notifications to Slack via an Incoming Webhook. Slack is the timeline channel — by default it receives every lifecycle event, so your channel becomes a running record of the incident.

For the underlying model (lifecycle states, event types, scoped hypothesis), see Incidents.

Set up

  1. In Slack, create an Incoming Webhook and copy the URL.
  2. In Yorker, go to Settings > Notification Channels, click Create Channel, pick Slack, and paste the webhook URL.
  3. The channel is subscribed to incidents by default. Wire it to any alert rule and it will participate in incident dispatch.

Or via the API:

curl -X POST https://yorkermonitoring.com/api/notification-channels \
  -H "Authorization: Bearer $YORKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ops-channel",
    "channel": {
      "type": "slack",
      "webhookUrl": "https://hooks.slack.com/services/T.../B.../..."
    }
  }'

What gets posted

Slack receives every incident event by default:

  • opened
  • alert_attached
  • severity_changed
  • acknowledged
  • auto_resolved
  • closed
  • reopened
  • note_added

The opened event uses a rich Block Kit layout — header, severity, affected checks, locations, symptom window, shared failing domains, hypothesis, ruled-out list, and a View in Yorker button. Subsequent events are single-section status lines so the thread reads like a timeline.

Example opened payload:

{
  "blocks": [
    { "type": "header", "text": { "type": "plain_text", "text": "🔴 Incident opened — Checkout API outage" } },
    { "type": "section", "text": { "type": "mrkdwn", "text": "*Severity*: `CRITICAL` · *Incident*: <https://yorkermonitoring.com/dashboard/incidents/inc_abc|inc_abc>" } },
    { "type": "section", "fields": [
      { "type": "mrkdwn", "text": "*Affected checks*\nCheckout API" },
      { "type": "mrkdwn", "text": "*Locations*\nloc_us_east_1, loc_eu_west_1" },
      { "type": "mrkdwn", "text": "*Symptom window*\n2026-04-15T09:58:00Z → ongoing" },
      { "type": "mrkdwn", "text": "*Shared failing domains*\napi.stripe.com" }
    ]},
    { "type": "section", "text": { "type": "mrkdwn", "text": "*Hypothesis*\nStripe API is returning 503/504; checkout is blocked." } },
    { "type": "section", "text": { "type": "mrkdwn", "text": "*Ruled out*\n• DNS resolution: NXDOMAIN not observed\n• TLS: handshake completes" } },
    { "type": "context", "elements": [{ "type": "mrkdwn", "text": "Scope: `external_symptoms_only` — Yorker measures external symptoms only" }] },
    { "type": "actions", "elements": [{ "type": "button", "text": { "type": "plain_text", "text": "View in Yorker" }, "url": "https://yorkermonitoring.com/dashboard/incidents/inc_abc" }] }
  ]
}

Template overrides

Every event's default payload can be replaced with a Handlebars-rendered Block Kit JSON string. The template renders against the full incident event context.

curl -X PUT https://yorkermonitoring.com/api/notification-channels/nch_abc \
  -H "Authorization: Bearer $YORKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "incidentTemplate": {
      "channelType": "slack",
      "overrides": {
        "opened": {
          "blocks": "{\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"{{severityEmoji incident.severity}} *{{incident.title}}*\\n{{payload.hypothesis.summary}}\"}}]}"
        }
      }
    }
  }'

The blocks body must render to a JSON object with a blocks: [...] array. A render error or a parse error falls back to the default payload and logs a warning. A bad template never fails dispatch.

Use "default" as the event key to define a single override that applies to every event type that doesn't have its own entry.

Available helpers

  • {{severityEmoji incident.severity}}🔴 / 🟡 / 🔵
  • {{eventEmoji eventType}}🚨 / ➕ / 🔺 / 👤 / ✅ / ☑️ / 🔁 / 📝
  • {{upperCase str}}, {{titleCase str}}
  • {{join array ", "}}
  • {{#ifHasSource "synthetic_http"}}…{{/ifHasSource}}synthetic_http, synthetic_browser, or synthetic_mcp
  • {{jsonBody payload}} — splat a value as raw JSON (already JSON.stringifyd). In JSON-producing channels (Slack, webhook, PagerDuty, ServiceNow) escaping is disabled, so {{jsonBody x}} and {{{jsonBody x}}} are equivalent. In email HTML templates the double-stash form is HTML-escaped by default — use triple-stash only as an explicit opt-out.

Render context

The full context mirrors serializeIncidentEventForExport:

  • eventId, eventType, incidentId, teamId, occurredAt
  • actor{ type: "user" | "system", id }
  • payload — the full event payload (observations, hypothesis, event-specific fields)
  • incident{ incidentId, title, severity, state, openedAt, triageUrl }

Disabling incident routing

To fall back to the legacy per-alert Slack dispatch, set incidentSubscribed: false on the channel:

curl -X PUT https://yorkermonitoring.com/api/notification-channels/nch_abc \
  -H "Authorization: Bearer $YORKER_API_KEY" \
  -d '{ "incidentSubscribed": false }'