# How to Send Jenkins Pipeline Results to Your Phone

> Use a protected Jenkins credential and a Declarative Pipeline post block to send one useful terminal build result to a PingRoom Room.

- Published: 2026-10-17
- Author: PingRoom
- Category: Developers
- Topics: Jenkins push notification, Jenkins post build webhook, Jenkins Pipeline phone alert
- Canonical: https://pingroom.io/blog/jenkins-pipeline-push-notifications
- Cover image: https://pingroom.io/blog/jenkins-pipeline-push-notifications-cover.webp
- Cover description: Two PingRoom avatars watch a miniature build railway end in one shared tray while Pingo inspects the final clay token.

To send a Jenkins Pipeline result to your phone, create a PingRoom incoming webhook, store its full URL as a Jenkins **Secret Text** credential, and call it from the Pipeline's top-level `post` section. Send one terminal result—**succeeded**, **failed**, **unstable**, or **aborted**—rather than every build step.

The recipe below sends a short Room Ping after the Pipeline finishes. It is a webhook recipe, not a native Jenkins integration, and the Jenkins build remains the source of truth.

## Decide which results deserve attention

Jenkins supports several `post` conditions. Start with four that change what a person might do next:

| Jenkins condition | Room title                | Human meaning                              |
| ----------------- | ------------------------- | ------------------------------------------ |
| `success`         | Jenkins build succeeded   | The run completed successfully             |
| `failure`         | Jenkins build failed      | Someone may need to inspect the build      |
| `unstable`        | Jenkins build is unstable | The run completed with a qualified problem |
| `aborted`         | Jenkins build was aborted | The run ended without a terminal result    |

Do not alert on every stage, console line, retry, or test case. The [CI/CD notification guide](/blog/ci-cd-deployment-notifications) explains how to keep the broader event stream readable. This page focuses on the Jenkins implementation.

## Prepare the Room and Jenkins credential

You need:

- a private PingRoom Room for the people who should receive the result;
- PingRoom Pro, because creating and using an incoming webhook is a Pro feature;
- permission to add a Secret Text credential and edit the Pipeline; and
- `curl` on the Jenkins agent that will run the `post` step.

In PingRoom, open the Room, add an incoming webhook, and copy its complete URL. A Room can carry up to four incoming webhooks. The final path segment is the secret, so treat the whole URL like a password.

In Jenkins, add that URL as a **Secret Text** credential with the ID `pingroom-room-webhook`. Define it at the lowest practical folder or item scope. Do not paste the URL into a `Jenkinsfile`, a shared library, a screenshot, or a build parameter.

## Add this helper and post block

Place the helper before your Declarative `pipeline` block. Merge the `post` section into your existing Pipeline if it already has one.

```groovy
import groovy.json.JsonOutput

def sendPingRoomResult(String result) {
  def correlationId = "${env.BUILD_TAG}".take(180)
  def payloadFile = ".pingroom-result-${env.BUILD_NUMBER}.json"
  def payload = JsonOutput.toJson([
    title: "Jenkins build ${result}",
    message: "${env.JOB_NAME} #${env.BUILD_NUMBER}".take(120),
    data: [
      job: env.JOB_NAME,
      build: env.BUILD_NUMBER,
      result: result,
      build_url: env.BUILD_URL
    ],
    correlation_id: correlationId
  ])

  writeFile file: payloadFile, text: payload

  withCredentials([
    string(
      credentialsId: 'pingroom-room-webhook',
      variable: 'PINGROOM_WEBHOOK_URL'
    )
  ]) {
    withEnv([
      "PINGROOM_PAYLOAD_FILE=${payloadFile}",
      "PINGROOM_IDEMPOTENCY_KEY=${correlationId}-${result}"
    ]) {
      def notificationStatus = sh label: 'Notify PingRoom', returnStatus: true, script: '''
        set +x
        status=0
        curl --fail --silent --show-error \
          --request POST \
          --header 'Accept: application/json' \
          --header 'Content-Type: application/json' \
          --header "Idempotency-Key: $PINGROOM_IDEMPOTENCY_KEY" \
          --data-binary "@$PINGROOM_PAYLOAD_FILE" \
          "$PINGROOM_WEBHOOK_URL" >/dev/null || status=$?
        rm -f "$PINGROOM_PAYLOAD_FILE"
        exit "$status"
      '''

      if (notificationStatus != 0) {
        echo "PingRoom notification failed with curl status ${notificationStatus}"
      }
    }
  }
}

pipeline {
  agent any

  stages {
    stage('Build') {
      steps {
        sh './build.sh'
      }
    }
  }

  post {
    success {
      script { sendPingRoomResult('succeeded') }
    }
    failure {
      script { sendPingRoomResult('failed') }
    }
    unstable {
      script { sendPingRoomResult('unstable') }
    }
    aborted {
      script { sendPingRoomResult('aborted') }
    }
  }
}
```

