Claude Code notifications on Windows with PowerShell
The short answer
Save a short PowerShell script as notify.ps1, then point a Claude Code Stop hook
and a Notification hook at it through powershell.exe. It shows a Windows toast
with the toast API built into Windows, so there is no module to install. We ran the script and read its
toast back on one PC. We have not yet run it through a real Claude Code hook, and the page says which
steps that covers.
Or let a light show it
A toast needs you looking at the screen. A light works from across the room, and adds no extra ping.
What it does
AC Bridge 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. It runs on Windows, macOS and Linux. Its Claude Code hooks come with its plugin, so there is nothing to paste into your settings files. In the example colours from our homepage, the lamp flashes red when the agent needs permission and turns green when it has finished; you pick the colours.
What it follows
On Claude Code, AC Bridge takes needs permission from the Notification hook's
permission_prompt matcher; finished follows Stop. See
the hook events AC Bridge uses for the full list.
The Claude desktop app has no plugins in its sessions inside the Windows Subsystem for Linux, so those sessions drive no light, and neither do cloud sessions.
What it costs
One plan: a 7-day free trial with no card needed, then £2.99 a month if you add a card to keep going; without one the trial just ends and nothing is charged. Cancel any time from your account. There is no API key to buy.
What it collects
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 7-day free trial, or read the setup docs and the status light guide first.
What ran on Windows 11, and what has not
The first card is what we saw work. The second is what we did not run, so treat those steps as written from the docs until you have tried them yourself.
Ran on 31 October 2026
- Windows 11 Home, build 10.0.26300, with Windows PowerShell 5.1 and no extra module.
- The script, with JSON piped in from a bash shell: exit 0, and the toast read back from the Windows notification history.
- The check script, and the listing of app IDs.
- A message holding odd characters, a message with no text, and empty input.
- The execution policy error, and the switch that clears it.
Not run yet
- A real Claude Code hook starting the script, on Windows or in a Linux distribution. The hook entries here come from the hooks reference.
- Git Bash, a PowerShell prompt, and
"shell": "powershell"on a hook. - PowerShell 7, which that PC does not have.
- The script saved under
.claude\hooks. It ran from a temporary folder. - The terminal bell in Windows Terminal.
- Do Not Disturb, and a full-screen app.
Claude Code toast notification on Windows with PowerShell
Claude Code's own hooks guide uses a message box on Windows, and warns that it can open behind your terminal. A toast appears in the corner of the screen instead.
Save it as notify.ps1
notify.ps1
# Show a Windows toast for a Claude Code hook. Needs no module.
[Console]::InputEncoding = [Text.Encoding]::UTF8
$hook = [Console]::In.ReadToEnd() | ConvertFrom-Json
$body = if ($hook.message) { $hook.message } else { 'Finished' }
$manager = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
$xml = $manager::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
$lines = $xml.GetElementsByTagName('text')
[void]$lines.Item(0).AppendChild($xml.CreateTextNode('Claude Code'))
[void]$lines.Item(1).AppendChild($xml.CreateTextNode($body))
$appId = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'
$manager::CreateToastNotifier($appId).Show([Windows.UI.Notifications.ToastNotification]::new($xml))
What each part does
- The input. Claude Code passes a hook its event as JSON on standard input. The script reads that as UTF-8, so accented letters survive.
- The text. A
Notificationevent carries amessagefield. AStopevent does not, so the toast says Finished. - The XML.
CreateTextNodehandles the escaping, so a message holding&or<cannot break the toast. - The app ID. A toast needs an app to belong to. This one borrows Windows PowerShell's Start menu entry, so nothing has to be registered.
Try it before you add a hook
Pipe a sample event into the script. This is the line we ran, from a bash shell, with the folder changed to yours. You should get a toast titled Claude Code with that message under it.
bash
printf '{"message":"Claude needs your permission to use Bash"}' | powershell.exe -NoProfile -ExecutionPolicy Bypass -File 'C:\Users\you\.claude\hooks\notify.ps1'
Set up a Claude Code notification hook on Windows
Not run yet: this entry follows the example in the hooks reference. We have not watched it fire.
~/.claude/settings.json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell.exe",
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Users\\you\\.claude\\hooks\\notify.ps1"],
"async": true
}
]
}
],
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "powershell.exe",
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Users\\you\\.claude\\hooks\\notify.ps1"],
"async": true
}
]
}
]
}
}
Why it is written this way
- With
argsset, the docs say Claude Code starts the program directly, with no shell. That avoids any question of whether Git Bash or PowerShell reads the line, and on Windowscommandmust then be a real program. - In JSON every backslash in the path is doubled.
"async": trueruns the hook in the background, so the toast does not hold Claude Code up.- The script prints nothing, because a hook's output can change what Claude Code does.
Which events to pick, and how they behave, is in the Claude Code notifications guide.
Claude Code PowerShell script blocked: running scripts is disabled
The error
PowerShell error
File C:\Users\you\.claude\hooks\notify.ps1 cannot be loaded because running scripts is disabled on this system.
Restricted is the default on Windows client PCs.
Our PC was set to RemoteSigned, so we brought the error up by passing
-ExecutionPolicy Restricted. It exited with code 1 and no toast.
The fix
Pass -ExecutionPolicy Bypass, as the test line and the hook entries do. It lasts
for that one PowerShell session, so your machine's policy does not change. Microsoft calls the
execution policy defence in depth rather than a security boundary, and the Claude Code hooks
reference uses the same switch.
-NoProfile sits beside it for a different reason: it skips your PowerShell profile so
the hook starts fast.
Windows toast not showing from Claude Code
This script asks Windows whether the app may show toasts, then prints the toasts it holds for that app.
Save it as check.ps1
check.ps1
$manager = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
$appId = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'
"setting=" + $manager::CreateToastNotifier($appId).Setting
$h = $manager::History.GetHistory($appId)
"history=" + $h.Count
foreach ($t in $h) { ($t.Content.GetElementsByTagName('text') | ForEach-Object { $_.InnerText }) -join ' | ' }
What it printed
setting=Enabled
history=1
Claude Code | Claude needs your permission to use Bash
Read the setting
We only saw Enabled. The others are from
Microsoft's list:
DisabledForApplication: you turned toasts off for this app.DisabledForUser: all notifications are off for your account on this PC.DisabledByGroupPolicy: an administrator turned them off for the PC.DisabledByManifest: the app has not declared that it can show toasts.
Wrong app ID?
If the app ID itself is wrong on your PC, this lists the ones Windows knows:
PowerShell
Get-StartApps | Where-Object { $_.Name -match "PowerShell|Terminal" }
Claude Code notifications in the Windows Subsystem for Linux
Every command above ran from a bash shell in a Linux distribution, so the piped test holds here too. The hook entry is the part that differs, and it is not run yet.
What changes
Windows programs reach the Linux side through interop, which is how powershell.exe runs
at all, and the Claude Code docs ask for it on your PATH. The script stays on the
Windows side and you give its Windows path, because that is what we ran.
~/.claude/settings.json in Linux
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File 'C:\\Users\\you\\.claude\\hooks\\notify.ps1'",
"async": true
}
]
}
]
}
}
Claude Code notification sound in Windows Terminal
Not run yet. The terminal bell setting
is the one-line option, and what you then get is up to your terminal.
Microsoft's docs
give Windows Terminal a bellStyle profile setting, audible by default, and
all adds a taskbar flash.