Claude Code fires two hook events you can alert on: Stop when it finishes a turn, and Notification when it needs your permission or input. Add both to ~/.claude/settings.json with a command that shows a banner, plays a sound or sends a push, or run npx anotifier@latest setup, the Claude Code notifier, to wire both for you. Ghostty, Kitty and iTerm2 also show a built-in desktop notification, and preferredNotifChannel can ring the terminal bell elsewhere. Below: which event fires when, banner, sound or phone, every option with configs, snooze and quiet hours, and fixes when nothing arrives.
Rather skip the manual setup? One command wires every agent you have installed to desktop toasts, phone push, and Slack, Discord or Telegram:
Updated 2026-10-16 · also see the Claude Code notifier and the full docs
| You want | Claude Code event | Read this |
|---|---|---|
| An alert when Claude finishes a turn | Stop | Option 2 below, or npx anotifier@latest setup |
| An alert when it needs permission or an answer | Notification, matcher permission_prompt | Permission and waiting-for-input notifications |
| A sound, nothing else | Stop and Notification, or preferredNotifChannel | Claude Code notification sound |
| A push on your phone | Any hook, posting to ntfy | Phone push with ntfy and the phone page |
| A message in Slack, Discord or Telegram | Any hook, posting to a webhook | Slack, Discord, Telegram |
| A real toast on Windows or WSL | Any hook, plus a toast backend | Windows and WSL notifications |
| A banner on macOS or Linux | Any hook, running osascript or notify-send | macOS and Linux notifications |
| A warning before a usage limit or auto-compact | Neither: a statusline command, not a hook | Usage limit warning and auto-compact warning |
| Alerts that never arrive | Check the hook is registered | Notifications not working |
Claude Code runs a hook when one of its lifecycle events happens. Two matter for alerts. Stop fires when Claude finishes responding. Notification fires when Claude Code sends a notification, and its payload names the kind in notification_type:
| Event or type | Fires when | Good for |
|---|---|---|
Stop | Claude finishes responding. The payload includes last_assistant_message, the final reply | The done alert |
Notification, permission_prompt | Claude needs you to approve a tool use and the prompt has waited about six seconds | The needs-permission alert |
Notification, idle_prompt | Claude finished about 60 seconds ago and you haven't typed since | A late reminder, not the done alert |
Notification, elicitation_dialog | An MCP server opens a form and you haven't typed for about six seconds | A question from a tool |
Notification, auth_success | Authentication completes | Nothing: filter it out |
An empty matcher runs the hook for every notification type, so a bare Notification hook also fires for auth_success and the rest. Set "matcher": "permission_prompt" to alert on approvals only. Because idle_prompt arrives about a minute after Stop, use Stop for the done alert. The permission notifications guide lists every type with its timing, including how Claude Desktop and the VS Code extension differ from the terminal.
| Channel | What you get | How |
|---|---|---|
| Terminal bell | A beep in the terminal, no text | "preferredNotifChannel": "terminal_bell" (sound guide) |
| Desktop banner | A toast with a title and a line of text | A hook that runs osascript, notify-send or PowerShell (Option 2) |
| Phone push (ntfy) | A push on Android or iOS; free, no account | A hook that POSTs to a topic (Option 3) |
| Slack, Discord, Telegram | A chat message | A webhook URL; anotifier has a format for each (Option 4) |
Claude Code also has its own phone push. With Remote Control active, /config offers Push when Claude decides and Push when actions required, and the Claude app on iOS or Android gets the push. It needs a Pro, Max, Team or Enterprise plan; API keys are not supported (Remote Control docs). The hooks in this guide need no Remote Control session.
When Claude finishes or pauses for a permission prompt and you appear to be away from the terminal, Claude Code sends a desktop notification, but by default only in Ghostty, Kitty, and iTerm2. In any other terminal, set preferredNotifChannel in ~/.claude/settings.json to ring the terminal bell instead:
{ "preferredNotifChannel": "terminal_bell" }set -g allow-passthrough on to ~/.tmux.conf, or the notification never reaches the outer terminal.What you get: a beep or a plain banner on the machine you are sitting at, with no project name, no message content, and nothing on your phone. Terminals without the built-in notification, such as Warp or the VS Code integrated terminal, need the bell or a hook. For the bell, custom sounds, and turning them off, see the Claude Code notification sound guide; for what the hooks do inside the editor, see anotifier in VS Code.
Hooks are shell commands Claude Code runs on lifecycle events, configured under hooks in ~/.claude/settings.json. A minimal macOS example, one banner for Stop and one for Notification:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude finished\" with title \"Claude Code\"'"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude needs your input\" with title \"Claude Code\"'"
}
]
}
]
}
}osascript banners come from Script Editor. If Script Editor has no notification permission, the command fails silently and macOS never asks. Run osascript -e 'display notification "test"' once, then allow Script Editor in System Settings > Notifications.notify-send "Claude Code" "Claude finished". It needs a desktop notification daemon, which headless servers, SSH sessions, and most containers lack; install libnotify-bin on Debian and Ubuntu if the command is missing.MessageBox, which is a dialog box and can open behind your terminal. A real toast needs a PowerShell module such as BurntToast. Inside WSL, powershell.exe must be reachable through Windows interop. Claude Code notifications on Windows and WSL covers both roads, Do not disturb, and fixes.The hook receives a JSON payload on stdin: session_id, cwd, transcript_path, and for Notification the message and notification_type. Stop also carries last_assistant_message, so a script can put Claude's own words in the banner. Type /hooks in Claude Code to check what is registered. To alert only on approvals, add "matcher": "permission_prompt" to the Notification entry (see which event fires when).
ntfy is a free, open-source push service with Android and iOS apps and no account. Install the app, subscribe to a topic name only you know, and make the hook POST to it:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "curl -s -d 'Claude finished' -H 'Title: Claude Code' https://ntfy.sh/your-secret-topic"
}
]
}
]
}
}Because public ntfy.sh topics are guessable, send generic text only, or run your own ntfy server before putting conversation content in the body. The ntfy phone push guide covers topics, privacy and a self-hosted server.
npx anotifier@latest setupThe wizard detects Claude Code, registers the Stop and Notification hooks in ~/.claude/settings.json (backing the file up first), checks the desktop toast backend for your OS, and offers to create an ntfy topic for phone push. Restart Claude Code and you are done. It also works as a Claude Code plugin: /plugin marketplace add DevinoSolutions/anotifier-for-claude-codex-cursor then /plugin install anotifier@anotifier. The same setup covers Codex CLI, Cursor and Gemini CLI, and the webhook channel reaches Slack, Discord and Telegram.
my-app · Claude Code. ntfy stays generic by default because public topics are guessable; ntfy.richContent opts in.permission_prompt arrives as an urgent "Needs your permission". The idle_prompt reminder is delivered at default priority. auth_success sends nothing, and a notification type Claude Code adds later still alerts.Searching for ccnotify? Several unrelated Claude Code tools share that name, and none of them is anotifier. ccnotify: which one is which tells them apart and compares each with anotifier.
| Feature | Built-in | DIY hook | anotifier |
|---|---|---|---|
| Desktop toast | Terminal-dependent | Yes, fixed text | Yes, Windows · macOS · Linux · WSL |
| Phone push | No | With a curl to ntfy | Yes, ntfy, generic by default |
| Slack / Discord / Telegram | No | Write the JSON yourself | Yes, pick a format |
| Shows what the agent said | No | No | Yes for Claude Code (toast + webhook) |
| Names the project | No | Hard-code it | Yes, my-app · Claude Code in every title |
| Urgent for approvals, calm for done | No | Two hooks, two scripts | Yes, per-event priority |
| Click to focus the window | No | No | Yes (Windows) |
| Every agent, one config | No | Repeat per agent | Yes |
| Snooze / quiet hours | No | No | Yes |
| Setup time | 1 setting | 10–20 min per agent | One command |
anotifier is free, open source (AGPL-3.0), has zero dependencies, and uninstalls cleanly with anotifier uninstall. Full reference in the docs.
Three controls in anotifier, none of which can stall Claude Code: the hook still exits successfully when a run is silenced.
events.needs_input.idleReminderPriority sets the level for the idle_prompt reminder only (min, low, default, high or urgent). Permission prompts keep their own priority.anotifier snooze 2h silences every channel (toast, ntfy, webhook and the terminal bell) until it expires. 30m, 90s and a bare 45 (minutes) work too; anotifier snooze off cancels it, and anotifier status shows whether you are snoozed.{
"quietHours": {
"enabled": true,
"from": "22:00",
"to": "08:00"
},
"events": {
"needs_input": {
"idleReminderPriority": "low"
}
}
}Put that in ~/.anotifier/config.json. Snooze and quiet hours work independently, and either one alone silences the run. The docs list every config key.
/hooks and confirm the hook is listed under Stop and Notification.preferredNotifChannel to terminal_bell or add a hook; inside tmux turn on allow-passthrough.permission_prompt waits about six seconds and each keystroke defers it, and idle_prompt comes about 60 seconds after Claude finishes.anotifier status shows which tools are wired and whether you are snoozed, anotifier test fires a test notification (add toast, ntfy, webhook, bell or both to test one channel), and anotifier doctor diagnoses each channel.Add a Stop hook to ~/.claude/settings.json that runs a notification command. Stop fires when Claude finishes responding. Or run npx anotifier@latest setup, which registers the Stop and Notification hooks and wires a desktop toast, a terminal bell and optional phone push.
Add a Notification hook with "matcher": "permission_prompt". It fires once an approval prompt has waited about six seconds, so a prompt you answer right away does not ping you. anotifier sends it as an urgent "Needs your permission" alert. The permission notifications guide covers every notification type.
Yes, two ways. A hook can post to ntfy, a free push service with Android and iOS apps and no account; anotifier creates the topic for you. Or use Claude Code's own mobile push: with Remote Control active, enable Push when Claude decides or Push when actions required in /config. That needs a Pro, Max, Team or Enterprise plan.
The idle_prompt notification is sent about 60 seconds after Claude finishes, and only if you haven't typed since, so alerting on Notification alone feels a minute late. Use a Stop hook for the done alert. If nothing arrives, restart Claude Code, check /hooks, and run anotifier doctor; the built-in desktop notification only works in Ghostty, Kitty and iTerm2.
Yes. anotifier posts to a Slack, Discord or Telegram webhook, or to any HTTP endpoint with an optional Authorization header. Set webhook.format to slack, discord, telegram or generic. The Slack, Discord and Telegram pages have the steps.
With anotifier, run anotifier snooze 8h to silence every channel for a while, or enable quietHours in ~/.anotifier/config.json for a nightly window such as 22:00 to 08:00. Both silence the toast, ntfy, webhook and terminal bell together, and neither can stall Claude Code.
Yes. One anotifier setup wires Claude Code, Codex CLI, Cursor and Gemini CLI to the same channels. Codex alerts on Stop and PermissionRequest, Gemini CLI on AfterAgent and Notification, and Cursor when a run finishes only, because it has no needs-input event.
Yes. The hook payload includes the transcript path. anotifier reads the last assistant message (or the question, for needs-input events) from it and puts that in the toast and webhook body, with the project name in the title. ntfy stays generic unless you set ntfy.richContent.
Yes. Hooks are part of Claude Code itself and fire identically in the VS Code extension and the integrated terminal. anotifier adds click-to-focus on Windows so the toast brings the right window forward.
Not noticeably. Claude Code waits for the hook to exit, and anotifier sends every channel in parallel, each with its own 5 to 7 second timeout. It registers its hook with a 10-second timeout (Claude Code's default for command hooks is 600 seconds) and always exits 0, so it can never stall a session.
Yes. Set preferredNotifChannel to terminal_bell for the terminal bell, or add a Stop hook that plays a sound file, such as afplay /System/Library/Sounds/Glass.aiff on macOS. The notification sound guide has commands for Linux and Windows.
One command wires Claude Code and every other agent you have installed.
npx anotifier@latest setup