A simple and flexible Slack integration with GitHub Actions.
-
Advanced users can use a configuration file and Handlebars templates to configure every aspect of the Slack message.
-
In addition to "legacy" attachments, rich messages can be created using layout blocks for flexible message visualisation and interactivity.
Create a Slack Webhook URL using either the Incoming Webhooks App (preferred) or by attaching an incoming webhook to an existing Slack App (beware, channel override not possible when using a Slack App):
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
Only required if the SLACK_WEBHOOK_URL environment variable is not set.
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
The status must be defined. It can either be the current job status
using:
with:
status: ${{ job.status }}
or a hardcoded custom status such as "starting" or "in progress":
with:
status: in progress
The individual status of job steps can be included in the Slack message using:
with:
status: ${{ job.status }}
steps: ${{ toJson(steps) }}
Note: Only steps that have an explicit "step id" will be reported on. Steps without
an id are assigned auto-generated hex hash identifiers by GitHub Actions (e.g.
58764083063d40139891dda6fb4654d2) and are automatically filtered out. See example below.
Parameters for matrix jobs can be included in Slack messages:
with:
status: ${{ job.status }}
matrix: ${{ toJson(matrix) }}
Parameters for workflow_call or workflow_dispatch events can be included in Slack messages:
with:
status: ${{ job.status }}
inputs: ${{ toJson(inputs) }}
To override the channel or to send the Slack message to an individual use:
with:
status: ${{ job.status }}
channel: '#workflows'
Note: To override the channel the Slack webhook URL must be an Incoming Webhook URL. See https://api.slack.com/faq#incoming_webhooks
To override the slack message use:
with:
status: ${{ job.status }}
channel: '#workflows'
message: Deploying {{env.GITHUB_REF_NAME}} branch
A configuration file can be used to customise the following Slack message fields:
usernameicon_urlpretexttitleandtitle_linktextfallbackplain text summary used for dumb clients and notificationsfieldstitle, value and short/longblocksincludingactions,context,divider,file,header,image,inputandsectionblocksblocks_onlyset totrueto send only blocks without the default legacy attachment- message
footer - border
colorsbased job statussuccess,failure,cancelled. valid colors aregood(green),warning(yellow),danger(red) or any hex color code eg.#439FE0 iconsfor step statussuccess,failure,cancelled,skipped, and a default
Default: .github/slack.yml
with:
status: ${{ job.status }}
config: .github/config/slack.yml
The following Slack message fields and block layouts support templating using Handlebars.js format:
pretexttitletextandmessagefallbackfieldstitleandvalueblocks
Supported Template Variables
| Variable | Description | Example |
|---|---|---|
env.* |
All environment variables | {{env.MY_VAR}} |
payload.* |
Full GitHub event payload (github.event) |
{{payload.repository.name}}, {{payload.commits}} |
jobName |
Current job name | {{jobName}} |
jobStatus |
Current job status | {{jobStatus}} |
jobSteps.* |
Step outcomes keyed by step id | {{jobSteps.build.outcome}} |
jobMatrix.* |
Matrix properties | {{jobMatrix.os}} |
jobInputs.* |
Workflow dispatch/call inputs | {{jobInputs.environment}} |
eventName |
GitHub event name | {{eventName}} |
workflow |
Workflow name | {{workflow}} |
workflowUrl |
Workflow runs URL | {{workflowUrl}} |
workflowRunUrl |
Current workflow run URL | {{workflowRunUrl}} |
repositoryName |
Repository (owner/repo) | {{repositoryName}} |
repositoryUrl |
Repository URL | {{repositoryUrl}} |
runId |
Workflow run ID | {{runId}} |
runNumber |
Workflow run number | {{runNumber}} |
sha |
Full commit SHA | {{sha}} |
shortSha |
Short commit SHA (8 chars) | {{shortSha}} |
branch |
Branch name | {{branch}} |
actor |
User who triggered the workflow | {{actor}} |
action |
Event action (e.g. opened, closed) | {{action}} |
ref |
Branch or tag ref | {{ref}} |
refType |
Ref type (branch or tag) | {{refType}} |
refUrl |
URL to the ref | {{refUrl}} |
diffRef |
Diff reference (commit or branch) | {{diffRef}} |
diffUrl |
URL to the diff/compare view | {{diffUrl}} |
description |
Event-specific description | {{description}} |
sender.* |
User who triggered the event | {{sender.login}}, {{sender.avatar_url}} |
ts |
Current Unix timestamp | {{ts}} |
Helper Functions
Apart from the standard helper functions such as #if and #each there are also a few custom
ones:
-
iconconverts a job status into an icon eg.{{icon jobStatus}} -
jsondumps the value as a JSON string eg.{{json payload.commits}} -
truncatecuts the string at the limit eg.{{truncate sha 8}} -
defaultallows a alternative or default value eg.{{default headRef "master"}} -
pluralizeoutputs different text based on item count eg.{{pluralize requested_reviewers "reviewer" "reviewers"}}(if only singular form is given plural is derived by adding an "s") -
eq,neq,not,and, andorcan be used as logical operators eg.{{#if (and (not has_issues) (or has_pages has_wiki))}}yes{{else}}no{{/if}} -
#ifeqand#ifneqtest for variable equality or not eg.{{#ifneq event_name "create"}}yes{{else}}no{{/ifneq}}
Example Using Config File
To generate the message format below use the slack.yml configuration file that follows.
Example Configuration File: slack.yml
username: GitHub-CI
icon_url: https://octodex.github.com/images/mona-the-rivetertocat.png
pretext: Triggered via {{eventName}} by {{actor}} {{or action "action"}} {{ref}} `{{diffRef}}`
title: GitHub Actions
title_link: https://support.github.com
text: |
*<{{workflowRunUrl}}|Workflow _{{workflow}}_ job _{{jobName}}_ triggered by _{{eventName}}_ is _{{jobStatus}}_>* for <{{refUrl}}|`{{ref}}`>
{{#if description}}<{{diffUrl}}|`{{diffRef}}`> - {{description}}{{/if}}
{{#if payload.commits}}
*Commits*
{{#each payload.commits}}
<{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}}
{{/each}}
{{/if}}
fallback: |-
[GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}}
fields:
- title: Job Steps
value: "{{#each jobSteps}}{{icon this.outcome}} {{@key}}\n{{/each}}"
short: false
- title: Job Matrix
value: "{{#each jobMatrix}}{{@key}}: {{this}}\n{{/each}}"
short: false
- title: Workflow
value: "<{{workflowUrl}}|{{workflow}}>"
short: true
- title: Git Ref
value: "{{ref}} ({{refType}})"
short: true
- title: Run ID
value: |-
<{{workflowRunUrl}}|{{runId}}>
short: true
- title: Run Number
value: "{{runNumber}}"
short: true
- title: Actor
value: "{{actor}}"
short: true
- title: Job Status
value: "{{jobStatus}}"
short: true
footer: >-
<{{repositoryUrl}}|{{repositoryName}}> {{workflow}} #{{runNumber}}
colors:
success: '#5DADE2'
failure: '#884EA0'
cancelled: '#A569BD'
default: '#7D3C98'
icons:
success: ':white_check_mark:'
failure: ':grimacing:'
cancelled: ':x:'
skipped: ':heavy_minus_sign:'
default: ':interrobang:'
Blocks-only mode:
To send only blocks without the default legacy attachment, set blocks_only: true
in the config file:
blocks_only: true
blocks:
- type: section
text:
type: mrkdwn
text: "*{{jobName}}* is *{{jobStatus}}*"
Notes:
- If template expressions occur at the start of a string the string must be double-quoted eg.
pretext: "{{eventName}} triggered by {{actor}}" - Use YAML multiline string formats
|,>,|-and>-or double-quotes"\n"to control new lines - Use
~(tilde) character to control whitepace when looping see Whitespace control
To ensure the Slack message is sent even if the job fails add the
always() function:
if: always()
or use a specific status function to only run when the job status matches. All possible status check functions are:
success()(default)always()cancelled()failure()
To send a Slack message when a workflow job has completed add the following as the last step of the job:
- uses: act10ns/slack@v2
with:
status: ${{ job.status }}
if: always()
To include statuses for each Job Step in the message include the
steps input (making sure to use the toJSON function):
- uses: act10ns/slack@v2
with:
status: ${{ job.status }}
steps: ${{ toJson(steps) }}
if: always()
Only steps that have an explicit "step id" assigned to them will be reported on.
Steps without an id are automatically filtered out because GitHub Actions assigns
them auto-generated 32-character hex identifiers that are not meaningful:
- name: Build
id: build
run: |
npm install
npm run build
The default Slack channel for the configured webhook can be overridden
using either another channel name #channel or a username @username.
- uses: act10ns/slack@v2
with:
status: ${{ job.status }}
channel: '#workflows'
or
- uses: act10ns/slack@v2
with:
status: ${{ job.status }}
channel: '@nick'
Multiple channels can be specified, separated by spaces or commas:
- uses: act10ns/slack@v2
with:
status: ${{ job.status }}
channel: '#channel1, #channel2, @nick'
name: Docker Build and Push
on:
push:
branches: [ master, release/* ]
jobs:
build:
runs-on: ubuntu-latest
env:
REPOSITORY_URL: docker.pkg.github.com
IMAGE_NAME: ${{ github.repository }}/alerta-cli
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
steps:
- uses: act10ns/slack@v2
with:
status: starting
channel: '#workflows'
message: Starting Docker Build and Push...
if: always()
- name: Checkout
uses: actions/checkout@v4
- name: Variables
id: vars
run: echo "SHORT_COMMIT_ID=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Build image
id: docker-build
run: >-
docker build
-t $IMAGE_NAME
-t $REPOSITORY_URL/$IMAGE_NAME:${{ steps.vars.outputs.SHORT_COMMIT_ID }}
-t $REPOSITORY_URL/$IMAGE_NAME:latest .
- name: Docker Login
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
run: docker login $REPOSITORY_URL --username "$DOCKER_USERNAME" --password "$DOCKER_PASSWORD"
- name: Publish Image
id: docker-push
run: docker push $REPOSITORY_URL/$IMAGE_NAME
- uses: act10ns/slack@v2
with:
status: ${{ job.status }}
steps: ${{ toJson(steps) }}
channel: '#workflows'
if: always()
The above "Docker Build and Push" workflow will appear in Slack as:
To report on the overall status of a workflow with multiple jobs, add a
final job that depends on all other jobs using needs and if: always().
Use the needs context to summarise the result of each job:
name: Release
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- run: npm test
deploy:
runs-on: ubuntu-latest
needs: test
steps:
- run: echo "Deploying..."
notify:
runs-on: ubuntu-latest
needs: [build, test, deploy]
if: always()
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
steps:
- uses: act10ns/slack@v2
with:
status: ${{ (contains(needs.*.result, 'failure') && 'failure') || 'success' }}
channel: '#workflows'
message: |
Release workflow: build=${{ needs.build.result }} test=${{ needs.test.result }} deploy=${{ needs.deploy.result }}
To enable runner diagnostic logging set the ACTIONS_RUNNER_DEBUG secret to true.
To enable step debug logging set the ACTIONS_STEP_DEBUG secret to true.
- GitHub Actions Toolkit https://github.com/actions/toolkit/tree/main/packages/github
- GitHub Actions Starter Workflows https://github.com/actions/starter-workflows
- Slack Incoming Webhooks https://slack.com/apps/A0F7XDUAZ-incoming-webhooks?next_id=0
- Env vars https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
- Webhook Payloads https://docs.github.com/en/webhooks/webhook-events-and-payloads
- GitHub Actions Cheat Sheet https://github.github.io/actions-cheat-sheet/actions-cheat-sheet.html
- Slack Secondary message attachments https://api.slack.com/reference/messaging/attachments
- Handlebars Language Guide https://handlebarsjs.com/guide/
- YAML multiline string formats https://yaml-multiline.info/
- Migrate your legacy message compositions to blocks https://api.slack.com/messaging/attachments-to-blocks
Copyright (c) 2020-2026 Nick Satterly. Available under the MIT License.





