GitHub Actions tells you a workflow failed by putting a red X next to a commit. If you're not staring at the repo page, you won't notice until someone asks why the deploy didn't go out. GitHub can send email notifications, but those get lost in the noise fast — especially if you have multiple repos with active workflows.
A better approach: get a Telegram message on your phone, a Discord ping in your team server, or a Slack alert in your dev channel the moment a workflow fails. Here's how to set that up, from a quick platform-specific approach to a cleaner solution that works across all three.
Option 1: Platform-Specific Webhooks (The Quick Way)
Each platform has its own webhook format. For Discord, you create a webhook URL in your channel settings and POST to it. For Slack, you set up an Incoming Webhook app. For Telegram, you call the Bot API directly.
Here's what a Discord webhook step looks like in a GitHub Actions workflow:
name: Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy to production run: ./scripts/deploy.sh - name: Notify on failure if: failure() run: | curl -s -X POST "$DISCORD_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "{ \"content\": \"Deploy failed on \`${{ github.repository }}\` — commit ${{ github.sha }}\" }" env: DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
This works for one platform in one repo. But the problems show up quickly:
- One integration per platform. If you want Discord and Telegram, you're writing two separate webhook steps with two different payload formats.
- One secret per repo. Every repo needs the webhook URL in its secrets. If you rotate the URL, you update it across all repos.
- Different payload formats. Discord expects
content, Slack expectstext, Telegram expectschat_id+text. You're learning three different APIs. - No history. Once the message is sent, there's no record of what was notified or when.
For a single repo with one platform, webhooks are fine. For a team with multiple repos and people on different platforms, it becomes a mess of duplicated YAML and secrets.
Option 2: A Notification API (The Cleaner Way)
Instead of calling each platform's API directly, you send the alert to a single notification endpoint. With NotificationsBot, one API call can reach Telegram, Discord, and Slack — whoever is subscribed to that channel. The same cURL command, the same payload format, every time.
name: Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy to production run: ./scripts/deploy.sh - name: Notify on failure if: failure() run: | curl -s -X POST https://api.notificationsbot.com/event \ -H "Authorization: Bearer $NOTIFICATIONSBOT_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"channel\": \"deploys\", \"title\": \"Deploy failed\", \"message\": \"${{ github.repository }} — commit ${{ github.sha }}\" }" env: NOTIFICATIONSBOT_API_KEY: ${{ secrets.NOTIFICATIONSBOT_API_KEY }}
One API key across all your repos. One payload format. If you want to add a new team member on Telegram, you add them in the dashboard — no YAML changes, no new secrets.
With platform-specific webhooks, your CI pipeline is coupled to a delivery platform. With a notification API, your pipeline just reports what happened — who gets notified and on which platform is managed separately. You can add subscribers, switch platforms, or split alerts into different channels without editing a single workflow file.
Setting Up CI/CD Alerts with NotificationsBot
If you don't have an account yet, the setup takes about 5 minutes:
- Sign up at app.notificationsbot.com — free tier, no credit card
- Create a channel (e.g.,
deploysorci-alerts) - Add a subscriber — choose Telegram, Discord, or Slack as the platform
- Link the subscriber — connect their account through the onboarding flow
- Assign the subscriber to the channel
- Create an API key in Settings and add it as a secret in your GitHub repo (
NOTIFICATIONSBOT_API_KEY)
From here, any event sent to your channel reaches whoever is subscribed. You can add more people later — different team members, different platforms — without changing your workflows.
Alert on Failure Only
The most common pattern is alerting only when something goes
wrong. GitHub Actions has a built-in failure()
condition that makes this easy:
- name: Notify on failure if: failure() run: | curl -s -X POST https://api.notificationsbot.com/event \ -H "Authorization: Bearer $NOTIFICATIONSBOT_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"channel\": \"ci-alerts\", \"title\": \"CI failed: ${{ github.repository }}\", \"message\": \"Branch: ${{ github.ref_name }}\nCommit: ${{ github.sha }}\nAuthor: ${{ github.actor }}\" }" env: NOTIFICATIONSBOT_API_KEY: ${{ secrets.NOTIFICATIONSBOT_API_KEY }}
This step runs only if a previous step in the job failed. It includes the repo name, branch, commit hash, and who pushed — enough context to know what broke without opening GitHub.
Alert on Success and Failure
For deploy workflows, you might want to know when a deploy
succeeds too — especially if it takes a few minutes
and you're waiting on it. Use the always()
condition and include the job status:
- name: Notify deploy result if: always() run: | if [ "${{ job.status }}" == "success" ]; then TITLE="Deploy succeeded" else TITLE="Deploy failed" fi curl -s -X POST https://api.notificationsbot.com/event \ -H "Authorization: Bearer $NOTIFICATIONSBOT_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"channel\": \"deploys\", \"title\": \"$TITLE\", \"message\": \"${{ github.repository }} — ${{ github.ref_name }}\nby ${{ github.actor }}\" }" env: NOTIFICATIONSBOT_API_KEY: ${{ secrets.NOTIFICATIONSBOT_API_KEY }}
Use if: failure() for test and build workflows
— you only care when they break. Use
if: always() for deploy workflows — knowing
that a deploy finished successfully is just as useful as
knowing it failed.
Reusable Workflow for Multiple Repos
If you have multiple repos that should all send notifications, you can extract the notification step into a reusable workflow. Define it once, call it from any repo:
name: Notify on: workflow_call: inputs: channel: required: true type: string title: required: true type: string message: required: true type: string secrets: NOTIFICATIONSBOT_API_KEY: required: true jobs: notify: runs-on: ubuntu-latest steps: - name: Send notification run: | curl -s -X POST https://api.notificationsbot.com/event \ -H "Authorization: Bearer $NOTIFICATIONSBOT_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"channel\": \"${{ inputs.channel }}\", \"title\": \"${{ inputs.title }}\", \"message\": \"${{ inputs.message }}\" }" env: NOTIFICATIONSBOT_API_KEY: ${{ secrets.NOTIFICATIONSBOT_API_KEY }}
Then call it from any workflow:
notify: if: failure() needs: deploy uses: your-org/shared-workflows/.github/workflows/notify.yml@main with: channel: deploys title: Deploy failed message: ${{ github.repository }} — ${{ github.ref_name }} secrets: NOTIFICATIONSBOT_API_KEY: ${{ secrets.NOTIFICATIONSBOT_API_KEY }}
Platform Webhooks vs NotificationsBot
- Setup per repo — Webhooks: one secret per platform per repo. NotificationsBot: one API key for all repos
- Multi-platform — Webhooks: separate step per platform, different payload formats. NotificationsBot: one step reaches Telegram + Discord + Slack
- Adding a team member — Webhooks: they need access to the channel/group. NotificationsBot: add a subscriber in the dashboard
- Changing platforms — Webhooks: rewrite the workflow step, add new secrets. NotificationsBot: change the subscriber's platform in the dashboard
- Notification history — Webhooks: no record. NotificationsBot: searchable event log with timestamps
- Payload format — Webhooks: different per platform. NotificationsBot: one JSON format everywhere
Bonus: Separate Channels for Tests and Deploys
A common setup for teams is having two notification channels: one for CI failures (test/lint/build) and one for deploy results. The CI channel goes to a Slack channel where the whole team can see what's broken. The deploy channel sends a Telegram ping to the person responsible for releases.
With NotificationsBot, this is just two channels with
different subscribers. Your test workflow sends to
ci-alerts, your deploy workflow sends to
deploys. Each channel has its own set of
subscribers on whatever platform they prefer. No workflow
changes needed when people join or leave the team.
Get Your First CI/CD Alert in 5 Minutes
Create a free account, add a subscriber on Telegram, Discord, or Slack, and wire up your first GitHub Actions notification.
Start for free