---
title: "Get Notified When a Long Terminal Command Finishes"
description: "Stop babysitting a 40-minute build. One line after any command sends the result to your phone — including when you already walked away."
date: 2026-09-09
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - terminal
  - shell
  - build notifications
  - AI coding agents
  - webhook notifications
---

# Get Notified When a Long Terminal Command Finishes

You start a training run, a full test suite, a `docker build`, or an AI coding agent working through a long task. Then you have two bad options: sit and watch a progress bar, or walk away and come back in twenty minutes to find it failed after ninety seconds.

There is a third option that takes one line.

## The one-liner

Create a channel in Echobell, copy its webhook URL, and append this to whatever you are running:

```bash
pnpm build; curl -sS -X POST https://hook.echobell.one/t/YOUR_TOKEN \
  -H 'content-type: application/json' \
  -d '{"title":"build finished","body":"echobell-web"}'
```

Note the `;` and not `&&`. With `&&` the notification only fires when the command succeeds, which is exactly backwards — a failure is the case you most want to hear about.

## Send the exit code too

A notification that says "finished" is only half the story. Capture the status:

```bash
pnpm build; s=$?; curl -sS -X POST https://hook.echobell.one/t/YOUR_TOKEN \
  -H 'content-type: application/json' \
  -d "{\"title\":\"build $([ $s -eq 0 ] && echo ok || echo FAILED)\",\"status\":\"$s\"}"
```

`s=$?` has to come immediately after the command — anything in between, including the `[` test itself, overwrites `$?`.

## Make it a shell function

Typing that every time defeats the purpose. Put this in `~/.zshrc` or `~/.bashrc`:

```bash
notify() {
  "$@"
  local status=$?
  curl -sS -X POST "$ECHOBELL_HOOK" \
    -H 'content-type: application/json' \
    -d "{\"command\":\"$*\",\"status\":\"$status\",\"host\":\"$(hostname -s)\"}" \
    >/dev/null
  return $status
}
```

Set `ECHOBELL_HOOK` in your shell profile — or better, keep it out of your dotfiles repo entirely and export it from a file you do not commit. Then:

```bash
notify pnpm test
notify cargo build --release
notify python train.py
```

`return $status` at the end matters: it keeps `notify` transparent, so `notify make && ./deploy.sh` still behaves the way you expect.

With that payload, the channel templates can be:

**Title**

```
{{command}} · {{status}}
```

**Body**

```
on {{host}}
```

## Only ring me when it fails

Most runs succeed and you do not need to hear about them. Two channels handle this cleanly: a normal one for everything, and a calling one with a condition:

```
status != "0"
```

Point `notify` at the second channel for anything you would get out of bed for, and the successful runs stay silent.

## Surviving a closed laptop and a dropped SSH session

If the command is running over SSH, closing your laptop kills the shell and the notification never fires. Two fixes:

**tmux** — start the command inside a session and detach:

```bash
tmux new -d -s build 'notify pnpm build'
```

**nohup** — for a one-off:

```bash
nohup bash -c 'notify pnpm build' >/dev/null 2>&1 &
```

Either way the process outlives your connection, and the notification arrives whether or not you are still attached.

## AI coding agents

The same pattern covers a CLI agent working through a long task:

```bash
notify codex exec "refactor the payments module and run the tests"
```

For agents that pause mid-run and wait for a human — an approval gate, a permission prompt — a completion notification is the wrong tool, because the run has not completed. That case needs the agent's own hook or callback, and it is worth a phone call rather than a push. [Turning agent approval gates into phone calls](/en/blog/ai-agent-human-in-the-loop-alerts) covers that, including Claude Code's `Notification` hook and its `agent_needs_input` matcher.

Use both: the hook for "I am stuck", the shell function for "I am done".

## What not to put in the notification

**Not the command output.** It is tempting to pipe the last few lines of a failing build into the body. Resist it — build logs contain tokens, connection strings and customer data more often than people expect, and a notification body ends up on a lock screen. Send the exit code and go look at the terminal.

**Not the channel URL, into a public dotfiles repo.** Anyone with the URL can post to your channel. Keep it in an untracked file, and turn on **POST Only** so a stray link preview cannot fire it.

## Frequently asked questions

### Why not just use `terminal-notifier` or `notify-send`?

Those show a notification on the machine running the command. That works while you are sitting at it. The point of this setup is the case where you are not — a remote box, a closed laptop, a different room.

### Does this work on Windows?

The pattern does; the syntax differs. In PowerShell, `Invoke-RestMethod -Method Post -Uri $env:ECHOBELL_HOOK -ContentType application/json -Body $json` is the equivalent, with `$LASTEXITCODE` in place of `$?`.

### Can I get the notification on my watch?

Yes. Subscriptions deliver to a paired Apple Watch, which is genuinely the right form factor for "the build is done."

### What if the command takes 12 hours?

Nothing in this setup times out — the `curl` runs whenever the command returns. Run it under tmux so a dropped connection does not take the process with it.

### Can my teammates get the same notifications?

Yes. Share the channel and each subscriber picks their own notification type. Handy for a shared training box where more than one person cares that the GPU is free again.

## Wrap-up

One shell function, one channel, and a condition if you only want the failures. It costs about two minutes to set up and it gives you back the twenty you spend watching progress bars.

[Download Echobell for iPhone](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-notify-when-terminal-command-finishes-en&mt=8) or [get it on Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid), then try it on something that takes long enough to walk away from.

---

## Related

- [Turning agent approval gates into phone calls](/en/blog/ai-agent-human-in-the-loop-alerts)
- [Never miss a GitHub Actions failure](/en/blog/github-actions-notifications)
- [Cron job failure alerts](/en/blog/cron-job-failure-alerts)
- [Webhook trigger documentation](/en/docs/webhook)
- [Channel conditions reference](/en/docs/conditions)
