Claude Code notifications
not working.
There are four causes and they are easy to tell apart. The OS is blocking the app that raises the alert, preferredNotifChannel names a channel your terminal cannot serve, the config command in most guides was removed, or the hook sits on Stop and fires every turn.
The OS permission is yours to grant. For the other three, npx @pushary/agent-hooks bell raises the alert itself on the right event, so the channel setting stops mattering. No account, no API key, no network call.
The four causes, in the order they happen
The OS is blocking the app that raises it
Claude Code has no application bundle of its own, so a desktop alert is raised by whatever is hosting the session: your terminal emulator, or the Claude app if you are on Claude Desktop. The permission lives against that app, not against something called Claude Code. Open System Settings, then Notifications, find Ghostty, iTerm2, Kitty, Terminal or Claude, and switch Allow notifications on.
preferredNotifChannel is on a channel your terminal cannot serve
The default is auto, which raises a real desktop notification in iTerm2, Ghostty and Kitty and does nothing at all anywhere else. In Terminal.app, Warp, tmux or the VS Code integrated terminal, auto is silence. Set terminal_bell there instead and you get an audible bell that every terminal can ring.
The config command in most guides no longer exists
claude config set --global preferredNotifChannel terminal_bell is still the command in most blog posts and it now fails with error: unknown option '--global', because the config subcommand was removed. The setting is a plain top-level key in ~/.claude/settings.json, so write it there instead of through the CLI.
The hook is on Stop, which fires on every turn
Stop looks like the finished event and is not: it fires after every assistant turn, so a bell there rings constantly and gets muted within an hour, which reads later as notifications not working. The Notification event carries the moments you actually want, matched on idle_prompt, agent_needs_input and agent_completed.
Which channel your terminal can actually serve
preferredNotifChannel is a top-level key in ~/.claude/settings.json. The accepted values are auto, terminal_bell, iterm2, iterm2_with_bell, kitty, ghostty and notifications_disabled.
| Terminal | Set preferredNotifChannel to | What you get |
|---|---|---|
| Ghostty | auto or ghostty | Desktop notification |
| iTerm2 | auto, iterm2 or iterm2_with_bell | Desktop notification |
| Kitty | auto or kitty | Desktop notification |
| Terminal.app | terminal_bell | Audible bell only |
| Warp | terminal_bell | Audible bell only |
| VS Code terminal | terminal_bell | Audible bell only |
| tmux or ssh | terminal_bell | Audible bell only |
Every one of these fixes stops at your desk
Once the banner works, it works in front of the screen you walked away from. Every preferredNotifChannel value is local to the machine Claude Code is running on, so the moment you leave the desk the notification system is doing nothing for you again. That is the design, not a fault.
Pushary takes the same three moments, idle_prompt, agent_needs_input and agent_completed, and sends them to your phone. When Claude is blocked on a question it holds the call until you answer from the lock screen, and writes every question and answer to a log you can replay.
Questions people ask when the bell stays quiet
Why does it say notifications are turned off for Claude?
That message comes from macOS, and it refers to the application that tried to raise the alert rather than to Claude Code itself. Claude Code is a CLI with no app bundle, so the alert is raised by your terminal emulator; on Claude Desktop it is raised by the Claude app. Open System Settings, then Notifications, find that app in the list, and turn Allow notifications on. If the app is missing from the list entirely it has never attempted a notification, which usually means preferredNotifChannel is set to a channel it cannot serve.
How do I enable Claude Code notifications?
Two things have to be true. The app raising the alert needs OS permission, set in System Settings then Notifications. And preferredNotifChannel has to name a channel your terminal can actually serve: auto for iTerm2, Ghostty and Kitty, terminal_bell for everything else. Write it as a top-level key in ~/.claude/settings.json. Or run npx @pushary/agent-hooks bell, which sidesteps the second half by raising the alert itself rather than relying on the channel. The OS permission is still yours to grant either way.
Why do I get no notification in Ghostty or iTerm2?
Ghostty, iTerm2 and Kitty are the three terminals where the default auto channel raises a real desktop notification, so if one of them is silent the cause is almost always OS permission rather than configuration. Check System Settings, then Notifications, for that terminal. The other common cause is a Focus mode or Do Not Disturb schedule that suppresses the banner without changing any Claude setting.
How do I get a terminal bell when Claude Code finishes?
Set preferredNotifChannel to terminal_bell in ~/.claude/settings.json. This is the channel to use in Terminal.app, Warp, tmux, over ssh and in the VS Code integrated terminal, because none of them can raise a desktop notification through the auto channel. The command most guides still show, claude config set --global preferredNotifChannel terminal_bell, fails with error: unknown option '--global' because the config subcommand was removed.
Why does my notification hook fire on every reply?
Because it is registered on Stop. Stop fires after every assistant turn rather than once when the work is done, so a bell there rings on each reply. Move the hook to the Notification event and match on idle_prompt, agent_needs_input and agent_completed. Those are the moments where Claude Code has judged you stepped away or it needs an answer.
Do Claude Code desktop notifications reach my phone?
No. Every preferredNotifChannel value is local to the machine Claude Code is running on, so a banner or a bell only exists in front of the screen you already left. That is the limit of the built-in notification system rather than a bug in it. Pushary sends the same moments to your phone and, when Claude is blocked on a question, holds the call until you answer it from the lock screen.
Fix the bell, then stop needing to be at the desk.
The local bell is free and always will be. When you want the same moments on your phone, with one-tap approvals and a log, that is Pushary.