Claude Code Notification When Waiting for Approval
Prepared and checked using AI. Sources are linked in the article.
To get a Claude Code notification when waiting for approval, configure a Notification hook matching permission_prompt. Have its handler include the event’s cwd and session_id in a desktop alert so you can identify the waiting session among parallel runs.
The setup below targets local terminal sessions on macOS and Linux. It sends an alert; return to the identified session to review the permission request.
Configure an approval-only hook
Put the configuration in ~/.claude/settings.json to apply it across your projects. Merge it into your existing settings rather than replacing unrelated keys or hooks.
For example, use ~/.claude/hooks/approval-notify.py for the handler and a worktree named checkout-api for the sample event later in this article.
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "python3 \"$HOME/.claude/hooks/approval-notify.py\""
}
]
}
]
}
}
Keep this notification setup in your user settings so you can maintain it once while running multiple Claude Code sessions.
Add a handler that identifies the session
You need Python 3 available as python3. On Linux, you also need notify-send and a desktop notification daemon; Debian and Ubuntu provide the command through libnotify-bin.
Create the handler’s directory and save the following Python code at the configured path. The handler reads the JSON input with json.load, selects the platform through sys.platform, and passes subprocess arguments as a list without invoking a shell.
import json
import subprocess
import sys
payload = json.load(sys.stdin)
if payload.get("notification_type") != "permission_prompt":
sys.exit(0)
cwd = payload["cwd"]
session = payload["session_id"]
folder = cwd.rstrip("/").rsplit("/", 1)[-1] or "/"
title = f"Claude approval: {folder} [{session[:8]}]"
body = f"{cwd}\nSession: {session}"
if sys.platform == "darwin":
command = [
"/usr/bin/osascript",
"-e", "on run argv",
"-e", "display notification (item 2 of argv) "
"with title (item 1 of argv)",
"-e", "end run",
"--", title, body,
]
elif sys.platform.startswith("linux"):
command = ["notify-send", title, body]
else:
raise SystemExit("This handler supports macOS and Linux")
subprocess.run(
command,
check=True,
timeout=5,
stdin=subprocess.DEVNULL,
)
On macOS, display notification accepts a message and title. The handler passes those strings through osascript arguments, keeping directory names out of the AppleScript source. On Linux, notify-send accepts a summary followed by a body.
This example puts the folder name and a shortened session ID in the title, then includes the full path and full ID in the body. The full values help distinguish sessions whose folders share a name or whose shortened IDs match. It deliberately leaves tool arguments and conversation text out of the alert.
Check delivery before waiting for an approval
First, check that desktop notifications reach your screen using the command for your platform.
macOS
osascript -e 'display notification "Notification check" with title "Claude Code"'
If nothing appears, open System Settings → Notifications → Script Editor and enable Allow Notifications, then run the command again.
Linux
notify-send 'Claude Code' 'Notification check'
If the command is missing, install your distribution’s package. A headless server, SSH session, or container generally lacks the desktop notification daemon this command needs.
Exercise the handler with sample input
Run this from your local desktop terminal:
python3 "$HOME/.claude/hooks/approval-notify.py" <<'JSON'
{
"hook_event_name": "Notification",
"notification_type": "permission_prompt",
"cwd": "/tmp/checkout-api",
"session_id": "example-session-id"
}
JSON
The intended alert title is Claude approval: checkout-api [example-], with the full sample path and ID in its body. This check exercises the handler and desktop delivery, independently of Claude Code’s event timing.
Inside Claude Code, open /hooks and confirm the entry appears under Notification. Then leave a real permission request unanswered and switch away from the terminal.
In terminal sessions, permission_prompt normally fires after about six seconds without typing; each keystroke defers it. idle_prompt is a separate event for an idle session after a response, so it is not a substitute for approval alerts. For an immediate tool-permission signal, PermissionRequest is the alternative event, but its matcher filters tool names rather than notification types.
If the standalone check succeeds but the real alert does not, check the matcher, script path, and whether python3 is available to the hook process. The synthetic check alone does not establish that Claude Code loaded the configuration.
Where Parallel Code fits
Parallel Code is our free, open-source desktop app for macOS and Linux. It runs several AI coding agents in parallel, each isolated in its own git worktree, and provides a diff-first review surface. For that workflow, including the worktree path and session ID in your hook’s alert helps identify which session needs attention.