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.

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 | 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 | 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 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:
- Paste the complete incoming-webhook URL as the value.
- Choose Masked and hidden when your GitLab version offers it. Otherwise use Masked.
- Clear Expand variable reference.
- 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:
- Run a successful pipeline and confirm one GitLab pipeline succeeded Ping.
- Run a deliberate failure and confirm one GitLab pipeline failed Ping.
- Retry the same notification job without changing its body and confirm that no duplicate Ping appears.
- Reuse the same idempotency key with a changed body in a controlled test and confirm an HTTP
409conflict.

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.


