# 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.

Source: https://docs.alphorn.dev/ai-agents/claude-code/

Claude Code runs shell commands on lifecycle events through [hooks](https://code.claude.com/docs/en/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:

```bash title="~/.claude/hooks/alphorn-notify.sh"
#!/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 0
```

```bash
chmod +x ~/.claude/hooks/alphorn-notify.sh
```

The 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):

```json title="~/.claude/settings.json"
{
  "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](/ai-agents) for the shared conventions, an `AGENTS.md` snippet, and an Agent Skill that lets Claude send notifications on its own.
