Skip to main content
smartcloud reports its results on GitHub: check runs, a comment and the job summary. Notifications also send them to the places your team already looks, so nobody has to watch GitHub to notice that a pull request broke a rule or that old issues are being closed. Each run can send two kinds of notification:
  • failures: the run’s policy findings, at error level by default, on the pull request, issue or repository the run was about.
  • stale: what the stale feature changed in a sweep: items it marked, unmarked, abandoned or closed.
You can send them to three kinds of channel:
  • Slack, through an incoming webhook.
  • Discord, through a channel webhook.
  • Linear, where each notification opens an issue in a team.
Notifications are not a check of their own: they never change a run’s result, and a channel that fails is only a warning.

Turn it on

1

Get the channel's secret

  • Slack: create an incoming webhook for the channel and copy its URL.
  • Discord: in the channel’s settings, open Integrations > Webhooks, create a webhook and copy its URL.
  • Linear: create a personal API key that can create issues in the team, and copy the id (a UUID) of the team issues should go to. Label ids are UUIDs too.
2

Store it as an Actions secret

In the repository or organisation, open Settings > Secrets and variables > Actions and add the secret, for example SLACK_WEBHOOK_URL. Never put it in the config file: anyone who can read the repository can read that.
3

Pass it to smartcloud as an environment variable

.github/workflows/smartcloud.yml
4

Add a channel

.github/smartcloud.yml
The key (maintainers) is a name of your choosing; type says where it sends. Send yourself a test by running a feature that finds an error, or preview with a dry run, which lists the notifications it would have sent.

Options

notifications.channels maps a name of your choosing to a channel. secret is only needed when two channels of the same type need different secrets, or your secret has another name:
Give each secret the least it needs: a webhook posts to one channel only, and a Linear key can belong to a member of just the team it writes to.

Complete example

.github/smartcloud.yml
.github/workflows/smartcloud.yml

How it works

After every run, once the check runs and the report comment are published, smartcloud looks at each channel:
  1. It works out which kinds (on) the run has something to say about. A failures notification needs at least one finding at level or above; a stale one needs at least one change the stale feature made.
  2. It reads the channel’s secret from its environment variable. When the variable is unset or empty, the channel is skipped with a warning.
  3. It sends the notification. Channels are sent to in parallel; one that fails or does not answer within 15 seconds is recorded as a warning, and the others are still sent to.
Failures are not sent again while they are unchanged: on a pull request or issue whose report comment already says the same thing, a new push does not notify again. Stale changes are sent each time a sweep makes some, which is once per item and step of the lifecycle. In a dry run nothing is sent and no secret is read: the job summary, the CLI and the MCP server list the notifications that would have been sent instead.

What you will see

  • Slack: a message titled, for example, “2 policy failures on pull request #42 in my-org/app”, linked to the pull request, issue or repository, with one bullet per finding (error DCO: ...) or change. Text is escaped, so a title cannot mention @channel or inject a link.
  • Discord: one embed with the same content. Mentions are switched off, so a title cannot ping @everyone.
  • Linear: a new issue in team, titled like the message, whose description lists the findings or changes and links back to GitHub.
A stale notification is titled like “3 stale changes in my-org/app”. A message lists at most 20 findings or changes and counts the rest. On GitHub, a channel that was skipped or failed shows as a warning annotation on the run, such as notifications: failures not sent on maintainers: SLACK_WEBHOOK_URL is not set.

Forks and restricted runs

GitHub passes no secrets to a pull request from a fork, or to Dependabot’s runs, so every channel’s variable is empty there. Each channel is skipped with a warning, and the run carries on and never fails because of it. Scheduled runs, which carry stale changes, always have their secrets.

Troubleshooting

The environment variable the channel reads is missing or empty. Pass the secret in the workflow step’s env, with the name the channel’s secret gives (or the default for its type). On a fork’s pull request this is expected.
The service did not answer in time. The notification is dropped for this run; the next run with something to say sends again.
Check the channel’s on includes failures, and its level: with the default error, warnings are not sent. A pull request whose report comment has not changed since the last run is not notified again.
The API key must be able to create issues in team, and team and labels must be ids, not names.

Adding a channel

Channels live in packages/notifications and share one interface, so adding one takes three steps:
  1. Add the channel’s options to the NotificationChannel union in packages/config/src/sections.ts, with a type literal, the shared fields (secret, on, level) and any of its own.
  2. Implement a Channel in packages/notifications/src/channels/: its type, the defaultSecret variable, and send, which delivers one Notification through the HTTP client and fails with a ChannelError.
  3. List it in defaultChannels in packages/notifications/src/registry.ts. The registry’s type has a key for every type in the union, so the compiler points at a channel that is configured but not implemented.
Then regenerate the schema and the configuration reference, as for any schema change.
Last modified on September 27, 2026