Free tool

Claude Code notification hook generator

Choose when Claude Code should alert you and how, and this page writes the hooks block for your settings.json. It uses the Stop and Notification hook events with osascript and afplay, so it is for Claude Code running on a Mac. It runs in your browser and sends nothing anywhere.

Alert me when
How

Your settings.json snippet

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Finished responding\" with title \"Claude Code\"'"
          },
          {
            "type": "command",
            "command": "afplay /System/Library/Sounds/Glass.aiff"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Waiting for your permission\" with title \"Claude Code\"'"
          },
          {
            "type": "command",
            "command": "afplay /System/Library/Sounds/Glass.aiff"
          }
        ]
      }
    ]
  }
}
The options and the copy button need JavaScript. Without it, the snippet above is the default: a banner and the Glass sound when Claude finishes and when it waits for permission.

For what each event means, the built-in alternatives and the Codex equivalent, read the guide: Get notified when Claude Code finishes or needs input.

Where to paste it

Hooks live in a Claude Code settings file, which is one JSON object. Pick the file by how far the hook should reach:

  • ~/.claude/settings.json applies to every project on your Mac. This is the right place for a personal notification.
  • .claude/settings.local.json in a project applies to you in that project only.
  • .claude/settings.json in a project is the file a team commits, so everyone who clones the repository gets it. These commands only exist on macOS, so avoid it unless the whole team is on Macs.

If the file does not exist yet, create it and paste the snippet as its entire content. If it exists and has no hooks key, add the key inside the outer braces, with a comma between it and the setting before it:

{
  "preferredNotifChannel": "terminal_bell",
  "hooks": {
    "Stop": [ … ],
    "Notification": [ … ]
  }
}

If you already have a hooks block

Do not paste a second hooks key. The file still parses, but only the last hooks key is used, so the hooks in the other one stop running without any error. Merge instead:

  1. If your existing hooks object has no Stop or Notification entry, copy those entries from the snippet into it.
  2. If it already has one, copy only the group from the snippet, the object that contains "hooks": [ … ], and add it to the end of that event's list after a comma.

A Stop list with an existing group and the generated sound group looks like this. Every group that matches runs, in parallel:

"Stop": [
  {
    "hooks": [
      { "type": "command", "command": "/path/to/your-existing-hook.sh" }
    ]
  },
  {
    "hooks": [
      { "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" }
    ]
  }
]

Trailing commas and comments are not allowed. python3 -m json.tool ~/.claude/settings.json prints the file if it parses and the position of the mistake if it does not.

Test it

  1. Run osascript -e 'display notification "Finished responding" with title "Claude Code"' and afplay /System/Library/Sounds/Glass.aiff in a terminal. Both should work before Claude Code is involved.
  2. In Claude Code, type /hooks and check that the hooks are listed under Stop and Notification. Changes to the file are normally picked up without restarting.
  3. Send a short prompt. The Stop hook runs when the reply ends.
  4. For the permission hook, ask Claude to run something that needs approval and leave the keyboard alone for more than six seconds. Each keystroke restarts the wait.

If nothing happens

  • No banner, even from the terminal: osascript banners are delivered as Script Editor. Open System Settings > Notifications, find Script Editor and turn on Allow Notifications. If it is not in the list, run the osascript command once and look again.
  • A Focus is on: Focus and Do Not Disturb silence banners. The sound still plays, because afplay is plain audio playback.
  • No sound: check the Mac's output volume and mute.
  • /hooks shows nothing: the JSON is invalid, or it is in the wrong file.
  • The permission hook seems dead: it only runs after about six seconds without a keystroke, and Claude Code ignores the exit code and error output of Notification hooks, so a broken command fails silently.

The guide has the full list, with fixes: why a hook runs and you still get nothing.

What the banner text can contain

One line of up to 120 characters, including quotes, apostrophes and non-English text. The text ends up inside an AppleScript string, inside a shell command, inside JSON, and the generator escapes it for each layer in turn: a backslash before every backslash and double quote for AppleScript, '\'' for every apostrophe in the shell's single quotes, and standard JSON string escaping on top. Line breaks and other control characters are replaced with spaces, because a line break would end the AppleScript statement. The sound comes from a fixed list of files in /System/Library/Sounds.

That is why a text such as It's "done" appears in the snippet as \"It'\\''s \\\"done\\\"\". If you edit a command by hand afterwards, keep those escapes, or change the text here and copy the snippet again.

Questions

Will the Stop hook go off after every reply?

Yes. Stop runs every time Claude finishes responding, including short replies you are watching. If that is too much, untick it and tick the one-minute option instead: idle_prompt only runs when Claude finished about 60 seconds ago and you have not typed since.

Does the generated snippet work on Linux or Windows?

No. osascript and afplay are macOS commands. The hook events and the JSON shape are the same everywhere, and the Claude Code hooks guide has the equivalent notify-send and PowerShell commands.

Can I use different sounds for finished and waiting?

Yes. Generate the snippet, then change the file name in one of the afplay commands by hand, for example Glass.aiff under Stop and Ping.aiff under Notification. Any file in /System/Library/Sounds works.

Does this page send what I type anywhere?

No. The generator is a small script that runs in your browser. It makes no network requests and stores nothing, and the snippet is built on the page you are looking at.

Sources

Details about other products come from their own pages, checked on the dates shown. If something has changed, tell us at support@armadai.sh and we will correct it.

  • Claude Code hooks are configured under a hooks key as event, matcher group and handler; Stop takes no matcher; Notification matches on permission_prompt, idle_prompt and elicitation_dialog; command hooks run with sh -c on macOS. code.claude.com, checked October 4, 2026.
  • Claude Code's hooks guide uses osascript display notification for macOS and explains the Script Editor notification permission. code.claude.com, checked October 4, 2026.
  • Claude Code's terminal configuration page uses afplay with a file from /System/Library/Sounds in a Notification hook. code.claude.com, checked October 4, 2026.