18 18
19<div style={{maxWidth: "500px", margin: "0 auto"}}>19<div style={{maxWidth: "500px", margin: "0 auto"}}>
20 <Frame>20 <Frame>
21 <img src="https://mintcdn.com/claude-code/lBsitdsGyD9caWJQ/images/hooks-lifecycle.svg?fit=max&auto=format&n=lBsitdsGyD9caWJQ&q=85&s=be3486ef2cf2563eb213b6cbbce93982" alt="Hook lifecycle diagram showing the sequence of hooks from SessionStart through the agentic loop (PreToolUse, PermissionRequest, PostToolUse, SubagentStart/Stop, TaskCompleted) to PostCompact and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution and WorktreeCreate, WorktreeRemove, Notification, ConfigChange, and InstructionsLoaded as standalone async events" width="520" height="1100" data-path="images/hooks-lifecycle.svg" />21 <img src="https://mintcdn.com/claude-code/2YzYcIR7V1VggfgF/images/hooks-lifecycle.svg?fit=max&auto=format&n=2YzYcIR7V1VggfgF&q=85&s=3004e6c5dc95c4fe7fa3eb40fdc4176c" alt="Hook lifecycle diagram showing the sequence of hooks from SessionStart through the agentic loop (PreToolUse, PermissionRequest, PostToolUse, SubagentStart/Stop, TaskCompleted) to Stop or StopFailure, TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution and WorktreeCreate, WorktreeRemove, Notification, ConfigChange, and InstructionsLoaded as standalone async events" width="520" height="1100" data-path="images/hooks-lifecycle.svg" />
22 </Frame>22 </Frame>
23</div>23</div>
24 24
36| `SubagentStart` | When a subagent is spawned |36| `SubagentStart` | When a subagent is spawned |
37| `SubagentStop` | When a subagent finishes |37| `SubagentStop` | When a subagent finishes |
38| `Stop` | When Claude finishes responding |38| `Stop` | When Claude finishes responding |
39| `StopFailure` | When the turn ends due to an API error. Output and exit code are ignored |
39| `TeammateIdle` | When an [agent team](/en/agent-teams) teammate is about to go idle |40| `TeammateIdle` | When an [agent team](/en/agent-teams) teammate is about to go idle |
40| `TaskCompleted` | When a task is being marked as completed |41| `TaskCompleted` | When a task is being marked as completed |
41| `InstructionsLoaded` | When a CLAUDE.md or `.claude/rules/*.md` file is loaded into context. Fires at session start and when files are lazily loaded during a session |42| `InstructionsLoaded` | When a CLAUDE.md or `.claude/rules/*.md` file is loaded into context. Fires at session start and when files are lazily loaded during a session |
166The `matcher` field is a regex string that filters when hooks fire. Use `"*"`, `""`, or omit `matcher` entirely to match all occurrences. Each event type matches on a different field:167The `matcher` field is a regex string that filters when hooks fire. Use `"*"`, `""`, or omit `matcher` entirely to match all occurrences. Each event type matches on a different field:
167 168
168| Event | What the matcher filters | Example matcher values |169| Event | What the matcher filters | Example matcher values |
169| :-------------------------------------------------------------------------------------------------------------------- | :------------------------ | :--------------------------------------------------------------------------------- |170| :---------------------------------------------------------------------------------------------- | :------------------------ | :------------------------------------------------------------------------------------------------------------------------ |
170| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |171| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |
171| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact` |172| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact` |
172| `SessionEnd` | why the session ended | `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |173| `SessionEnd` | why the session ended | `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
175| `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |176| `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |
176| `SubagentStop` | agent type | same values as `SubagentStart` |177| `SubagentStop` | agent type | same values as `SubagentStart` |
177| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` |178| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` |
178| `UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, `InstructionsLoaded` | no matcher support | always fires on every occurrence |179| `StopFailure` | error type | `rate_limit`, `authentication_failed`, `billing_error`, `invalid_request`, `server_error`, `max_output_tokens`, `unknown` |
180| `InstructionsLoaded` | load reason | `session_start`, `nested_traversal`, `path_glob_match`, `include`, `compact` |
181| `Elicitation` | MCP server name | your configured MCP server names |
182| `ElicitationResult` | MCP server name | same values as `Elicitation` |
183| `UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove` | no matcher support | always fires on every occurrence |
179 184
180The matcher is a regex, so `Edit|Write` matches either tool and `Notebook.*` matches any tool starting with Notebook. The matcher runs against a field from the [JSON input](#hook-input-and-output) that Claude Code sends to your hook on stdin. For tool events, that field is `tool_name`. Each [hook event](#hook-events) section lists the full set of matcher values and the input schema for that event.185The matcher is a regex, so `Edit|Write` matches either tool and `Notebook.*` matches any tool starting with Notebook. The matcher runs against a field from the [JSON input](#hook-input-and-output) that Claude Code sends to your hook on stdin. For tool events, that field is `tool_name`. Each [hook event](#hook-events) section lists the full set of matcher values and the input schema for that event.
181 186
199}204}
200```205```
201 206
202`UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, and `InstructionsLoaded` don't support matchers and always fire on every occurrence. If you add a `matcher` field to these events, it is silently ignored.207`UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, and `WorktreeRemove` don't support matchers and always fire on every occurrence. If you add a `matcher` field to these events, it is silently ignored.
203 208
204#### Match MCP tools209#### Match MCP tools
205 210
440 445
441### Common input fields446### Common input fields
442 447
443All hook events receive these fields as JSON, in addition to event-specific fields documented in each [hook event](#hook-events) section. For command hooks, this JSON arrives via stdin. For HTTP hooks, it arrives as the POST request body.448Hook events receive these fields as JSON, in addition to event-specific fields documented in each [hook event](#hook-events) section. For command hooks, this JSON arrives via stdin. For HTTP hooks, it arrives as the POST request body.
444 449
445| Field | Description |450| Field | Description |
446| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |451| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
447| `session_id` | Current session identifier |452| `session_id` | Current session identifier |
448| `transcript_path` | Path to conversation JSON |453| `transcript_path` | Path to conversation JSON |
449| `cwd` | Current working directory when the hook is invoked |454| `cwd` | Current working directory when the hook is invoked |
450| `permission_mode` | Current [permission mode](/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"dontAsk"`, or `"bypassPermissions"` |455| `permission_mode` | Current [permission mode](/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"dontAsk"`, or `"bypassPermissions"`. Not all events receive this field: see each event's JSON example below to check |
451| `hook_event_name` | Name of the event that fired |456| `hook_event_name` | Name of the event that fired |
452 457
453When running with `--agent` or inside a subagent, two additional fields are included:458When running with `--agent` or inside a subagent, two additional fields are included:
514| `TeammateIdle` | Yes | Prevents the teammate from going idle (teammate continues working) |519| `TeammateIdle` | Yes | Prevents the teammate from going idle (teammate continues working) |
515| `TaskCompleted` | Yes | Prevents the task from being marked as completed |520| `TaskCompleted` | Yes | Prevents the task from being marked as completed |
516| `ConfigChange` | Yes | Blocks the configuration change from taking effect (except `policy_settings`) |521| `ConfigChange` | Yes | Blocks the configuration change from taking effect (except `policy_settings`) |
522| `StopFailure` | No | Output and exit code are ignored |
517| `PostToolUse` | No | Shows stderr to Claude (tool already ran) |523| `PostToolUse` | No | Shows stderr to Claude (tool already ran) |
518| `PostToolUseFailure` | No | Shows stderr to Claude (tool already failed) |524| `PostToolUseFailure` | No | Shows stderr to Claude (tool already failed) |
519| `Notification` | No | Shows stderr to user only |525| `Notification` | No | Shows stderr to user only |
574Not every event supports blocking or controlling behavior through JSON. The events that do each use a different set of fields to express that decision. Use this table as a quick reference before writing a hook:580Not every event supports blocking or controlling behavior through JSON. The events that do each use a different set of fields to express that decision. Use this table as a quick reference before writing a hook:
575 581
576| Events | Decision pattern | Key fields |582| Events | Decision pattern | Key fields |
577| :------------------------------------------------------------------------------------ | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |583| :------------------------------------------------------------------------------------------------- | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
578| UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChange | Top-level `decision` | `decision: "block"`, `reason` |584| UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChange | Top-level `decision` | `decision: "block"`, `reason` |
579| TeammateIdle, TaskCompleted | Exit code or `continue: false` | Exit code 2 blocks the action with stderr feedback. JSON `{"continue": false, "stopReason": "..."}` also stops the teammate entirely, matching `Stop` hook behavior |585| TeammateIdle, TaskCompleted | Exit code or `continue: false` | Exit code 2 blocks the action with stderr feedback. JSON `{"continue": false, "stopReason": "..."}` also stops the teammate entirely, matching `Stop` hook behavior |
580| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason` |586| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason` |
582| WorktreeCreate | stdout path | Hook prints absolute path to created worktree. Non-zero exit fails creation |588| WorktreeCreate | stdout path | Hook prints absolute path to created worktree. Non-zero exit fails creation |
583| Elicitation | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values for accept) |589| Elicitation | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values for accept) |
584| ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |590| ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |
585| WorktreeRemove, Notification, SessionEnd, PreCompact, PostCompact, InstructionsLoaded | None | No decision control. Used for side effects like logging or cleanup |591| WorktreeRemove, Notification, SessionEnd, PreCompact, PostCompact, InstructionsLoaded, StopFailure | None | No decision control. Used for side effects like logging or cleanup |
586 592
587Here are examples of each pattern in action:593Here are examples of each pattern in action:
588 594
661 "session_id": "abc123",667 "session_id": "abc123",
662 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",668 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
663 "cwd": "/Users/...",669 "cwd": "/Users/...",
664 "permission_mode": "default",
665 "hook_event_name": "SessionStart",670 "hook_event_name": "SessionStart",
666 "source": "startup",671 "source": "startup",
667 "model": "claude-sonnet-4-6"672 "model": "claude-sonnet-4-6"
732 737
733Fires when a `CLAUDE.md` or `.claude/rules/*.md` file is loaded into context. This event fires at session start for eagerly-loaded files and again later when files are lazily loaded, for example when Claude accesses a subdirectory that contains a nested `CLAUDE.md` or when conditional rules with `paths:` frontmatter match. The hook does not support blocking or decision control. It runs asynchronously for observability purposes.738Fires when a `CLAUDE.md` or `.claude/rules/*.md` file is loaded into context. This event fires at session start for eagerly-loaded files and again later when files are lazily loaded, for example when Claude accesses a subdirectory that contains a nested `CLAUDE.md` or when conditional rules with `paths:` frontmatter match. The hook does not support blocking or decision control. It runs asynchronously for observability purposes.
734 739
735InstructionsLoaded does not support matchers and fires on every load occurrence.740The matcher runs against `load_reason`. For example, use `"matcher": "session_start"` to fire only for files loaded at session start, or `"matcher": "path_glob_match|nested_traversal"` to fire only for lazy loads.
736 741
737#### InstructionsLoaded input742#### InstructionsLoaded input
738 743
752 "session_id": "abc123",757 "session_id": "abc123",
753 "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",758 "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
754 "cwd": "/Users/my-project",759 "cwd": "/Users/my-project",
755 "permission_mode": "default",
756 "hook_event_name": "InstructionsLoaded",760 "hook_event_name": "InstructionsLoaded",
757 "file_path": "/Users/my-project/CLAUDE.md",761 "file_path": "/Users/my-project/CLAUDE.md",
758 "memory_type": "Project",762 "memory_type": "Project",
1182 "session_id": "abc123",1186 "session_id": "abc123",
1183 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1187 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1184 "cwd": "/Users/...",1188 "cwd": "/Users/...",
1185 "permission_mode": "default",
1186 "hook_event_name": "Notification",1189 "hook_event_name": "Notification",
1187 "message": "Claude needs your permission to use Bash",1190 "message": "Claude needs your permission to use Bash",
1188 "title": "Permission needed",1191 "title": "Permission needed",
1209 "session_id": "abc123",1212 "session_id": "abc123",
1210 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1213 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1211 "cwd": "/Users/...",1214 "cwd": "/Users/...",
1212 "permission_mode": "default",
1213 "hook_event_name": "SubagentStart",1215 "hook_event_name": "SubagentStart",
1214 "agent_id": "agent-abc123",1216 "agent_id": "agent-abc123",
1215 "agent_type": "Explore"1217 "agent_type": "Explore"
1259### Stop1261### Stop
1260 1262
1261Runs when the main Claude Code agent has finished responding. Does not run if1263Runs when the main Claude Code agent has finished responding. Does not run if
1262the stoppage occurred due to a user interrupt.1264the stoppage occurred due to a user interrupt. API errors fire
1265[StopFailure](#stopfailure) instead.
1263 1266
1264#### Stop input1267#### Stop input
1265 1268
1293}1296}
1294```1297```
1295 1298
1299### StopFailure
1300
1301Runs instead of [Stop](#stop) when the turn ends due to an API error. Output and exit code are ignored. Use this to log failures, send alerts, or take recovery actions when Claude cannot complete a response due to rate limits, authentication problems, or other API errors.
1302
1303#### StopFailure input
1304
1305In addition to the [common input fields](#common-input-fields), StopFailure hooks receive `error`, optional `error_details`, and optional `last_assistant_message`. The `error` field identifies the error type and is used for matcher filtering.
1306
1307| Field | Description |
1308| :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1309| `error` | Error type: `rate_limit`, `authentication_failed`, `billing_error`, `invalid_request`, `server_error`, `max_output_tokens`, or `unknown` |
1310| `error_details` | Additional details about the error, when available |
1311| `last_assistant_message` | The rendered error text shown in the conversation. Unlike `Stop` and `SubagentStop`, where this field holds Claude's conversational output, for `StopFailure` it contains the API error string itself, such as `"API Error: Rate limit reached"` |
1312
1313```json theme={null}
1314{
1315 "session_id": "abc123",
1316 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1317 "cwd": "/Users/...",
1318 "hook_event_name": "StopFailure",
1319 "error": "rate_limit",
1320 "error_details": "429 Too Many Requests",
1321 "last_assistant_message": "API Error: Rate limit reached"
1322}
1323```
1324
1325StopFailure hooks have no decision control. They run for notification and logging purposes only.
1326
1296### TeammateIdle1327### TeammateIdle
1297 1328
1298Runs when an [agent team](/en/agent-teams) teammate is about to go idle after finishing its turn. Use this to enforce quality gates before a teammate stops working, such as requiring passing lint checks or verifying that output files exist.1329Runs when an [agent team](/en/agent-teams) teammate is about to go idle after finishing its turn. Use this to enforce quality gates before a teammate stops working, such as requiring passing lint checks or verifying that output files exist.
1440 "session_id": "abc123",1471 "session_id": "abc123",
1441 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1472 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1442 "cwd": "/Users/...",1473 "cwd": "/Users/...",
1443 "permission_mode": "default",
1444 "hook_event_name": "ConfigChange",1474 "hook_event_name": "ConfigChange",
1445 "source": "project_settings",1475 "source": "project_settings",
1446 "file_path": "/Users/.../my-project/.claude/settings.json"1476 "file_path": "/Users/.../my-project/.claude/settings.json"
1571 "session_id": "abc123",1601 "session_id": "abc123",
1572 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1602 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1573 "cwd": "/Users/...",1603 "cwd": "/Users/...",
1574 "permission_mode": "default",
1575 "hook_event_name": "PreCompact",1604 "hook_event_name": "PreCompact",
1576 "trigger": "manual",1605 "trigger": "manual",
1577 "custom_instructions": ""1606 "custom_instructions": ""
1598 "session_id": "abc123",1627 "session_id": "abc123",
1599 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1628 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1600 "cwd": "/Users/...",1629 "cwd": "/Users/...",
1601 "permission_mode": "default",
1602 "hook_event_name": "PostCompact",1630 "hook_event_name": "PostCompact",
1603 "trigger": "manual",1631 "trigger": "manual",
1604 "compact_summary": "Summary of the compacted conversation..."1632 "compact_summary": "Summary of the compacted conversation..."
1631 "session_id": "abc123",1659 "session_id": "abc123",
1632 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1660 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1633 "cwd": "/Users/...",1661 "cwd": "/Users/...",
1634 "permission_mode": "default",
1635 "hook_event_name": "SessionEnd",1662 "hook_event_name": "SessionEnd",
1636 "reason": "other"1663 "reason": "other"
1637}1664}
1787* `PreCompact`1814* `PreCompact`
1788* `SessionEnd`1815* `SessionEnd`
1789* `SessionStart`1816* `SessionStart`
1817* `StopFailure`
1790* `SubagentStart`1818* `SubagentStart`
1791* `TeammateIdle`1819* `TeammateIdle`
1792* `WorktreeCreate`1820* `WorktreeCreate`