How to get a notification when Claude Code finishes

Get a sound, a terminal bell or a desktop notification when Claude Code finishes a task or waits for your permission. Which terminals notify on their own, the preferredNotifChannel setting, Notification and Stop hooks for macOS, Linux and Windows, and why a notification never shows up.

Short answer: In iTerm2, Ghostty and Kitty, Claude Code already sends a desktop notification when it finishes or needs you. In any other terminal, add "preferredNotifChannel": "terminal_bell" to ~/.claude/settings.json for a bell, or add a Notification hook that runs your system's notification command (osascript on macOS, notify-send on Linux). For a ping the moment every reply ends, use a Stop hook instead.

You give Claude Code a long task, switch to your browser, and come back ten minutes later to find it has been waiting for a "yes" since minute two. The fix takes one setting or one hook. Checked against Claude Code 2.1.308.

Which terminals notify you without any setup?

With the default setting ("auto"), Claude Code sends a desktop notification in iTerm2, Ghostty and Kitty. In macOS Terminal it rings the bell only when you have turned Terminal's audible bell off, and in every other terminal, such as Warp or the VS Code terminal, it does nothing. It also only notifies when you seem to be away from the terminal.

How do you turn on the terminal bell?

One line in ~/.claude/settings.json:

{
  "preferredNotifChannel": "terminal_bell"
}

It also appears in /config as Local notifications. The values:

ValueWhat happens
"auto" (default)A desktop notification in iTerm2, Ghostty and Kitty; elsewhere see above
"terminal_bell"Rings the bell in any terminal
"iterm2", "kitty", "ghostty"That terminal's desktop notification
"iterm2_with_bell"iTerm2's notification and the bell
"notifications_disabled"Nothing

How do you get a desktop notification in any terminal?

With a Notification hook: a command Claude Code runs whenever it would notify you. Hooks run alongside the built-in notification, not instead of it. Add the block for your system to ~/.claude/settings.json; if the file already has a hooks object, add Notification inside it.

macOS:

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          { "type": "command", "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'" }
        ]
      }
    ]
  }
}

Linux:

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          { "type": "command", "command": "notify-send 'Claude Code' 'Claude Code needs your attention'" }
        ]
      }
    ]
  }
}

Windows (a message box, which can open behind your terminal window):

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          { "type": "command", "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\"" }
        ]
      }
    ]
  }
}

For a sound instead of a picture on macOS, the command can be afplay /System/Library/Sounds/Glass.aiff.

Type /hooks in Claude Code to check that it loaded.

When does the Notification hook fire?

Without a matcher it fires for every kind of notification. To pick one, add "matcher" next to "hooks":

MatcherFires when
permission_promptClaude needs your approval, and the prompt has waited about six seconds
idle_promptClaude finished about 60 seconds ago and you have not typed since
auth_successYou finished signing in

So "done" through a Notification hook comes a minute late, on purpose: it is meant for when you have walked away.

How do you get a ping the moment Claude finishes?

Use the Stop event, which runs every time Claude finishes responding (not when you interrupt it):

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "notify-send 'Claude Code' 'Finished'" }
        ]
      }
    ]
  }
}

Swap in the macOS command for a Mac. Stop takes no matcher. It fires after short answers too, so it suits people who send long tasks and leave; for a back-and-forth chat it gets noisy, and the 60-second idle_prompt is the calmer choice.

Why does no notification appear?

SymptomCauseFix
Nothing on macOS with osascriptScript Editor has no notification permission, and macOS does not askRun osascript -e 'display notification "test"' once, then allow Script Editor in System Settings, Notifications
notify-send: command not foundThe package is missingInstall libnotify-bin (Debian, Ubuntu) or your system's equivalent
Nothing over SSH or in a containernotify-send needs a desktopUse the terminal bell, or Ghostty or Kitty, whose notifications reach your computer over SSH
Nothing inside tmuxtmux swallows themAdd set -g allow-passthrough on to ~/.tmux.conf
The hook never runsSettings file error, or the session started before the changeCheck with /hooks; restart the session

Where can you check this?

Can you set this up without editing JSON by hand?

Config Studio for Claude Code has the notification setting under its settings, with every value explained, and hooks as one of its 20 surfaces: it explains each event, writes the block into the right settings file, and nothing changes on your computer until you run its install script yourself.

The tool

Do it in your browser.

Claude Code

Config Studio for Claude Code

Every file Claude Code reads, explained: what each setting does, where it lives, which file wins, and an editor that writes it for you.

Keep reading

More from the blog.

Every post