The single-quoted shell script matters. Jenkins expands the credential in the shell instead of interpolating the secret into a Groovy string. `set +x` stops the shell from echoing commands. Jenkins can mask an accidentally printed credential, but masking is not a security boundary.

The payload file contains build metadata, not the secret URL, and the shell removes it after the request. Keep its fields non-sensitive; the Room title and message may appear on a lock screen. `returnStatus: true` keeps a notification problem from rewriting the build result, while the explicit Jenkins console line preserves evidence that the POST failed. If webhook delivery must gate another process, implement that as a separate trusted job with its own failure policy.

## Keep one identity for one terminal event

`BUILD_TAG` gives the run a stable identity. The recipe uses it as `correlation_id` and appends the terminal result to the `Idempotency-Key`.

If Jenkins retries the same HTTP request with the same body and key, PingRoom can replay the completed response instead of creating a duplicate Ping. Reusing that key with a changed body returns `409 idempotency_conflict`. If your Jenkins folder paths are unusually long, replace the truncated `BUILD_TAG` with a stable, shorter identifier that remains unique inside the destination Room.

Do not use the same idempotency key for different terminal results. A failed run and a later successful rebuild are different source events and need different build numbers.

## Do not expose the webhook to untrusted Pipeline code

Anyone who can run code while the Secret Text value is in scope may be able to capture it. Credential masking cannot make an untrusted job safe.

For a multibranch Pipeline that builds pull requests from forks, keep this notification step out of untrusted branches unless Jenkins' trust policy prevents those changes from receiving the credential. A practical pattern is to send the Room Ping only from a trusted protected branch or from a separate trusted completion job.

Also avoid co-locating trusted secret-using jobs with jobs controlled by untrusted parties on the same agent account. Jenkins' own credential guidance notes that environment variables can be visible to other processes under that account.

## Test four cases before relying on it

Use a non-sensitive test Room and run:

1. one successful build;
2. one deliberate failed build;
3. one exact retry with the same payload and idempotency key; and
4. one run from an untrusted branch, which should not receive the credential at all.

Check the Jenkins console for accidental URL exposure. Confirm the Room receives one concise Ping for each intended terminal event. Then rotate the webhook secret and verify that the old URL no longer works.

![Two PingRoom avatars test a miniature duplicate gate while Pingo watches one clay build token pass and its exact replay stop.](/blog/jenkins-pipeline-push-notifications-1.webp)

[Pingo the Pigeon](/Pingo), PingRoom's fictional CMO, supervised our replay test from beside the gate. He cannot access Jenkins, enter a private Room, inspect a build, or send a Ping on anyone's behalf. The human team chose the states, fields, and credential boundary.

Our recorded fixture and local mock checked the POST method, JSON headers, terminal-state body, exact replay, and changed-body conflict. It did not run a live Jenkins controller or PingRoom Room, so the final harmless end-to-end test still belongs in your own environment.

## Know what a successful POST proves

An HTTP `200` means PingRoom accepted and queued the Ping. It does not prove that every phone displayed it. Connectivity, operating-system permissions, Focus modes, battery settings, and Room preferences still affect delivery.

Keep Jenkins console output and build history as the technical record. Keep your existing pager and escalation path for critical systems. PingRoom is the short attention path to the people in the Room, not a build log, incident-management system, or delivery guarantee.

Review the complete [incoming webhook contract](/documents/webhooks) if you need field limits, cooldown behavior, or error handling. For a first request outside Jenkins, use the [webhook-to-phone walkthrough](/blog/webhook-to-push-notification).

When the four test cases pass, [open PingRoom](/app/rooms), create the destination Room, and connect only the terminal Jenkins results that someone should act on.

_The two editorial illustrations were created with AI using approved PingRoom character references and reviewed by the PingRoom team._
