Claude Code Hooks: Complete Guide to All 30 Lifecycle Events
Every Claude Code hook event, which ones exit code 2 can block, and production patterns. Now with TeammateIdle, TaskCompleted, PostToolBatch.
Agentic Orchestration Kit for Claude Code.
Problem: You're deep in flow state, building a feature. Claude needs to write a file. Click approve. Run a command. Click approve. Format code. Click approve. Twenty interruptions later, you've forgotten what you were building.
Quick Win: Add this to .claude/settings.json and never approve a Prettier format again:
Every file Claude writes now auto-formats. Zero clicks. Zero context switches.
The 30 Hook Lifecycle Events
Hooks intercept Claude Code events and execute shell commands or LLM prompts. This guide shipped when there were twelve events. There are now thirty, and the additions are not filler: TeammateIdle, TaskCreated, and TaskCompleted are the hooks that let you gate multi-agent work, and PostToolBatch is the only place you can interrupt the agentic loop between a batch of parallel tool calls and the next model call.
Here is the complete list, grouped the way the lifecycle actually runs. "Can block" means exit code 2 stops the thing that was about to happen.
Session lifecycle
| Hook | When It Fires | Can Block? | Best Use |
|---|---|---|---|
| Setup | --init-only, or --init / --maintenance in -p mode | NO | One-time CI and script prep |
| SessionStart | Session begins or resumes | NO | Load context, set env vars |
| SessionEnd | Session terminates | NO | Cleanup, logging |
| InstructionsLoaded | A CLAUDE.md or .claude/rules/*.md file loads into context | NO | Audit what actually got loaded |
Per turn
| Hook | When It Fires | Can Block? | Best Use |
|---|---|---|---|
| UserPromptSubmit | You hit enter, before Claude sees it | YES | Context injection, validation |
| UserPromptExpansion | A typed command expands into a prompt | YES | Gate or rewrite slash-command expansion |
| MessageDisplay | While assistant message text streams to the display | NO | Display-only filtering |
The agentic loop
| Hook | When It Fires | Can Block? | Best Use |
|---|---|---|---|
| PreToolUse | Before a tool call executes | YES | Security blocking, auto-approve (extends permission system) |
| PermissionRequest | A tool call needs a permission decision | YES | Auto-approve/deny |
| PermissionDenied | Auto mode's classifier denied a call | NO* | Offer the model a retry |
| PostToolUse | After a tool call succeeds | NO** | Auto-format, lint, log |
| PostToolUseFailure | After a tool call fails | NO | Error handling |
| PostToolBatch | After a batch of parallel calls, before the next model call | YES | Stop the loop mid-flight |
| SubagentStart | A subagent is spawned | NO | Subagent initialization |
| SubagentStop | A subagent finishes | YES | Subagent validation |
| TaskCreated | A task is being created via TaskCreate | YES | Reject malformed tasks (exit 2 rolls the creation back) |
| TaskCompleted | A task is being marked completed | YES | Gate completion on real evidence |
| Elicitation | An MCP server requests user input mid-call | YES | Deny unwanted MCP prompts |
| ElicitationResult | After the user answers an MCP elicitation | YES | Filter the response before it leaves |
*PermissionDenied ignores exit code and stderr because the denial already happened. Use JSON hookSpecificOutput.retry: true instead.
**PostToolUse can prompt Claude with feedback but cannot undo the tool execution.
Turn completion
| Hook | When It Fires | Can Block? | Best Use |
|---|---|---|---|
| Stop | Claude finishes responding | YES | Task enforcement |
| StopFailure | The turn ends on an API error | NO | Alerting (output and exit code are ignored) |
| TeammateIdle | An agent-teams teammate is about to idle | YES | Report gates, keep a teammate working |
Compaction
| Hook | When It Fires | Can Block? | Best Use |
|---|---|---|---|
| PreCompact | Before context compaction | YES | Transcript backup |
| PostCompact | After compaction completes | NO | Re-inject context |
Async and standalone
| Hook | When It Fires | Can Block? | Best Use |
|---|---|---|---|
| Notification | Claude Code sends a notification | NO | Desktop alerts, TTS |
| ConfigChange | A configuration file changes mid-session | YES | Block unsanctioned config edits |
| CwdChanged | The working directory changes | NO | direnv-style env management |
| FileChanged | A watched file changes on disk (matcher = name) | NO | React to external edits |
| WorktreeCreate | A worktree is being created | YES | Replace default git behavior |
| WorktreeRemove | A worktree is being removed | NO | Cleanup |
Two corrections to older guidance, this page's own included. PreCompact can block; the earlier version of this table said it could not. And PostCompact did not exist when this guide was written, so anyone re-injecting context after a compact was doing it from a SessionStart matcher with the compact matcher instead.
The three events worth your attention if you have not looked at hooks in a while are TeammateIdle, TaskCompleted, and PostToolBatch. The first two are how you enforce something on an agent rather than on yourself, and building a real gate on TeammateIdle turns out to have loop-safety rules the docs do not carry. That is its own post: Claude Code hook loops.
Exit Codes: The Control Mechanism
Every hook communicates via exit codes:
| Exit Code | What Happens |
|---|---|
| 0 | Success - hook ran, stdout processed for JSON |
| 2 | Block - operation stopped, stderr sent to Claude |
| Other | Error - stderr shown to user, execution continues |
Exit code 2 is your power tool. A PreToolUse hook that exits 2 stops the tool. A Stop hook that exits 2 forces Claude to keep working.
Hook Types: Command, HTTP, Prompt, and Agent
Claude Code supports four hook handler types. Pick the one that fits your use case:
Command hooks run shell scripts:
HTTP hooks POST to an endpoint and receive JSON back: New - Feb 2026
HTTP hooks send the event's JSON input as the POST body (Content-Type: application/json). The response uses the same JSON output format as command hooks. Key differences from command hooks:
- Non-blocking errors: Non-2xx responses, connection failures, and timeouts produce non-blocking errors (execution continues). To actually block a tool call, return a 2xx response with
decision: "block"in the JSON body. - Header env vars: Use
$VAR_NAMEor${VAR_NAME}in header values. Only variables listed inallowedEnvVarsget resolved; unlisted ones become empty strings. - Deduplication: HTTP hooks are deduplicated by URL (command hooks by command string).
- Config only: Must be added by editing settings JSON directly. The
/hooksinteractive menu only supports command hooks.
Prompt hooks use LLM evaluation (great for Stop/SubagentStop):
The LLM responds with {"ok": true} or {"ok": false, "reason": "..."}.
Agent hooks spawn a subagent with tool access (Read, Grep, Glob) for deeper verification:
Agent hooks can explore the codebase before making a decision, making them more thorough than prompt hooks but slower (default timeout: 60s vs 30s for prompts).
Async Hooks (Non-Blocking) New - Jan 2026
Add async: true to run hooks in the background without blocking Claude's execution. Released by Anthropic in January 2026:
Best for:
- Logging and analytics
- Backup creation (PreCompact)
- Notifications
- Any side-effect that shouldn't slow things down
Not suitable for:
- Security blocking (PreToolUse with exit code 2)
- Auto-approve decisions (PermissionRequest)
- Any hook where Claude needs the result
HTTP Hooks: Post to Endpoints New - Feb 2026
HTTP hooks let you send hook events to a web server instead of running a local script. This opens up use cases that were previously awkward or impossible with command hooks:
- Remote validation services that enforce team-wide policies
- Centralized logging to a shared audit system
- Webhook integrations with Slack, PagerDuty, or custom dashboards
- Microservice architectures where hook logic runs alongside your API
Basic HTTP Hook
Route all Bash commands through a validation endpoint:
Your server receives the exact same JSON that command hooks get via stdin, but as the POST request body. Return the same JSON output format in the response body.
HTTP Response Handling
HTTP hooks interpret responses differently from command hooks. There are no exit codes, only HTTP status codes and response bodies:
| Response | Behavior |
|---|---|
| 2xx + empty body | Success, equivalent to exit code 0 with no output |
| 2xx + plain text body | Success, the text is added as context to Claude |
| 2xx + JSON body | Success, parsed using the same JSON output schema as command hooks |
| Non-2xx status | Non-blocking error, execution continues |
| Connection failure / timeout | Non-blocking error, execution continues |
The critical difference: HTTP hooks cannot block via status codes alone. A 4xx or 5xx just logs an error and keeps going. To actually block a tool call or deny a permission, you must return a 2xx response with the appropriate decision fields in the JSON body:
Secure Header Authentication
The headers field supports environment variable interpolation, but only for variables explicitly listed in allowedEnvVars. This prevents accidental secret leakage:
Any $VAR reference not in allowedEnvVars resolves to an empty string. No warnings, no errors. Just empty. So double-check your allowlist.
JSON Output: Advanced Control
Beyond exit codes, hooks can return structured JSON for precise control.
PreToolUse Decisions
"allow": Bypasses permission system"deny": Blocks tool, tells Claude why"ask": Prompts user for confirmationupdatedInput: Modify tool parameters before execution
PermissionRequest Decisions
Stop/SubagentStop Enforcement
Hook 1: Auto-Format on Save
Run formatters after every file write:
Multiple hooks run in parallel. Format AND lint before Claude's response appears.
Hook 2: Session Context Injection
Load context when sessions start:
Persist Environment Variables
SessionStart and Setup hooks can set environment variables for the session:
Hook 3: Security Blocking
Block dangerous operations with PreToolUse:
Hook 4: Auto-Approve Safe Commands
Use PermissionRequest to auto-approve without prompts:
Hook 5: Transcript Backup
Save transcripts before compaction with PreCompact. Use async: true since backups don't need to block Claude:
Use matchers manual or auto to distinguish between /compact and auto-compact.
Hook 6: Task Completion Enforcement
Use Stop hooks to ensure work is complete:
See the Stop Hook guide for command-based patterns.
Hook 7: Skill Activation
The Skill Activation Hook intercepts prompts and appends skill recommendations. It matches keywords in your input against a rules file and injects the right skill context before Claude starts working -- so Claude loads database optimization rules for a Postgres query without you asking. The Code Kit's SkillActivationHook implements this pattern against 21 skill categories:
Hook 8: Threshold-Based Backups via StatusLine
StatusLine is the only mechanism that receives live context metrics. Use it to trigger backups at thresholds:
The $CLAUDE_PROJECT_DIR prefix points the hook at your project root from any working directory; a bare relative path throws MODULE_NOT_FOUND once a session's working directory moves into a subdirectory.
Critical: The remaining_percentage field includes a fixed 33K-token autocompact buffer. To get actual "free until autocompact":
The backup system uses two trigger systems simultaneously. The token-based system is the primary trigger, with percentage thresholds as a safety net:
Unlike PreCompact (which triggers on compaction), StatusLine-based backups capture state proactively. The token-based system ensures early backups on large context windows (1M) where percentage thresholds would fire too late.
Architecture: Three-File Structure
The backup system uses a clean separation of concerns:
| File | Trigger | Responsibility |
|---|---|---|
backup-core.mjs | Called by others | Parse transcript, format markdown, save file, update state |
statusline-monitor.mjs | StatusLine (continuous) | Monitor tokens/context %, detect triggers, display status |
conv-backup.mjs | PreCompact hook | Handle pre-compaction event |
This architecture means backup logic lives in one place. Changes to formatting, file naming, or state management only need to be made in backup-core.mjs.
Backup File Naming
Backups use numbered filenames with timestamps for history:
StatusLine Display
When a backup exists for the current session, the statusline shows the backup path:
This tells you exactly which file to load after compaction.
State Tracking
Both StatusLine and PreCompact hooks update a shared state file at ~/.claude/claudefast-statusline-state.json:
Recommended workflow: When compaction occurs, run /clear to start a fresh session, then load the backup file shown in the statusline. This avoids confusion from having both compaction summary and injected context.
Hook 9: Setup Hooks for Installation and Maintenance
Setup hooks run before your session starts, triggered by special CLI flags:
Configure them with matchers in settings.json:
The power move: Pass a prompt after the flag:
The hook runs first (deterministic), then the /install command executes (agentic). This combines predictable script execution with intelligent agent oversight.
See the complete Setup Hooks Guide for the full pattern including interactive onboarding and justfile integration.
Configuration Locations
| Location | Scope | Priority |
|---|---|---|
| Managed policy | Enterprise | Highest |
.claude/settings.json | Project (shared) | High |
.claude/settings.local.json | Project (personal) | Medium |
~/.claude/settings.json | All projects | Lowest |
Disabling and Restricting Hooks
Disable All Hooks
If hooks are causing issues or you want a clean baseline, set disableAllHooks to true in your settings:
This turns off all hooks across every scope (user, project, and local settings). Useful for debugging or when a hook causes unexpected behavior.
Managed Hook Restrictions
For organizations that need centralized control, administrators can set allowManagedHooksOnly to true in managed settings:
When enabled, only hooks defined in the managed settings file and SDK hooks are allowed. User-level, project-level, and plugin hooks are all blocked. This prevents developers from adding hooks that could bypass organizational security policies.
This pairs well with allowManagedPermissionRulesOnly, which applies the same restriction to permission rules. Together, these settings give administrators full control over both the permission system and its hook-based extensions.
Matcher Syntax
| Pattern | Matches |
|---|---|
"" or omitted | All tools |
"Bash" | Only Bash (exact, case-sensitive) |
"Write|Edit" | Write OR Edit (regex) |
"mcp__memory__.*" | All memory MCP tools |
Critical: No spaces around |. Matchers are case-sensitive.
Event-Specific Matchers
SessionStart: startup, resume, clear, compact
PreCompact: manual, auto
Setup: init, maintenance
Notification: permission_prompt, idle_prompt, auth_success
FileChanged: the matcher field holds the filenames to watch, not a tool name
Environment Variables
| Variable | Description |
|---|---|
CLAUDE_PROJECT_DIR | Project root (all hooks) |
CLAUDE_ENV_FILE | Persist env vars (SessionStart, Setup) |
CLAUDE_CODE_REMOTE | "true" if web, empty if CLI |
Debugging
Hook not triggering?
- Check matcher syntax (case-sensitive, no spaces)
- Verify settings file location
- Test:
echo '{"session_id":"test"}' | python your-hook.py
Command failing?
- Add logging:
command 2>&1 | tee ~/.claude/hook-debug.log - Run with debug:
claude --debug - Hooks breaking on other operating systems? See cross-platform hook patterns
Infinite loops with Stop?
- Always check the
stop_hook_activeflag first - The built-in flag only covers
Stop. If you are gatingTeammateIdleorTaskCompleted, you own loop safety yourself, and there are three non-obvious rules: see hook loops and the exit-2 gate that fired five times
Start With One Hook
Pick your biggest friction point:
- Constant formatting? PostToolUse formatter
- Approving safe commands? PermissionRequest auto-approve
- Missing context? SessionStart injection
- Losing progress? PreCompact backup
- Incomplete tasks? Stop enforcement
One hook. One friction point eliminated. Then iterate. If you want a head start, the Code Kit ships with 5 pre-configured hooks covering skill activation, auto-formatting, context recovery, permission automation, and code validation -- each following the patterns in this guide.
Next Steps
- Configure Setup Hooks for automated onboarding and maintenance
- Configure the Stop Hook to enforce task completion
- Install the Permission Hook for intelligent auto-approval
- Set up Skill Activation for automatic skill loading
- Configure Context Recovery to survive compaction
- Explore Session Lifecycle Hooks for setup and cleanup
- Master permission rules and modes for static permission control alongside hooks
- Learn configuration basics for complete Claude Code setup
Last updated on