Get Notified When a Long-Running Command Finishes
Send a phone alert when a long-running terminal command succeeds or fails with a Bash wrapper that preserves the command’s exit status.

With PingRoom Pro, create a private room and incoming webhook, then wrap the long-running command to send one event after it exits. Capture the exit code first: send Command finished for zero and Command failed for anything else.
That small distinction makes the alert useful. A notification that only says “done” still sends you back to the terminal to discover whether the job worked.
Start with the result you need
Use this pattern for work that takes long enough to pull you away from the screen: a database export, model evaluation, media render, test suite, backup, migration, or remote build. Give the job a stable, non-secret name and decide who needs to know when it ends.
The developer who kept checking shows how automation still consumes attention when it cannot ask for input or report that it is done.
Open the room’s settings, choose Connections, then select Incoming webhooks → Add. Store the generated URL in a protected environment variable such as PINGROOM_WEBHOOK_URL. The URL contains a secret, so do not paste it into source control, screenshots, build logs, or client-side code.
The expensive extra step shows why a brief terminal check can cost more attention than the check itself.
Keep the wrapper close to the environment that owns the job. A command running on a remote server should send from that server or its job runner, not from a laptop that may disconnect. Use a separate safe label for each recurring job so Backup failed and Model run finished remain recognizable without exposing the command itself.
This Bash wrapper follows the documented PingRoom webhook pattern. Replace your_long_running_command and the safe label Data export; do not insert raw command arguments or output into the notification.
#!/usr/bin/env bash started_at=$(date +%s) run_id="data-export-$started_at-$$" your_long_running_command command_status=$? finished_at=$(date +%s) duration_seconds=$((finished_at - started_at)) if [ "$command_status" -eq 0 ]; then title="Command finished" result="succeeded" else title="Command failed" result="failed" fi payload=$(printf \ '{"title":"%s","message":"Data export %s after %s seconds","data":{"job":"data-export","status":"%s","exit_code":%s,"duration_seconds":%s},"correlation_id":"%s"}' \ "$title" "$result" "$duration_seconds" "$result" "$command_status" "$duration_seconds" "$run_id") curl --fail --silent --show-error \ --connect-timeout 5 --max-time 15 \ -X POST "$PINGROOM_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $run_id" \ -d "$payload" webhook_status=$? if [ "$webhook_status" -ne 0 ]; then printf 'PingRoom notification request failed\n' >&2 fi exit "$command_status"
The final exit preserves the command’s original result even if the notification request fails, so the wrapper cannot turn a failed job green or a successful job red in CI. The timeouts bound the reporting call, while --fail gives HTTP errors a non-zero curl status. The per-run Idempotency-Key lets the same request be retried without creating a second Ping, as long as the key and body remain unchanged. The title and message show the outcome and duration; structured data carries the job name, status, exit code, and duration. That is usually enough to decide whether to open the terminal.
See the incoming webhook documentation for the payload contract and current limits, and the webhook-to-push guide for setup and credential handling.
Send fewer, clearer completion alerts
Not every exit deserves the same interruption. A useful default is:
- Send both success and failure when you are actively waiting for one job.
- Send only failure for a routine job whose success is already recorded elsewhere.
- Use a stronger room sound only when failure requires prompt human action.
- Put the durable log, artifact, and retry controls in the system that ran the command.
The quiet app shows how one deliberate signal lets you leave the screen instead of checking it.
If one process has several phases, do not send a new push for every percentage point. A PingRoom live-status stream can update one lock-screen card during longer work and send a terminal alert when it ends; see Live Activities and acknowledgement before choosing that richer pattern.
Use a unique correlation_id per concurrent run when your receiver needs to distinguish overlapping work. The example combines the start time and shell process ID; a CI run ID, job ID, or another non-secret identifier is better when one already exists.
Make the escalation rule explicit before the first real run. A success can be a routine Ping or no interruption at all. A failure should reach the person who owns the job with a safe link back to its logs. If nobody responds within the required time, the scheduler, CI platform, or incident tool should escalate through its normal path. PingRoom should not be the only timer or pager in that chain.
Test the paths that usually get missed
Run a short successful command, a deliberate failure, and a job whose webhook request cannot connect. Confirm that the wrapper preserves the original exit code in all three cases. Then lock the receiving phone and check what the message reveals; filenames, customer names, repository paths, and command arguments can expose more than expected on a lock screen.
The wrapper does not keep a local process alive during sleep, retry the job, or prove that someone saw the push. Use a remote runner, scheduler, tmux, or another process supervisor when the command itself must survive disconnection. PingRoom only reports the result after the process exits.
Push delivery can be delayed or suppressed by connectivity, device settings, Focus modes, and provider conditions. Do not use a completion Ping as the only paging or emergency path for critical infrastructure.
See PingRoom Stories for more examples of completion signals, escalation, and human handoffs.
PingRoom
The Ping that cuts through.

