Claude Code GitHub Actions Notification (2026)

Setting up notifications for GitHub Actions when working with Claude Code ensures you stay informed about workflow status, test results, and deployment outcomes. This guide walks you through configuring notifications across multiple channels.

Why Configure GitHub Actions Notifications

When Claude Code executes workflows through skills like the TDD skill or automation pipelines, you need visibility into what happens in your CI/CD environment. Without proper notifications, you might miss failed builds, broken tests, or successful deployments that require your attention.

GitHub Actions provides native notification mechanisms, but extending these to Slack, Discord, email, or custom webhooks gives you flexibility. The setup involves understanding workflow triggers, artifact handling, and notification channels.

Notification strategy also scales with team size. A solo developer might only need GitHub’s built-in email alerts, but a team of fifteen engineers shipping to production multiple times per day needs structured routing: failures go to an on-call channel, deploy confirmations go to a product channel, and weekly summaries get emailed to stakeholders. Claude Code workflows add another dimension because automated AI-driven tasks may run outside business hours, making async notification delivery even more important.

Notification Channel Comparison

Before writing a single line of YAML, choose your notification target based on your team’s actual workflow:

Channel Setup Complexity Real-Time Rich Formatting Cost Best For
GitHub Email (native) None No (batched) No Free Solo devs, simple projects
Slack (webhook) Low Yes Yes (blocks) Free tier available Team CI alerts
Discord (webhook) Low Yes Yes (embeds) Free Open-source, community projects
Microsoft Teams Medium Yes Yes (cards) Free with M365 Enterprise orgs
PagerDuty High Yes Limited Paid On-call escalation
Custom HTTP webhook Medium Yes You control Free Internal dashboards

For most Claude Code users running automated pipelines, Slack or Discord gives the best signal-to-noise ratio with minimal configuration overhead.

Basic GitHub Actions Notification Workflow

The foundation of notification setup starts with your workflow file. Here’s a basic configuration that triggers on workflow completion:

name: Claude Code Build Pipeline
on:
 push:
 branches: [main, develop]
 pull_request:
 branches: [main]
jobs:
 build:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v4
 - name: Run Claude Code Task
 run: |
 # Your Claude Code execution here
 claude --print "$(cat CLAUDE.md)"
 - name: Run Tests
 run: npm test
 - name: Upload Results
 if: always()
 uses: actions/upload-artifact@v4
 with:
 name: test-results
 path: test-results/

The if: always() condition ensures artifacts upload regardless of success or failure, which is critical for debugging.

Understanding Job-Level vs Step-Level Notifications

A common mistake is placing notification steps only at the job level and losing granularity. Consider this expanded structure that gives you step-level insight:

jobs:
 build:
 runs-on: ubuntu-latest
 outputs:
 test_status: ${{ steps.tests.outcome }}
 deploy_status: ${{ steps.deploy.outcome }}
 steps:
 - uses: actions/checkout@v4
 - name: Run Tests
 id: tests
 run: npm test
 - name: Deploy
 id: deploy
 if: success()
 run: ./scripts/deploy.sh
 - name: Notify with Step Details
 if: always()
 uses: 8398a7/action-slack@v3
 with:
 status: custom
 custom_payload: |
 {
 "text": "Build complete for ${{ github.repository }}",
 "attachments": [{
 "color": "${{ job.status == 'success' && 'good' || 'danger' }}",
 "fields": [
 {"title": "Tests", "value": "${{ steps.tests.outcome }}", "short": true},
 {"title": "Deploy", "value": "${{ steps.deploy.outcome }}", "short": true},
 {"title": "Branch", "value": "${{ github.ref_name }}", "short": true},
 {"title": "Triggered by", "value": "${{ github.actor }}", "short": true}
 ]
 }]
 }
 env:
 SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Exposing step-level outcomes via outputs and steps.<id>.outcome means your notification payload carries actionable detail rather than just pass or fail.

Slack Notification Configuration

