Skip to main content
All posts
Developers5 min read

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.

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:

NeedBetter starting pointWhy
Notify one GitLab user by emailGitLab notification settingsNo extra service or CI job is needed
Notify a shared group with one recognizable phone alertThe PingRoom .post recipe belowThe destination is a Room, and the copy can name the project, ref, result, and pipeline
Transform many GitLab event types before deliveryA trusted relay using GitLab project webhooksThe 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 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 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.

.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.

Pingo the Pigeon, 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 for field limits, cooldown behavior, error handling, and acknowledgement options. For a first request outside GitLab, use the webhook-to-phone walkthrough.

When the four tests pass, open PingRoom, 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.

PingRoom

Short-form communication.

View all posts