How to get a notification when Claude Code or Codex finishes or needs your input
The short answer
Add "preferredNotifChannel": "terminal_bell" to ~/.claude/settings.json and
Claude Code rings the terminal bell when it finishes or waits on a permission prompt while you
appear to be away. Desktop notifications are on by default only in Ghostty, Kitty and iTerm2. For
a sound, a desktop toast or a phone push in any terminal, add a Stop hook (every finished turn)
and a Notification hook (permission prompts and idle reminders). Or let a light show it.
Which signal fires when
Most "my notification never came" reports are timing. Claude Code has two kinds of signal: hooks that fire at the moment something happens, and notifications that wait to see whether you are still at the keyboard.
| Signal | Fires when | Delay | Only if you seem away? |
|---|---|---|---|
Stop hook |
Claude finished responding, at the end of every turn. Not when you interrupt it; an API error fires StopFailure instead. |
None | No |
PermissionRequest hook |
Claude is about to show a permission prompt. | None | No |
Notification, matcher permission_prompt |
A permission prompt has waited without you typing. Each keystroke pushes it back. | About 6 s | Yes, in a terminal |
Notification, matcher idle_prompt |
Claude finished responding a while ago and you have not typed since. | About 60 s | Yes |
| Built-in bell or desktop notification | The same moments as the two Notification matchers. |
6 s or 60 s | Yes |
The gotcha
Both Notification types, and the built-in bell and desktop notification that share
their timing, only fire in a terminal when you appear to be away: stop typing and they come, keep
typing and they wait. permission_prompt waits about six seconds.
idle_prompt is not a "waiting for input" signal: it fires about 60 seconds after
Claude finished responding, and only if you have not typed since. For a signal the moment it
happens, use Stop for "done" and PermissionRequest for "needs
permission".
In Claude Desktop and the VS Code extension, permission_prompt comes about six
seconds after Claude asks whether or not you type. The full table, with the rarer notification
types, is in the
hooks reference.
1. The terminal bell: one line of settings
Claude Code's own alert is the preferredNotifChannel setting, shown as
Local notifications in /config. Put this in
~/.claude/settings.json to ring the bell in any terminal:
{
"preferredNotifChannel": "terminal_bell"
}
The other values are auto (the default), iterm2,
iterm2_with_bell, kitty, ghostty and
notifications_disabled. The bell rings at the moments in the table above, so only
when you appear to be away. Turning notifications off does not stop your hooks: they run either
way. In Apple's Terminal, Claude Code's first-run terminal setup turns the audible bell off; if you
hear nothing, switch Audible bell back on in the Terminal profile's Advanced settings.
Details:
terminal configuration.
2. Native desktop notifications
With the default auto setting, Claude Code sends a real desktop notification in
Ghostty, Kitty and iTerm2, and nothing in most other terminals: Windows Terminal, Warp and the VS
Code terminal need the bell or a hook.
- iTerm2 needs one switch: Settings, Profiles, Terminal, tick Notification Center Alerts, then under Filter Alerts enable Send escape sequence-generated alerts.
- Over SSH the notification still reaches your own computer; Ghostty and Kitty hand it to the OS with no setup.
-
Inside tmux, add
set -g allow-passthrough onto~/.tmux.conf, or tmux swallows it. - Still nothing? Check that your terminal app is allowed to send notifications in the OS settings.
- The Claude desktop app (the Code tab) sends an OS notification when a session finishes and you are not looking at it. Nothing to set up.
3. A Notification hook: permission prompts and idle reminders
A hook runs your own command, so it works in every terminal and can play any sound or show any notification. Hooks run alongside the built-in alert, not instead of it. This one shows a Linux desktop notification for each of the two useful matchers; swap in the macOS or Windows command from the next section:
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Needs your permission'",
"async": true
}
]
},
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Waiting for you'",
"async": true
}
]
}
]
}
}
"async": true runs the command in the background, so a slow notifier never holds
Claude up. Leave the matcher out to fire on every notification type; the hook gets
notification_type and message as JSON on stdin if you would rather
branch in one script. For a sound on macOS, the command can be
afplay /System/Library/Sounds/Glass.aiff. Type /hooks in Claude Code to
check that the hook is registered; edits to the settings file are normally picked up without a
restart. More examples:
hooks guide.
4. A Stop hook: a notification every time Claude finishes
Stop fires at the end of every turn, as soon as Claude finishes responding, whether or
not you are at the keyboard. Pick the command for your system.
macOS
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code finished\" with title \"Claude Code\"'",
"async": true
}
]
}
]
}
}
osascript posts through Script Editor. If nothing appears, run
osascript -e 'display notification "test"' once in Terminal, then allow notifications
for Script Editor in System Settings, Notifications.
Linux
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Finished'",
"async": true
}
]
}
]
}
}
notify-send needs a desktop notification daemon, so it does nothing on a headless
server or over plain SSH. On Debian and Ubuntu it comes from the libnotify-bin
package.
Windows
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell.exe -NoProfile -Command \"New-BurntToastNotification -Text 'Claude Code', 'Finished'\"",
"async": true
}
]
}
]
}
}
BurntToast is a PowerShell module
that shows a real toast in the corner of the screen. Install it once, in PowerShell:
Install-Module -Name BurntToast -Scope CurrentUser. The official docs use a
MessageBox instead, which opens a dialog that can land behind your terminal.
Stop fires on every turn, one-line answers included. If that is too chatty, use the
idle_prompt matcher from section 3, which waits until you have been away for about a
minute.
5. Instant permission alerts with PermissionRequest
PermissionRequest fires the moment Claude is about to show a permission prompt, with
no six-second wait and whether or not you are typing. It can also answer the prompt: a
PermissionRequest hook that prints a decision approves or denies the request
for you. So this one runs in the background and prints nothing:
{
"hooks": {
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Asking for permission'",
"async": true
}
]
}
]
}
}
With "async": true the hook cannot decide anything, so the normal prompt still
appears. One gap: PermissionRequest does not run for a sandboxed command's network
request, which only permission_prompt covers.
6. A push to your phone with ntfy
ntfy is a free push service with Android and iOS apps. Subscribe to a topic in the app, then publish to it from a hook. There is no sign-up, so the topic name works like a password: pick one nobody could guess.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "curl -s -o /dev/null -d \"Claude Code finished\" ntfy.sh/your-hard-to-guess-topic",
"async": true
}
]
}
]
}
}
-o /dev/null keeps curl quiet: ntfy answers with JSON, and JSON that a hook prints is
read as instructions. Put the same command in a Notification hook for permission
prompts. It needs a POSIX shell, which means macOS, Linux, or Windows with Git Bash installed.
Titles, priorities and self-hosting:
ntfy publishing docs.
Windows, and Claude Code in a Linux shell on Windows
On Windows, Claude Code runs hook commands in Git Bash, or in PowerShell when Git Bash is not
installed; set "shell": "powershell" on a hook to choose. The BurntToast command in
section 4 works from either, because it starts powershell.exe itself. A hook can also
return a terminalSequence (OSC 9 is the one the hooks reference lists for Windows
Terminal) and Claude Code writes it to the terminal for you:
emit terminal notifications.
If you run Claude Code inside a Linux distribution under the Windows Subsystem for Linux,
notify-send usually has no notification daemon to talk to. Call Windows instead:
Windows programs are on the Linux PATH through interop, so the same
powershell.exe command from section 4 works unchanged from the Linux side, and the
toast appears on your Windows desktop. Install BurntToast in Windows PowerShell, not inside Linux.
powershell.exe can take a moment to start, which is one more reason for
"async": true.
Codex CLI
Codex has three ways to tell you: its own terminal notifications, notify, and hooks.
Built-in notifications
On by default: tui.notifications is true, and Codex notifies you when the terminal is
not focused (tui.notification_condition, unfocused by default;
always tells you regardless). tui.notification_method picks how:
auto prefers an OSC 9 desktop notification where the terminal supports it and falls
back to the bell; osc9 or bel forces one. In
~/.codex/config.toml:
[tui]
notifications = ["agent-turn-complete", "approval-requested"]
notification_method = "bel"
notification_condition = "always"
notify: run a program when a turn completes
notify runs any program when Codex finishes a turn. It fires on
agent-turn-complete only, and the program gets one JSON argument carrying
type, thread-id, turn-id, cwd,
input-messages and last-assistant-message. It belongs in your own
~/.codex/config.toml (Codex ignores it in a project's .codex/config.toml),
above the first [table] line, or TOML files it under that table:
notify = ["notify-send", "Codex"]
That is the line from Codex's sample config; notify-send shows the JSON as the
message. For a tidy message, point notify at a small script that reads
last-assistant-message, as in the
advanced configuration guide.
Codex hooks: Stop and PermissionRequest
Codex has hooks too, in
~/.codex/hooks.json (or inline [hooks] tables in
config.toml), and they are on by default. PermissionRequest fires when
Codex is about to ask for approval, so a Codex permission alert arrives as soon as Codex asks.
Keep these quiet too: Stop accepts only JSON or nothing on stdout, and a
PermissionRequest hook that prints a decision approves or denies the request.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "notify-send 'Codex' 'Finished'",
"async": true
}
]
}
],
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "notify-send 'Codex' 'Needs your approval'",
"async": true
}
]
}
]
}
}
Codex will not run a new or changed hook until you trust it: start Codex, type
/hooks, review the hook and trust it. Change the command later and it needs trusting
again.
| You want | Claude Code | Codex CLI |
|---|---|---|
| A built-in alert | preferredNotifChannel: the bell, or a desktop notification in Ghostty, Kitty and iTerm2, when you seem away |
tui.notifications: OSC 9 or the bell, when the terminal is not focused |
| Done, right away | Stop hook |
Stop hook, or notify |
| Needs permission, right away | PermissionRequest hook |
PermissionRequest hook |
| Needs permission, once you look away | Notification permission_prompt, about 6 s |
-- |
| An idle reminder | Notification idle_prompt, about 60 s |
-- |
| Where hooks go | ~/.claude/settings.json |
~/.codex/hooks.json or config.toml |
| Before a new hook runs | Nothing; /hooks shows what is registered |
Trust it in /hooks |
Or let a light show it
A sound needs you at the desk and listening, and a toast needs you looking at the screen. A light
works from across the room, with headphones on, and adds no extra ping. You can build one yourself:
a Stop or Notification hook can call a Home Assistant webhook, and the
Claude Code status light page lists free DIY projects.
AC Bridge is the ready-made version. It turns the smart lights and plugs you already own (Hue, LIFX, Govee, WiZ, Home Assistant and more) into a status light for Claude Code and the Codex CLI, with the hooks already wired. In the example colours from our homepage, the lamp breathes blue while the agent works, flashes red when it needs permission and turns green when it has finished; you pick the colours, per project.
The timing rules above apply to a light too. On Claude Code, AC Bridge takes needs permission from
the Notification hook's permission_prompt matcher, so the red light comes about six
seconds after the prompt opens; finished follows Stop. On Codex it follows
PermissionRequest, so the light changes as soon as Codex asks.
One plan: a 3-day free trial with your card taken up front, then £2.99 a month, charged when the trial ends. Cancel any time from your account; cancel before the trial ends and you pay nothing. It works with the Claude Code or Codex you already run, on Windows, macOS or Linux, so there is no API key to buy.
LAN devices are driven straight from your machine and their keys never leave it; your account, settings, device list, usage counts and session states sync through our servers. Your prompts, code and session transcripts are never collected. Privacy policy.
Try it on your own lights Start the 3-day free trial, or read the setup docs and the status light guide first.