Slack remains popular for team notifications. Create an incoming webhook in your Slack workspace, then add a notification step:

- name: Notify Slack
 if: always()
 uses: 8398a7/action-slack@v3
 with:
 status: ${{ job.status }}
 fields: repo,message,commit,author,action
 channel: '#ci-notifications'
 env:
 SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

For more detailed notifications, customize message formatting with a raw curl call:

- name: Custom Slack Message
 if: failure()
 run: |
 curl -X POST -H 'Content-type: application/json' \
 --data '{"text":"Build failed: ${{ github.repository }} - ${{ github.workflow }}"}' \
 ${{ secrets.SLACK_WEBHOOK_URL }}

Advanced Slack Block Kit Formatting

Slack’s Block Kit format lets you build visually structured notifications with buttons, dividers, and context sections. This level of detail is especially valuable for Claude Code pipelines that run multi-step AI tasks:

- name: Rich Slack Notification
 if: always()
 run: |
 STATUS_EMOJI="${{ job.status == 'success' && ':white_check_mark:' || ':x:' }}"
 STATUS_COLOR="${{ job.status == 'success' && '#36a64f' || '#e01e5a' }}"
 RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
 curl -X POST -H 'Content-type: application/json' \
 --data "{
 \"attachments\": [{
 \"color\": \"${STATUS_COLOR}\",
 \"blocks\": [
 {
 \"type\": \"header\",
 \"text\": {
 \"type\": \"plain_text\",
 \"text\": \"${STATUS_EMOJI} ${{ github.workflow }}\"
 }
 },
 {
 \"type\": \"section\",
 \"fields\": [
 {\"type\": \"mrkdwn\", \"text\": \"*Repository*\n${{ github.repository }}\"},
 {\"type\": \"mrkdwn\", \"text\": \"*Branch*\n${{ github.ref_name }}\"},
 {\"type\": \"mrkdwn\", \"text\": \"*Commit*\n\`${{ github.sha }}\`\"},
 {\"type\": \"mrkdwn\", \"text\": \"*Triggered by*\n${{ github.actor }}\"}
 ]
 },
 {
 \"type\": \"actions\",
 \"elements\": [{
 \"type\": \"button\",
 \"text\": {\"type\": \"plain_text\", \"text\": \"View Run\"},
 \"url\": \"${RUN_URL}\"
 }]
 }
 ]
 }]
 }" \
 ${{ secrets.SLACK_WEBHOOK_URL }}

If you use the Slack MCP server with Claude Code, you can further automate response handling when notifications arrive, enabling you to trigger Claude Code actions directly from Slack messages.

Discord Webhook Notifications

Discord offers similar functionality with its webhook system:

- name: Discord Notification
 if: always()
 run: |
 curl -X POST \
 -H "Content-Type: application/json" \
 -d '{
 "content": null,
 "embeds": [{
 "title": "Workflow ${{ job.status }}",
 "description": "${{ github.repository }} - ${{ github.workflow }}",
 "color": ${{ job.status == 'success' && 3066993 || 15158332 }},
 "fields": [
 {"name": "Branch", "value": "${{ github.ref }}", "inline": true},
 {"name": "Commit", "value": "${{ github.sha }}", "inline": true}
 ]
 }]
 }' \
 ${{ secrets.DISCORD_WEBHOOK_URL }}

Discord Color Reference

Discord embeds use decimal integer color codes. Here are the most useful values for CI status notifications:

Status Hex Decimal Use Case
Success #2ECC71 3066993 Build passed
Failure #E74C3C 15158332 Build failed
Warning #F39C12 15968802 Flaky tests, slow build
Cancelled #95A5A6 9807322 Manual cancellation
In Progress #3498DB 3447003 Long-running jobs

Using consistent colors across your Discord server makes it easy to scan notification history at a glance.

Email Notifications with GitHub

