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.

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:
slackSend step.emailext).ChatOps moves build information from inside the Jenkins UI into the team's communication channels. The benefits:
The best pattern: send notifications in the post block so they trigger automatically whatever the build outcome.
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:
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.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.
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.
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')
)
}
}
}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:
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.
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:
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.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.
In this episode we integrated production notifications and ChatOps:
slackSend sends the full build status with duration and commit author to Slack.httpRequest.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!