# How to Send GitLab CI Pipeline Results to Your Phone

> Add two small GitLab CI .post jobs, protect the Room webhook URL, and send one useful success or failure result without changing the pipeline outcome.

- Published: 2026-10-18
- Author: PingRoom
- Category: Developers
- Topics: GitLab CI push notification, GitLab pipeline phone alert, GitLab CI webhook notification
- Canonical: https://pingroom.io/blog/gitlab-ci-pipeline-push-notifications
- Cover image: https://pingroom.io/blog/gitlab-ci-pipeline-push-notifications-cover.webp
- Cover description: Two PingRoom avatars watch a miniature pipeline end at one result dish while Pingo stands beside the terminal arch.

To send a GitLab CI pipeline result to your phone, create a PingRoom incoming webhook, save its complete URL as a protected GitLab CI/CD variable, and add two small `.post` jobs: one for success and one for failure. Each terminal job sends a single concise Ping to the people in the Room.

This is a webhook recipe, not a native GitLab integration. GitLab remains the source of truth for the pipeline, jobs, logs, and artifacts.

## First choose the right notification path

GitLab already supports pipeline notification settings. Use the narrowest path that matches the job:

| Need                                                    | Better starting point                                                                                        | Why                                                                                     |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| Notify one GitLab user by email                         | [GitLab notification settings](https://docs.gitlab.com/user/profile/notifications/)                          | No extra service or CI job is needed                                                    |
| Notify a shared group with one recognizable phone alert | The PingRoom `.post` recipe below                                                                            | The destination is a Room, and the copy can name the project, ref, result, and pipeline |
| Transform many GitLab event types before delivery       | A trusted relay using [GitLab project webhooks](https://docs.gitlab.com/user/project/integrations/webhooks/) | The relay can validate, filter, reshape, and route the event stream                     |

Do not send every job update. Start with terminal **succeeded** and **failed** states. Add another state only when it changes what someone should do next. The broader [CI/CD notification guide](/blog/ci-cd-deployment-notifications) explains how to keep the event stream useful.

## Prepare the Room and protected variable

You need:

- a private PingRoom Room for the people who should receive the result;
- PingRoom Pro, because incoming webhooks are a Pro connection;
- permission to add a GitLab CI/CD variable and edit `.gitlab-ci.yml`; and
- a runner that can pull the container image used by the notification jobs.

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

In GitLab, open **Settings → CI/CD → Variables** and create `PINGROOM_WEBHOOK_URL`:

1. Paste the complete incoming-webhook URL as the value.
2. Choose **Masked and hidden** when your GitLab version offers it. Otherwise use **Masked**.
3. Clear **Expand variable reference**.
4. Select **Protect variable** if only protected branches and tags should send this notification.

Do not place the URL in `.gitlab-ci.yml`, a shell trace, an artifact, or a screenshot. GitLab's [CI/CD variable guidance](https://docs.gitlab.com/ci/variables/) also warns that masking does not make untrusted pipeline code safe.

## Add two terminal notification jobs

Merge this block into a pipeline that already has at least one ordinary build, test, or deploy job. GitLab runs `.post` after user-defined stages; a pipeline containing only `.pre` or `.post` jobs does not run.

```yaml
.notify_pingroom:
  stage: .post
  image: python:3.13-alpine
  allow_failure: true
  script:
    - |
      python - <<'PY'
      import json
      import os
      from urllib.request import Request, urlopen

      result = os.environ["PINGROOM_RESULT"]
      project = os.environ["CI_PROJECT_PATH"]
      pipeline_id = os.environ["CI_PIPELINE_ID"]
      correlation_id = (
          f"gitlab-{os.environ['CI_PROJECT_ID']}-{pipeline_id}"
      )[:180]

      body = {
          "title": f"GitLab pipeline {result}",
          "message": (
              f"{project} · {os.environ['CI_COMMIT_REF_NAME']}"
          )[:120],
          "data": {
              "project": project,
              "pipeline": pipeline_id,
              "ref": os.environ["CI_COMMIT_REF_NAME"],
              "result": result,
              "pipeline_url": os.environ["CI_PIPELINE_URL"],
          },
          "correlation_id": correlation_id,
      }

      request = Request(
          os.environ["PINGROOM_WEBHOOK_URL"],
          data=json.dumps(body).encode("utf-8"),
          headers={
              "Accept": "application/json",
              "Content-Type": "application/json",
              "Idempotency-Key": f"{correlation_id}-{result}",
          },
          method="POST",
      )

      with urlopen(request, timeout=10) as response:
          if response.status != 200:
              raise RuntimeError(
                  f"PingRoom returned HTTP {response.status}"
              )

      print(f"PingRoom accepted the {result} pipeline result")
      PY

notify_pingroom_success:
  extends: .notify_pingroom
  variables:
    PINGROOM_RESULT: succeeded
  when: on_success

notify_pingroom_failure:
  extends: .notify_pingroom
  variables:
    PINGROOM_RESULT: failed
  when: on_failure
```

The script uses Python's JSON encoder, so a slash, quote, or other ordinary character in a project path or branch name cannot break the request body. It prints the accepted result, never the secret URL or response payload.

`allow_failure: true` is deliberate. A notification problem should appear in GitLab without changing a successful build into a failed pipeline. If delivery must gate a release, implement that as a separate trusted control with its own policy; a phone alert should not become deployment authorization.

## Give one pipeline a stable identity

The recipe uses GitLab's project ID and pipeline ID as `correlation_id`. It appends the terminal result to the `Idempotency-Key`.

An exact retry of the same job, body, and key can replay the original PingRoom response instead of creating a second Ping. Reusing the same key with a changed body returns `409 idempotency_conflict`. If a failed pipeline is retried and later succeeds, the result suffix changes, so the Room receives a distinct recovery update tied to the same pipeline correlation.

The message stays short enough for a notification preview. Keep secrets, raw logs, commit messages, customer data, and test output in GitLab. The pipeline URL points the recipient back to the system that owns the technical record.

## Keep the webhook away from untrusted code

Anyone who can run pipeline code while `PINGROOM_WEBHOOK_URL` is available may be able to capture it. A masked or hidden variable reduces accidental exposure; it does not stop malicious code from sending the value elsewhere.

For merge requests from forks or other untrusted sources, do not expose the variable. Use protected refs where that fits the workflow, review `.gitlab-ci.yml` changes before running them with secrets, or move notification delivery into a separate trusted project or completion pipeline.

## Test success, failure, replay, and conflict

Before relying on the workflow, use a non-sensitive test Room and check four cases:

1. Run a successful pipeline and confirm one **GitLab pipeline succeeded** Ping.
2. Run a deliberate failure and confirm one **GitLab pipeline failed** Ping.
3. Retry the same notification job without changing its body and confirm that no duplicate Ping appears.
4. Reuse the same idempotency key with a changed body in a controlled test and confirm an HTTP `409` conflict.

![Two PingRoom avatars test a closed credential box and a duplicate gate while Pingo watches one accepted result and one held replay.](/blog/gitlab-ci-pipeline-push-notifications-1.webp)

[Pingo the Pigeon](/Pingo), PingRoom's fictional CMO, observed the duplicate gate from the only position with no access to the credential box. He cannot read GitLab, enter a private Room, inspect a pipeline, or send a Ping. The human team chose the fields, recipients, and secret boundary.

Our recorded fixture and local mock check both terminal payloads, the request headers, exact replay, and changed-body conflict. They do not run a live GitLab runner or PingRoom Room, so the harmless end-to-end test still belongs in your own environment.

## Know what HTTP 200 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 GitLab's pipeline page, job logs, and artifacts 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) for field limits, cooldown behavior, error handling, and acknowledgement options. For a first request outside GitLab, use the [webhook-to-phone walkthrough](/blog/webhook-to-push-notification).

When the four tests pass, [open PingRoom](/app/rooms), create the destination Room, and connect only the terminal GitLab 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._