For teams preferring email, GitHub’s native notification system works well. Configure branch protection rules to require status checks, then subscribe to workflow run notifications:

  1. Go to your repository Settings
  2. Click Notifications
  3. Enable “Workflow runs” under GitHub Actions

For custom email handling with templates, use a GitHub App or action:

- name: Send Email Notification
 if: failure()
 uses: dawidd6/action-send-mail@v3
 with:
 server_address: smtp.gmail.com
 server_port: 465
 username: ${{ secrets.MAIL_USERNAME }}
 password: ${{ secrets.MAIL_PASSWORD }}
 subject: "Build Failed: ${{ github.repository }}"
 body: |
 Workflow ${{ github.workflow }} failed
 Branch: ${{ github.ref }}
 Commit: ${{ github.sha }}
 View logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
 to: "[email protected]"
 from: "CI Notification <[email protected]>"

HTML Email Templates for Richer Context

Plain text emails work but miss an opportunity to pack in structured information. The dawidd6/action-send-mail action supports HTML bodies, letting you send a miniature status dashboard on every failure:

- name: Send HTML Failure Report
 if: failure()
 uses: dawidd6/action-send-mail@v3
 with:
 server_address: smtp.gmail.com
 server_port: 465
 secure: true
 username: ${{ secrets.MAIL_USERNAME }}
 password: ${{ secrets.MAIL_PASSWORD }}
 subject: "[FAILURE] ${{ github.workflow }} on ${{ github.ref_name }}"
 html_body: |
 <html>
 <body style="font-family: Arial, sans-serif; padding: 20px;">
 <h2 style="color: #e01e5a;">Build Failure Report</h2>
 <table style="border-collapse: collapse; width: 100%;">
 <tr style="background: #f5f5f5;">
 <td style="padding: 8px; border: 1px solid #ddd;"><strong>Repository</strong></td>
 <td style="padding: 8px; border: 1px solid #ddd;">${{ github.repository }}</td>
 </tr>
 <tr>
 <td style="padding: 8px; border: 1px solid #ddd;"><strong>Branch</strong></td>
 <td style="padding: 8px; border: 1px solid #ddd;">${{ github.ref_name }}</td>
 </tr>
 <tr style="background: #f5f5f5;">
 <td style="padding: 8px; border: 1px solid #ddd;"><strong>Commit</strong></td>
 <td style="padding: 8px; border: 1px solid #ddd;">${{ github.sha }}</td>
 </tr>
 <tr>
 <td style="padding: 8px; border: 1px solid #ddd;"><strong>Author</strong></td>
 <td style="padding: 8px; border: 1px solid #ddd;">${{ github.actor }}</td>
 </tr>
 </table>
 <p>
 <a href="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
 style="background: #0366d6; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">
 View Full Logs
 </a>
 </p>
 </body>
 </html>
 to: "[email protected]"
 from: "CI Notifications <[email protected]>"

Conditional Notifications Based on Changes

You can reduce notification noise by filtering based on file changes or authors:

name: Conditional Notifications
on:
 push:
 paths-ignore:
 - '.md'
 - 'docs/'
 pull_request:
 paths-ignore:
 - '.md'
jobs:
 notify:
 runs-on: ubuntu-latest
 steps:
 - name: Check changed files
 id: changes
 uses: dorny/paths-filter@v2
 with:
 filters: |
 src:
 - 'src/'
 - 'lib/'
 - '*.js'
 - '*.ts'
 - name: Notify on source changes
 if: steps.changes.outputs.src == 'true'
 run: |
 echo "Source code changed - notifying team"

Environment-Specific Routing

Production deploys warrant louder notifications than staging pushes. Use environment conditions to route to different channels:

