Claude Code
Get notified when Claude Code finishes, waits for input, or needs permission. Claude Code hooks (Stop, Notification) that send to Slack, Telegram, or SMS.
Claude Code runs shell commands on lifecycle events through hooks. Two events matter for notifications:
Notification- Claude needs you: a permission prompt, or it has been waiting for input.Stop- Claude finished responding. Fires after every response, so use it if you want a message for each completed turn.
1. Create the hook script
The hook receives the event as JSON on stdin. This script turns it into an Alphorn notification:
#!/usr/bin/env bash
# Forwards Claude Code Stop and Notification hook events to Alphorn.
jq -c '{
title: ("Claude Code - " + (.cwd | split("/") | last)),
message: (if .hook_event_name == "Stop"
then (.last_assistant_message // "Finished")
else (.message // "Needs your attention") end),
priority: (if .notification_type == "permission_prompt" then 4
elif .hook_event_name == "Stop" then 2 else 3 end),
tags: ["ai-agent", "claude-code",
(if .hook_event_name == "Stop" then "completed"
elif .notification_type == "permission_prompt" then "permission"
else "waiting" end)]
}' | curl -fsS --max-time 5 -X POST "$ALPHORN_WEBHOOK_URL" \
-H "Authorization: Bearer $ALPHORN_API_KEY" \
-H "Content-Type: application/json" \
-d @- > /dev/null
exit 0chmod +x ~/.claude/hooks/alphorn-notify.shThe script needs jq and curl, and reads ALPHORN_WEBHOOK_URL and ALPHORN_API_KEY from the environment (set below). It always exits 0: on a Stop hook, exit code 2 tells Claude to keep working instead of stopping.
2. Register the hooks
Add the hooks to ~/.claude/settings.json (all projects), .claude/settings.json (one project, committed), or .claude/settings.local.json (one project, not committed):
{
"env": {
"ALPHORN_WEBHOOK_URL": "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT",
"ALPHORN_API_KEY": "alp_..."
},
"hooks": {
"Notification": [
{
"matcher": "permission_prompt|idle_prompt",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/alphorn-notify.sh" }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/alphorn-notify.sh", "async": true }
]
}
]
}
}Leave out the Stop block if you only want to hear from Claude when it is blocked. "async": true runs the hook in the background so Claude does not wait for the HTTP request. Instead of the env block, you can export ALPHORN_WEBHOOK_URL and ALPHORN_API_KEY in your shell profile. Keep the key out of .claude/settings.json if you commit that file.
Notification matchers
The matcher on Notification is matched against the notification type:
| Matcher | When it fires |
|---|---|
permission_prompt | Claude needs permission to use a tool |
idle_prompt | Claude is waiting for your input |
elicitation_dialog | An MCP server is asking for input |
auth_success | Authentication completed |
| (empty string) | All notification types |
Stop does not support matchers; a matcher there is ignored.
Event fields
Fields from the hook input you can use in the jq program:
| Event | Fields |
|---|---|
| All | session_id, cwd, hook_event_name, transcript_path |
Notification | message, title, notification_type |
Stop | last_assistant_message (Claude's final response text) |
Routing
Route by the tags the script sets, for example:
| Channel | Filter |
|---|---|
| Telegram or SMS | Tags has any of permission |
| Slack | Tags has any of claude-code |
See AI Agent Notifications for the shared conventions, an AGENTS.md snippet, and an Agent Skill that lets Claude send notifications on its own.
MCP Server
Alphorn MCP server: give Claude Code, Claude Desktop, Cursor, Codex, and other MCP clients a send_notification tool that reaches Slack, Telegram, email, or SMS.
OpenAI Codex CLI
Get notified when OpenAI Codex CLI finishes a turn or needs approval. Codex hooks (Stop, PermissionRequest) and the notify setting, sending to Slack or SMS.