AC Bridge ac-bridge.com

How to get a notification when Claude Code or Codex finishes or needs your input

By Sam, solo developer in the UK · Last updated

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.

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.