jobs:
 deploy:
 runs-on: ubuntu-latest
 environment: ${{ github.ref_name == 'main' && 'production' || 'staging' }}
 steps:
 - name: Deploy Application
 run: ./scripts/deploy.sh
 - name: Notify Production Channel
 if: github.ref_name == 'main' && success()
 run: |
 curl -X POST -H 'Content-type: application/json' \
 --data '{"text":"Production deploy succeeded for ${{ github.repository }} at commit ${{ github.sha }}"}' \
 ${{ secrets.SLACK_PROD_WEBHOOK_URL }}
 - name: Notify Staging Channel
 if: github.ref_name != 'main' && success()
 run: |
 curl -X POST -H 'Content-type: application/json' \
 --data '{"text":"Staging deploy succeeded on branch ${{ github.ref_name }}"}' \
 ${{ secrets.SLACK_STAGING_WEBHOOK_URL }}
 - name: Escalate Production Failure
 if: github.ref_name == 'main' && failure()
 run: |
 # PagerDuty or high-priority channel for production failures
 curl -X POST -H 'Content-type: application/json' \
 --data '{"text":"<!channel> PRODUCTION DEPLOY FAILED: ${{ github.repository }} - immediate attention required"}' \
 ${{ secrets.SLACK_ONCALL_WEBHOOK_URL }}

The <!channel> mention in Slack triggers a channel-wide notification, which is appropriate for production failures and should never appear in staging notifications.

Storing and Managing Secrets Properly

Every notification channel requires a credential: a webhook URL, SMTP password, or API token. Mismanaging these is a common source of broken notifications and security exposure.

Add secrets at the repository level via Settings > Secrets and variables > Actions. For organization-wide pipelines, add them at the organization level so repositories can reference them without duplication.

Reference secrets in workflows using this pattern:

env:
 SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Never interpolate secrets directly into log-visible strings. This example is wrong:

BAD - the webhook URL will appear in logs
run: echo "Webhook is ${{ secrets.SLACK_WEBHOOK_URL }}"

To audit which secrets a workflow uses, scan your YAML for secrets. references and verify each one exists in your repository settings before the first run.

Integrating with Claude Code Skills

Several Claude skills enhance notification workflows. The supermemory skill can track notification history and patterns, helping you understand which types of failures require immediate attention versus those that can wait.

For frontend projects using the frontend-design skill, you might want notifications specifically about visual regression test failures:

- name: Visual Regression Check
 uses: chromaui/action@v1
 with:
 projectDirectory: .
 buildScriptName: test:visual
- name: Notify on Visual Changes
 if: failure()
 run: |
 echo "Visual regression detected - review required"

The pdf skill can attach test reports to notifications. After generating test results, use the /pdf skill in Claude Code to create a formatted report, then attach it as an artifact:

- name: Attach Report to Notification
 uses: actions/upload-artifact@v4
 with:
 name: test-report-pdf
 path: report.pdf

Full Reusable Notification Workflow

If your repository contains multiple workflows, each one would otherwise need its own copy-pasted notification steps. Instead, create a reusable notification workflow once and call it from every consumer:

.github/workflows/notify.yml
name: Reusable Notification Dispatcher
on:
 workflow_call:
 inputs:
 status:
 required: true
 type: string
 workflow_name:
 required: true
 type: string
 run_url:
 required: true
 type: string
 secrets:
 SLACK_WEBHOOK_URL:
 required: true
jobs:
 notify:
 runs-on: ubuntu-latest
 steps:
 - name: Send Slack Notification
 run: |
 COLOR="${{ inputs.status == 'success' && '#36a64f' || '#e01e5a' }}"
 curl -X POST -H 'Content-type: application/json' \
 --data "{
 \"attachments\": [{
 \"color\": \"${COLOR}\",
 \"text\": \"*${{ inputs.workflow_name }}* completed with status: *${{ inputs.status }}*\",
 \"actions\": [{
 \"type\": \"button\",
 \"text\": \"View Run\",
 \"url\": \"${{ inputs.run_url }}\"
 }]
 }]
 }" \
 ${{ secrets.SLACK_WEBHOOK_URL }}

Then call it from any other workflow with a minimal uses block:

