How to Send a New GitHub Issue Alert to a Shared Room
Use one opt-in triage label, a no-checkout GitHub Actions workflow, and one stable replay key to send fixed shared-phone copy.

Use GitHub's own notifications when maintainers only need updates inside GitHub. When one selected kind of new issue should reach a small shared group, add an opt-in triage label and let a GitHub Actions workflow send fixed copy to a PingRoom Room.
The recipe below listens for issues.opened and issues.labeled, but sends only when shared-intake is present. GitHub remains the record for the title, body, author, labels, discussion, and assignment. PingRoom carries only the repository label, issue number, and link back to GitHub.
This is a webhook recipe, not a native GitHub integration. A new issue is an unreviewed submission, not proof of urgency, validity, or work accepted.
Try GitHub's notification paths first
GitHub lets people watch a repository and subscribe to issue activity. GitHub Mobile has notification settings and working hours. GitHub's official integrations can also send issue activity to Microsoft Teams with label filters. Start there when the people who need the update already use those destinations and want GitHub context.
Add a shared PingRoom path only when the selected intake has a separate attention job.
| Need | Better starting point |
|---|---|
| A maintainer wants the normal issue stream | GitHub notifications and repository watch settings |
| A Teams channel should receive selected issue activity | GitHub's Teams integration and label filter |
| One selected intake lane should reach a small shared phone group | GitHub Actions → PingRoom incoming webhook |
| Someone needs the title, body, author, labels, or discussion | Open the issue in GitHub |
The opt-in label is the noise boundary. This example uses shared-intake: it means “send the short shared intake signal,” not “this issue is important” or “this issue is safe.” Decide who may apply the label, and use repository permissions and rules for the actual triage policy.
Create the Room and protect its URL
Create or choose a private Room for the people who need the signal. The Room owner needs PingRoom Pro for incoming webhooks.
Open Settings → Connections → From another app, add a webhook, leave Quick Ping on Custom message, and copy the full URL. The incoming-webhook walkthrough covers setup. The webhook reference carries the current request limits and response contract.
In the GitHub repository, open Settings → Secrets and variables → Actions and add a repository secret named PINGROOM_WEBHOOK_URL. Treat that full URL as a credential. Do not put it in the workflow file, an issue, a log, an artifact, or browser-side code.
Create the shared-intake label. Then choose a short REPOSITORY_LABEL that is safe on a lock screen, such as Mobile project.
Add one label-gated workflow
Save this as .github/workflows/new-issue-room-alert.yml on the repository's default branch.
name: Shared new-issue alert on: issues: types: [opened, labeled] permissions: {} jobs: notify-room: if: > contains(github.event.issue.labels.*.name, 'shared-intake') && (github.event.action == 'opened' || github.event.label.name == 'shared-intake') runs-on: ubuntu-latest env: PINGROOM_WEBHOOK_URL: ${{ secrets.PINGROOM_WEBHOOK_URL }} REPOSITORY_LABEL: "Selected repository" REPOSITORY: ${{ github.repository }} ISSUE_ID: ${{ github.event.issue.id }} ISSUE_NUMBER: ${{ github.event.issue.number }} SERVER_URL: ${{ github.server_url }} steps: - name: Send shared new-issue alert shell: bash run: | node <<'NODE' const { createHash } = require("node:crypto"); async function main() { const webhookUrl = process.env.PINGROOM_WEBHOOK_URL; const label = process.env.REPOSITORY_LABEL; const repository = process.env.REPOSITORY; const issueId = process.env.ISSUE_ID; const issueNumber = process.env.ISSUE_NUMBER; const serverUrl = process.env.SERVER_URL; if (!webhookUrl) throw new Error("Missing PINGROOM_WEBHOOK_URL"); const destination = new URL(webhookUrl); if (destination.protocol !== "https:") throw new Error("PINGROOM_WEBHOOK_URL must use HTTPS"); if (!label || label.length > 80 || /[\r\n]/.test(label)) throw new Error("REPOSITORY_LABEL must be 1–80 single-line characters"); if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository ?? "")) throw new Error("Invalid REPOSITORY"); if (!/^\d+$/.test(issueId ?? "") || !/^\d+$/.test(issueNumber ?? "")) throw new Error("Invalid issue identity"); const server = new URL(serverUrl); if (server.protocol !== "https:" || server.username || server.password || server.pathname !== "/" || server.search || server.hash) throw new Error("Invalid SERVER_URL"); const issueUrl = `${server.origin}/${repository}/issues/${issueNumber}`; const digest = createHash("sha256").update(`${repository}:${issueId}:shared-intake`).digest("hex"); const eventId = `github-new-issue:${digest}`; const payload = { action: null, title: "New GitHub issue", message: `${label} · issue #${issueNumber}`, data: { url: issueUrl, button_label: "Open issue", issue_number: Number(issueNumber) }, correlation_id: eventId, }; const response = await fetch(webhookUrl, { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", "Idempotency-Key": eventId }, body: JSON.stringify(payload), redirect: "error", signal: AbortSignal.timeout(10000), }); if (!response.ok) throw new Error(`PingRoom returned HTTP ${response.status}`); const accepted = await response.json(); if (accepted.success !== true) throw new Error("PingRoom did not accept the issue alert"); console.log(`PingRoom accepted ${accepted.notification_id}`); } main().catch((error) => { console.error(error.message); process.exit(1); }); NODE
GitHub's issues event reference documents both opened and labeled activity and requires the workflow file to exist on the default branch. permissions: {} removes repository access from the workflow's GITHUB_TOKEN. The job does not check out code, install a dependency, call GitHub's API, or execute issue content.
The two event types cover both ways the lane can begin:
- An issue created with
shared-intakealready attached qualifies onopened. - An existing issue qualifies when a maintainer later adds exactly
shared-intake. - Adding another label later does not qualify, even while
shared-intakeremains present.
Make both paths converge on one Ping
The replay key is a SHA-256 digest of the repository, GitHub's numeric issue ID, and the fixed lane name. It does not use the workflow run ID. If GitHub emits both a qualifying opened event and a labeled event, both requests have the same key and body, so PingRoom can return the first accepted result instead of creating a second Ping.
Removing and re-adding shared-intake also reuses that identity. This recipe therefore means one shared signal per issue for this lane, not one signal every time the label changes. Use a different explicit workflow if repeated state transitions are the real task.
The Ping step is allowed to fail the workflow. That makes a missed shared alert visible in GitHub Actions while leaving the issue record unchanged. Do not treat this small signal as a guaranteed escalation path; keep durable triage and paging in the systems that own them.

Pingo the Pigeon, PingRoom's fictional Chief Marketing Officer, is watching the second marker stop at the gate. He has no GitHub access, private-Room access, or authority to label an issue or send its alert. Human maintainers choose the lane, workflow, Room, recipients, copy, and failure policy.
These AI-generated editorial illustrations feature approved PingRoom avatars and Pingo. They are not GitHub or PingRoom screenshots and do not depict customers.
Keep issue content out of the phone copy
The workflow sends a maintainer-written repository label and numeric issue number. It does not send the issue title, body, author, labels, comments, assignees, or repository contents.
The Ping carries an Open issue button that returns an authorized Room member to GitHub for the real record. If the repository name is sensitive, use a neutral REPOSITORY_LABEL; the issue URL still contains the repository path and is visible to Room members. Do not route private reports, credentials, security findings, customer data, or unreleased plans through a lock-screen preview.
Rehearse the actual boundary
Use harmless test issues in the selected repository.
- Open an issue without
shared-intake. The job should be skipped. - Add
shared-intake. One New GitHub issue Ping should appear. - Add a different label. No new Ping should appear.
- Remove and re-add
shared-intake. PingRoom should return the original accepted result, not create another Ping. - Open a second test issue with
shared-intakealready attached. It should create one new Ping. - Tap Open issue as a Room member. GitHub should still enforce repository access.
- Remove the secret and try a fresh test issue. The workflow should fail, while the issue remains in GitHub.
For this article, we ran the exact embedded Node request against a stateful local mock of PingRoom's documented contract. The checks covered acceptance, exact replay, changed-body conflict, a distinct issue, required headers, HTTPS and input validation, privacy exclusions, HTTP rejection, and malformed JSON. They did not use a live GitHub repository, PingRoom Room, phone, or notification receipt.
An HTTP 200 with success: true means PingRoom accepted and queued the Ping. Connectivity, notification permission, Focus settings, Room mutes, and operating-system policy still decide what appears on each phone. GitHub remains the source of truth.
If one selected intake lane deserves a shared signal, create the destination Room and webhook, add the secret, and rehearse it with non-sensitive issues. For a selected issue leaving the workflow, use the separate issue-closed alert recipe.
GitHub is a trademark of GitHub, Inc. PingRoom is not affiliated with or endorsed by GitHub.
PingRoom
Short-form communication.


