Skip to Content
CLINotifications

Notifications

sfdt can push deployment events and org-health snapshots to Slack, Microsoft Teams, Google Chat, email, a generic webhook, or Grafana Loki — with per-channel event filtering and severity routing.

The multi-channel channels[] system is gated by notifications.enabled. The older single-Slack shape (notifications.slack.webhookUrl) is still honored when features.notifications: true. See Configuration.

Configuration

{ "features": { "notifications": true }, "notifications": { "enabled": true, "summary": { "enabled": false }, "channels": [ { "type": "slack", "name": "team-deploys", "webhookUrlEnv": "SLACK_WEBHOOK_URL", "severityThreshold": "warn", "events": ["deploy-failure", "snapshot"] }, { "type": "teams", "name": "ops", "webhookUrlEnv": "TEAMS_WEBHOOK_URL", "severityThreshold": "fail", "events": ["snapshot"] }, { "type": "googlechat", "name": "gchat-ops", "webhookUrlEnv": "GCHAT_WEBHOOK_URL", "severityThreshold": "warn", "events": ["deploy-failure", "snapshot"] }, { "type": "email", "name": "admins", "from": "ci@example.com", "to": ["admin@example.com"], "smtp": { "hostEnv": "SFDT_SMTP_HOST", "portEnv": "SFDT_SMTP_PORT", "userEnv": "SFDT_SMTP_USER", "passwordEnv": "SFDT_SMTP_PASS" }, "severityThreshold": "fail", "events": ["snapshot"] }, { "type": "webhook", "name": "loki", "format": "loki", "url": "https://logs-prod.grafana.net/loki/api/v1/push", "severityThreshold": "warn", "events": ["snapshot"] } ] } }

Secrets are referenced by env-var name, never inline. Webhook URLs and SMTP credentials are given as the name of an environment variable (webhookUrlEnv, urlEnv, SMTP *Env) — the value is read from process.env at send time and is never stored in config. Inline webhookUrl/url are still supported for back-compat but discouraged.

The same applies to webhook auth headers — use headersEnv, not headers, for anything secret.

Authenticating a webhook channel

headers takes literal values, so a token placed there is written into .sfdt/config.json. Use headersEnv instead — it maps a header name to the name of the env var holding its value:

{ "type": "webhook", "name": "n8n", "webhookUrlEnv": "HARNESS_WEBHOOK_URL", "headersEnv": { "X-Auth-Token": "HARNESS_WEBHOOK_TOKEN" } }

headersEnv overrides a literal headers entry of the same name, so an existing config can adopt it one key at a time. If a named env var is unset or empty, that channel fails with an error naming the variable — it does not fall back to sending the request unauthenticated. Other channels are unaffected.

Works with n8n Header Auth, Cloudflare Access service tokens, and any gateway expecting a shared-secret header.

Channel fields

FieldApplies toMeaning
typeallslack | teams | googlechat | email | webhook
nameallFriendly label (shown in CLI/GUI results)
webhookUrl / webhookUrlEnvslack, teams, googlechatIncoming-webhook URL (inline or env-var name)
url / urlEnvwebhookEndpoint URL (inline or env-var name)
formatwebhookjson (default) or loki (Grafana Loki push)
headerswebhookExtra HTTP headers (literal values)
headersEnvwebhookHeader name → name of the env var holding its value. Use this for auth tokens.
from, to, smtpemailSender, recipients, and SMTP env-var names
severityThresholdallMinimum snapshot severity to receive (ok | warn | error | fail)
eventsallWhich events this channel receives (omit = all)

Email uses nodemailer, an optional dependency that is lazy-loaded only when an email channel is configured.

Events & severity routing

  • Lifecycle eventsdeploy-success, deploy-failure, test-failure, release-created, harness-escalation — are sent to any channel whose events filter allows them.
  • The snapshot event (an audit/monitor run) is routed by severity: a channel receives it only when the snapshot’s worst status is at or above the channel’s severityThreshold.

Severity rank: ok < warn < error < fail.

AI executive-summary digests

Set notifications.summary.enabled = true to have snapshot notifications carry a 1–2 paragraph AI executive summary (top issue + next steps) instead of the raw findings list. It requires features.ai and an available AI provider; the summary is computed once and reused across channels, and falls back to the normal body when AI is unavailable. The wording is controlled by the editable monitor-summary prompt (see AI Providers).

Commands

sfdt notify deploy-failure --org prod --message "Release 1.4 failed" sfdt notify snapshot --type monitor # push the latest monitor snapshot sfdt notify snapshot --type audit sfdt monitor all --notify # run + notify in one step sfdt audit all --notify

See Org Health & Operations for the audit/monitor commands.

What leaves your machine

Every notification body is redacted before it is sent — session ids, force:// auth URLs, bearer tokens, private-key blocks and password-shaped CLI arguments are replaced with [REDACTED] markers. This applies to all channel types, including email and the snapshot object embedded in webhook and Loki payloads.

Since 0.24.0. Two gaps closed here. Redaction ran per webhook shape and skipped the email branch, so an email channel relayed run output through your own SMTP server unredacted. And the destination check covered webhookUrl/url but not an email channel’s to list — so a committed .sfdt/config.json could name a recipient and mail your org snapshots there with no warning. A literal recipient list now needs the same SFDT_ALLOW_UNSAFE_CONFIG=1 opt-in as a literal webhook URL. Use webhookUrlEnv / environment-sourced values for anything the repository should not fix.

Since 0.25.0: webhook redirects are refused. A 3xx response used to be followed, and 307/308 preserve the method and the body — so an endpoint answering 307 Location: https://elsewhere.example received your notification body along with any token in headersEnv. That defeated the destination check rather than merely lacking depth: the check validates the URL you configured and has no say over where a redirect points. A redirect is now an error naming the Location. Configure the final URL directly.

Surfaces

  • GUI — the Notifications page lists configured channels (secrets redacted), shows whether the AI summary is on, and offers a “send test to all channels” button.
  • MCPsfdt_notify pushes the latest snapshot.
  • CI — the generated pipelines run monitor all --notify on a schedule (see CI/CD Integration).
Last updated on