Learn Jenkins - Production Notification & ChatOps Integration
Episode 17 of 21

Learn Jenkins - Production Notification & ChatOps Integration

In this episode we integrate real-time notifications to Slack, Microsoft Teams, and a Telegram Bot so successful, failed, or aborted build statuses reach the team directly. We also learn the Email Extension plugin to send custom HTML reports to developer and QA teams when a pipeline fails.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 16 we successfully deployed to Kubernetes and Helm. But there is one thing that is often forgotten: how does the team know a build failed? A silent pipeline without notifications is like an important message sent without a body — no one notices it until a problem shows up in production. This is where ChatOps and automated notifications come in.

In this episode we will discuss:

  1. Real-time notifications to Slack with the slackSend step.
  2. The alternatives: Microsoft Teams and a Telegram Bot via incoming webhooks.
  3. Custom HTML email with the Email Extension plugin (emailext).

Why ChatOps Matters?

ChatOps moves build information from inside the Jenkins UI into the team's communication channels. The benefits:

  • Fast response — failures are seen immediately, not when someone happens to open Jenkins.
  • Complete context — build duration, commit author, and log link are available in a single message.
  • Accountability — whoever introduced a problematic change is immediately visible.
  • Audit — the notification history becomes an activity trail of deployments.

The best pattern: send notifications in the post block so they trigger automatically whatever the build outcome.

Slack Integration with slackSend

The Slack Notification plugin provides the slackSend step. First, create a Slack App in your team's workspace and enable the Incoming Webhooks feature. Store the webhook token as a Secret text credential in Jenkins (for example ID slack-token).

Tip

The global Slack configuration (base URL and default channel) can be filled in once in Manage Jenkins then Configuration System. The pipeline step only needs to use a channel override when necessary.

Here is an example notification carrying the status, duration, and commit author:

JenkinsJenkinsfile - Slack notification in the post block
post {
    always {
        script {
            def author = sh(
                script: 'git log -1 --pretty=%an',
                returnStdout: true
            ).trim()
            def duration = currentBuild.durationString.replace(' and counting', '')
            def channel = currentBuild.currentResult == 'SUCCESS' ? '#deploy-success' : '#deploy-alert'
            slackSend(
                color: currentBuild.currentResult == 'SUCCESS' ? 'good' : 'danger',
                message: "Build ${env.JOB_NAME} #${env.BUILD_NUMBER} ${currentBuild.currentResult} by ${author} (${duration}) - ${env.BUILD_URL}",
                channel: channel,
                tokenCredentialId: 'slack-token'
            )
        }
    }
}

A few things to note:

  • currentBuild.currentResult has the value SUCCESS, FAILURE, or ABORTED.
  • color is good for green and danger for red in Slack.
  • tokenCredentialId refers to the Secret text we created.
  • The commit author is fetched with git log -1 at the start of the block.

Warning

Never write the Slack token directly in the Jenkinsfile. Always use the credentials store via tokenCredentialId so the token is not readable in the repository.

Microsoft Teams Integration

Teams uses an Incoming Webhook in the team channel. After the webhook URL is created, send a JSON message using the httpRequest step from the HTTP Request plugin. Store the webhook URL as a Secret text.

JenkinsJenkinsfile - Microsoft Teams notification
def teamsMessage(String status) {
    return """
    {
      "@type": "MessageCard",
      "summary": "Build ${env.JOB_NAME}",
      "themeColor": "${status == 'SUCCESS' ? '00FF00' : 'FF0000'}",
      "sections": [{
        "activityTitle": "Build ${env.JOB_NAME} #${env.BUILD_NUMBER} ${status}",
        "text": "Log: ${env.BUILD_URL}"
      }]
    }
    """
}
 
post {
    failure {
        withCredentials([string(credentialsId: 'teams-webhook', variable: 'TEAMS_URL')]) {
            httpRequest(
                url: env.TEAMS_URL,
                httpMode: 'POST',
                contentType: 'APPLICATION_JSON',
                requestBody: teamsMessage('FAILURE')
            )
        }
    }
}

Telegram Bot Integration

A Telegram Bot is generated from BotFather, which produces an API token, and we determine the destination chat ID. The endpoint called is sendMessage. Example:

JenkinsJenkinsfile - Telegram notification
post {
    failure {
        withCredentials([string(credentialsId: 'telegram-token', variable: 'TG_TOKEN')]) {
            sh 'curl -s -X POST \
                "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
                --data-urlencode chat_id=-1001234567890 \
                --data-urlencode "text=Build ${JOB_NAME} #${BUILD_NUMBER} FAILED - ${BUILD_URL}"'
        }
    }
}

curl is sent to the Telegram API with a negative chat ID for groups. The message contains the job name, build number, and console link so group members can immediately trace the cause of the failure.

Custom HTML Email with Email Extension

The Email Extension Plugin provides the emailext step, which is far more flexible than Jenkins's built-in email notification. First, configure SMTP in Manage Jenkins then Configuration System, and make sure the default recipients are filled in.

An example HTML email for the dev and QA teams when a build fails:

JenkinsJenkinsfile - emailext with an HTML body
post {
    failure {
        emailext(
            to: 'dev-team@example.com, qa-team@example.com',
            subject: "BUILD FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
            mimeType: 'text/html',
            body: '''
                <h2>Build Gagal</h2>
                <p>Job <b>${env.JOB_NAME}</b> build <b>#${env.BUILD_NUMBER}</b> gagal.</p>
                <p>Author: ${env.CHANGE_AUTHOR}</p>
                <p>Durasi: ${currentBuild.durationString}</p>
                <p><a href="${env.BUILD_URL}console">Lihat Console Log</a></p>
                <hr>
                <p>Ringkasan kesalahan:</p>
                <pre>${env.ERROR_MESSAGE}</pre>
            ''',
            attachLog: true
        )
    }
}

The features used:

  • to — the recipient list; it can use variables or the recipient list from configuration.
  • mimeType: 'text/html' — the email is rendered as HTML.
  • attachLog — attaches the console log as an artifact.
  • The body templating supports Jenkins variable interpolation, so the email is always contextual.

Note

The CHANGE_AUTHOR environment variable is available for pipelines triggered by a pull request. For builds from a direct branch, use the value from git log as in the Slack example above.

Notification Best Practices

  • Single source: create a notification function in a shared library (episode 9) so all pipelines use the same message format.
  • Not every channel: send success notifications only to a concise channel, while failures go to an alert channel monitored quickly.
  • Always include a link: add the console build URL to every message.
  • Size limits: do not send full logs to chat; just a summary and a link.

Conclusion

In this episode we integrated production notifications and ChatOps:

  • slackSend sends the full build status with duration and commit author to Slack.
  • Microsoft Teams uses an incoming webhook with httpRequest.
  • The Telegram Bot uses the sendMessage API with curl.
  • emailext sends a custom HTML report along with the console log to the dev and QA teams.

A build that always reports is the foundation of observability. In episode 18 we will discuss how to keep Jenkins itself healthy: High Availability, backup, disaster recovery, and monitoring with Prometheus and Grafana. See you there!

Learn Jenkins - Production Notification & ChatOps Integration | Learn Jenkins