Slack App for Channel Routing
groundcover supports sending notifications to Slack using a Slack App with bot tokens instead of static webhooks. This method allows dynamic routing of alerts to any channel by including the channel ID in the payload. In addition to routing, messages can be enriched with formatting, blocks, and mentions — for example including <@user_id> in the payload to directly notify specific team members. This provides a flexible and powerful alternative to fixed incoming webhooks for alerting.
Make sure you created a webhook for a Slack App with Bot Tokens.
Use the following workflow as an example. You can later enrich your workflow with additional functionality.
Here are a few tips for using the example workflow:
In the consts section, the
channels
attribute defines the mapping between Slack channels and their IDs. Use a clear, readable label to identify each channel (for example, the channel’s actual name in Slack), and map it to the corresponding channel ID.To locate a channel ID, open the channel in Slack, click the channel name at the top, and scroll to the About section. The channel ID is shown at the bottom of this section.

The channel name should be included in the monitor’s Metadata Labels, or you can fall back to a default. See the channel_id attribute in the workflow example.
Finally, replace the integration name in
{{ providers.slack-routing-webhook }}
with the actual name of the Webhook integration you created.
workflow:
id: slack-channel-routing-workflow
description: workflow for all channels with dynamic routing
triggers:
- type: alert
filters:
- key: annotations.slack-channel-routing-workflow
value: enabled
name: slack-channel-routing-workflow
consts:
channels: '{"devops":"C0111111111", "alerts":"C0222222222", "incidents":"C0333333333"}'
channel_id: keep.dictget( '{{ consts.channels }}', '{{ alert.labels.channel_id }}', 'C09G9AFHLTB')
env: keep.dictget({{ alert.labels }}, 'env', 'no-env')
upper_env: "keep.uppercase({{consts.env}})"
severity: keep.dictget({{ alert.annotations }}, '_gc_severity', 'unknown-severity')
summary: keep.dictget({{ alert.labels }}, 'summary', 'no-summary')
slack_message: "<https://app.groundcover.com/monitors/create-silence?keep.replace(keep.join(keep.dict_pop({{ alert.labels }}, \"_gc_monitor_id\", \"_gc_monitor_name\", \"_gc_severity\", \"backend_id\", \"grafana_folder\", \"_gc_issue_header\"), \"&\", \"matcher_\"), \" \", \"+\")|Silence> :no_bell: | \n<https://app.groundcover.com/monitors/issues?backendId={{ alert.labels.backend_id }}&selectedObjectId={{ alert.fingerprint }}|Investigate> :mag: | \n<https://app.groundcover.com/monitors?backendId={{ alert.labels.backend_id }}&selectedObjectId={{ alert.labels._gc_monitor_id }}|See Monitor> :chart_with_upwards_trend:\n\n*Labels:* \n- keep.join(keep.dict_pop({{alert.labels}}, \"_gc_monitor_id\", \"_gc_monitor_name\", \"_gc_severity\", \"backend_id\", \"grafana_folder\", \"_gc_issue_header\"), \"\\n- \")\n"
title_link: "https://app.groundcover.com/monitors/issues?backendId={{ alert.labels.backend_id }}&selectedObjectId={{ alert.fingerprint }}"
red_color: "#FF0000"
green_color: "#008000"
footer_url: "groundcover.com"
footer_icon: "https://app.groundcover.com/favicon.ico"
actions:
- if: "{{ alert.status }} == 'firing'"
name: webhook-alert
provider:
type: webhook
config: "{{ providers.slack-routing-webhook }}"
with:
body:
channel: "{{ consts.channel_id }}"
attachments:
- color: "{{ consts.red_color }}"
footer: "{{ consts.footer_url }}"
footer_icon: "{{ consts.footer_icon }}"
text: "{{ consts.slack_message }}"
title: "\U0001F6A8 Firing: {{ alert.alertname }} [{{ consts.upper_env}}]"
title_link: "{{ consts.title_link }}"
type: plain_text
- if: "{{ alert.status }} != 'firing'"
name: webhook-alert-resolved
provider:
type: webhook
config: "{{ providers.slack-routing-webhook }}"
with:
body:
channel: "{{ consts.channel_id }}"
text: "\u2705 [RESOLVED][{{ consts.upper_env}}] {{ consts.severity }} {{ alert.alertname }}"
attachments:
- color: "{{ consts.green_color }}"
text: "*Summary:* {{ consts.summary }}"
fields:
- title: "Environment"
value: "{{ consts.upper_env}}"
short: true
footer: "{{ consts.footer_url }}"
footer_icon: "{{ consts.footer_icon }}"
Last updated