.github/workflows/build.yml
jobs:
 build:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v4
 - run: npm ci && npm test
 send-notification:
 needs: build
 if: always()
 uses: ./.github/workflows/notify.yml
 with:
 status: ${{ needs.build.result }}
 workflow_name: "Main Build Pipeline"
 run_url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
 secrets:
 SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

This pattern keeps notification logic in one place and makes it easy to change your channel or message format across the entire repository.

Best Practices

  1. Use status-specific channels: Send failures to a high-priority channel and successes to a lower-priority one.

  2. Include actionable information: Always provide links to logs, run IDs, and direct commits.

  3. Filter noise: Use path filters and branch conditions to avoid notifications for documentation-only changes.

  4. Secure your secrets: Store webhook URLs and credentials in GitHub Secrets, never in workflow files.

  5. Test your notifications: Create a manual workflow dispatch to verify notification delivery before relying on it.

  6. Rate-limit awareness: Slack, Discord, and other webhooks enforce rate limits. If your pipeline dispatches many jobs in parallel, throttle notifications to avoid rejected requests. Batch job results into a single summary notification where possible.

  7. Use if: always() on notification steps: Without this condition, a failed upstream step will skip your notification step entirely, leaving the team with no visibility into the failure.

  8. Cache run metadata early: Steps that compute URLs or format commit messages can fail if they depend on context that’s unavailable in certain trigger types. Set up a dedicated step at the top of the job to export all variables you’ll use in notifications.

Troubleshooting Notification Issues

If notifications aren’t arriving, verify these common issues:

  • Check that webhook URLs are correct and not expired
  • Ensure secrets are properly configured in repository settings
  • Verify the workflow has permission to send notifications
  • Review GitHub Actions logs for specific error messages

For Slack, use the Slack API’s test endpoint to confirm connectivity. For Discord, the developer portal provides webhook testing tools.

Diagnosing Silent Failures

Notification steps themselves can silently fail if the curl exit code is non-zero but the step has no error handling. Add explicit error handling to detect and surface these failures:

- name: Notify with Error Handling
 if: always()
 run: |
 HTTP_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
 -X POST -H 'Content-type: application/json' \
 --data '{"text":"Build finished: ${{ job.status }}"}' \
 ${{ secrets.SLACK_WEBHOOK_URL }})
 if [ "$HTTP_RESPONSE" != "200" ]; then
 echo "Notification failed with HTTP $HTTP_RESPONSE"
 exit 1
 else
 echo "Notification delivered successfully"
 fi

This approach surfaces HTTP errors (400 bad request from malformed JSON, 410 for expired webhooks, 429 for rate limiting) as visible step failures in your Actions log, making diagnosis straightforward.

A complementary approach is to run a dedicated workflow_dispatch-triggered test workflow that fires a single notification to your test channel. Run it after any changes to your notification configuration to confirm end-to-end delivery before a real failure exposes a broken alert pipeline.

Setting up proper GitHub Actions notifications for Claude Code workflows transforms your CI/CD pipeline from a black box into a transparent, observable system. Teams can respond faster to issues, track deployment success rates, and maintain confidence in their automated workflows.



This took me 3 hours to figure out. I put it in a CLAUDE.md so I'd never figure it out again. Now Claude gets it right on the first try, every project. 16 framework templates. Next.js, FastAPI, Laravel, Rails, Go, Rust, Terraform, and 9 more. Each one 300+ lines of "here's exactly how this stack works." Copy into your project. Done. **[See the templates →](https://zovo.one/lifetime?utm_source=ccg&utm_medium=cta-deploy&utm_campaign=claude-code-github-actions-notification-setup)** $99 once. Yours forever. I keep adding templates monthly.

Related Reading

Built by theluckystrike. More at zovo.one

Find the right skill → Browse 155+ skills in our Skill Finder.

See Also

Configure MCP → Build your server config with our MCP Config Generator.

Try it: Paste your error into our Error Diagnostic for an instant fix.