4 4
5# Hooks reference5# Hooks reference
6 6
7> Reference for Claude Code hook events, configuration schema, JSON input/output formats, exit codes, async hooks, prompt hooks, and MCP tool hooks.7> Reference for Claude Code hook events, configuration schema, JSON input/output formats, exit codes, async hooks, HTTP hooks, prompt hooks, and MCP tool hooks.
8 8
9<Tip>9<Tip>
10 For a quickstart guide with examples, see [Automate workflows with hooks](/en/hooks-guide).10 For a quickstart guide with examples, see [Automate workflows with hooks](/en/hooks-guide).
11</Tip>11</Tip>
12 12
13Hooks are user-defined shell commands or LLM prompts that execute automatically at specific points in Claude Code's lifecycle. Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks and MCP tool hooks. If you're setting up hooks for the first time, start with the [guide](/en/hooks-guide) instead.13Hooks are user-defined shell commands, HTTP endpoints, or LLM prompts that execute automatically at specific points in Claude Code's lifecycle. Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks, HTTP hooks, and MCP tool hooks. If you're setting up hooks for the first time, start with the [guide](/en/hooks-guide) instead.
14 14
15## Hook lifecycle15## Hook lifecycle
16 16
17Hooks fire at specific points during a Claude Code session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, this arrives on stdin. Your handler can then inspect the input, take action, and optionally return a decision. Some events fire once per session, while others fire repeatedly inside the agentic loop:17Hooks fire at specific points during a Claude Code session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. For HTTP hooks, it arrives as the POST request body. Your handler can then inspect the input, take action, and optionally return a decision. Some events fire once per session, while others fire repeatedly inside the agentic loop:
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/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=ce5f1225339bbccdfbb52e99205db912" alt="Hook lifecycle diagram showing the sequence of hooks from SessionStart through the agentic loop to SessionEnd, with WorktreeCreate and WorktreeRemove as standalone setup and teardown events" data-og-width="520" width="520" data-og-height="1020" height="1020" data-path="images/hooks-lifecycle.svg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=280&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=7c7143c65492c1beb6bc66f5d206ba15 280w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=560&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=dafaebf8f789f94edbf6bd66853c69df 560w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=840&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=2caa51d2d95596f1f80b92e3f5f534fa 840w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=1100&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=614def559f34f9b0c1dec93739d96b64 1100w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=1650&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=ca45b85fdd8b2da81c69d12c453230cb 1650w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=2500&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=7fd92d6b9713493f59962c9f295c9d2f 2500w" />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
25The table below summarizes when each event fires. The [Hook events](#hook-events) section documents the full input schema and decision control options for each one.25The table below summarizes when each event fires. The [Hook events](#hook-events) section documents the full input schema and decision control options for each one.
26 26
27| Event | When it fires |27| Event | When it fires |
28| :------------------- | :---------------------------------------------------------------------------------------------------------- |28| :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- |
29| `SessionStart` | When a session begins or resumes |29| `SessionStart` | When a session begins or resumes |
30| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |30| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
31| `PreToolUse` | Before a tool call executes. Can block it |31| `PreToolUse` | Before a tool call executes. Can block it |
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 |
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 |
41| `ConfigChange` | When a configuration file changes during a session |43| `ConfigChange` | When a configuration file changes during a session |
42| `WorktreeCreate` | When a worktree is being created via `--worktree` or `isolation: "worktree"`. Replaces default git behavior |44| `WorktreeCreate` | When a worktree is being created via `--worktree` or `isolation: "worktree"`. Replaces default git behavior |
43| `WorktreeRemove` | When a worktree is being removed, either at session exit or when a subagent finishes |45| `WorktreeRemove` | When a worktree is being removed, either at session exit or when a subagent finishes |
44| `PreCompact` | Before context compaction |46| `PreCompact` | Before context compaction |
47| `PostCompact` | After context compaction completes |
48| `Elicitation` | When an MCP server requests user input during a tool call |
49| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
45| `SessionEnd` | When a session terminates |50| `SessionEnd` | When a session terminates |
46 51
47### How a hook resolves52### How a hook resolves
89Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"`. Here's what happens:94Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"`. Here's what happens:
90 95
91<Frame>96<Frame>
92 <img src="https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=5bb890134390ecd0581477cf41ef730b" alt="Hook resolution flow: PreToolUse event fires, matcher checks for Bash match, hook handler runs, result returns to Claude Code" data-og-width="780" width="780" data-og-height="290" height="290" data-path="images/hook-resolution.svg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=280&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=5dcaecd24c260b8a90365d74e2c1fcda 280w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=560&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=c03d91c279f01d92e58ddd70fdbe66f2 560w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=840&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=1be57a4819cbb949a5ea9d08a05c9ecd 840w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=1100&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=0e9dd1807dc7a5c56011d0889b0d5208 1100w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=1650&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=69496ac02e70fabfece087ba31a1dcfc 1650w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=2500&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=a012346cb46a33b86580348802055267 2500w" />97 <img src="https://mintcdn.com/claude-code/c5r9_6tjPMzFdDDT/images/hook-resolution.svg?fit=max&auto=format&n=c5r9_6tjPMzFdDDT&q=85&s=ad667ee6d86ab2276aa48a4e73e220df" alt="Hook resolution flow: PreToolUse event fires, matcher checks for Bash match, hook handler runs, result returns to Claude Code" width="780" height="290" data-path="images/hook-resolution.svg" />
93</Frame>98</Frame>
94 99
95<Steps>100<Steps>
139See [How a hook resolves](#how-a-hook-resolves) above for a complete walkthrough with an annotated example.144See [How a hook resolves](#how-a-hook-resolves) above for a complete walkthrough with an annotated example.
140 145
141<Note>146<Note>
142 This page uses specific terms for each level: **hook event** for the lifecycle point, **matcher group** for the filter, and **hook handler** for the shell command, prompt, or agent that runs. "Hook" on its own refers to the general feature.147 This page uses specific terms for each level: **hook event** for the lifecycle point, **matcher group** for the filter, and **hook handler** for the shell command, HTTP endpoint, prompt, or agent that runs. "Hook" on its own refers to the general feature.
143</Note>148</Note>
144 149
145### Hook locations150### Hook locations
162The `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:
163 168
164| Event | What the matcher filters | Example matcher values |169| Event | What the matcher filters | Example matcher values |
165| :---------------------------------------------------------------------------------------------- | :------------------------ | :--------------------------------------------------------------------------------- |170| :---------------------------------------------------------------------------------------------- | :------------------------ | :------------------------------------------------------------------------------------------------------------------------ |
166| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |171| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |
167| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact` |172| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact` |
168| `SessionEnd` | why the session ended | `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |173| `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
169| `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog` |174| `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog` |
170| `SubagentStart` | agent type | `Bash`, `Explore`, `Plan`, or custom agent names |175| `SubagentStart` | agent type | `Bash`, `Explore`, `Plan`, or custom agent names |
171| `PreCompact` | what triggered compaction | `manual`, `auto` |176| `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |
172| `SubagentStop` | agent type | same values as `SubagentStart` |177| `SubagentStop` | agent type | same values as `SubagentStart` |
173| `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` |
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` |
174| `UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove` | no matcher support | always fires on every occurrence |183| `UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove` | no matcher support | always fires on every occurrence |
175 184
176The 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.
243 252
244### Hook handler fields253### Hook handler fields
245 254
246Each object in the inner `hooks` array is a hook handler: the shell command, LLM prompt, or agent that runs when the matcher matches. There are three types:255Each object in the inner `hooks` array is a hook handler: the shell command, HTTP endpoint, LLM prompt, or agent that runs when the matcher matches. There are four types:
247 256
248* **[Command hooks](#command-hook-fields)** (`type: "command"`): run a shell command. Your script receives the event's [JSON input](#hook-input-and-output) on stdin and communicates results back through exit codes and stdout.257* **[Command hooks](#command-hook-fields)** (`type: "command"`): run a shell command. Your script receives the event's [JSON input](#hook-input-and-output) on stdin and communicates results back through exit codes and stdout.
258* **[HTTP hooks](#http-hook-fields)** (`type: "http"`): send the event's JSON input as an HTTP POST request to a URL. The endpoint communicates results back through the response body using the same [JSON output format](#json-output) as command hooks.
249* **[Prompt hooks](#prompt-and-agent-hook-fields)** (`type: "prompt"`): send a prompt to a Claude model for single-turn evaluation. The model returns a yes/no decision as JSON. See [Prompt-based hooks](#prompt-based-hooks).259* **[Prompt hooks](#prompt-and-agent-hook-fields)** (`type: "prompt"`): send a prompt to a Claude model for single-turn evaluation. The model returns a yes/no decision as JSON. See [Prompt-based hooks](#prompt-based-hooks).
250* **[Agent hooks](#prompt-and-agent-hook-fields)** (`type: "agent"`): spawn a subagent that can use tools like Read, Grep, and Glob to verify conditions before returning a decision. See [Agent-based hooks](#agent-based-hooks).260* **[Agent hooks](#prompt-and-agent-hook-fields)** (`type: "agent"`): spawn a subagent that can use tools like Read, Grep, and Glob to verify conditions before returning a decision. See [Agent-based hooks](#agent-based-hooks).
251 261
255 265
256| Field | Required | Description |266| Field | Required | Description |
257| :-------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |267| :-------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
258| `type` | yes | `"command"`, `"prompt"`, or `"agent"` |268| `type` | yes | `"command"`, `"http"`, `"prompt"`, or `"agent"` |
259| `timeout` | no | Seconds before canceling. Defaults: 600 for command, 30 for prompt, 60 for agent |269| `timeout` | no | Seconds before canceling. Defaults: 600 for command, 30 for prompt, 60 for agent |
260| `statusMessage` | no | Custom spinner message displayed while the hook runs |270| `statusMessage` | no | Custom spinner message displayed while the hook runs |
261| `once` | no | If `true`, runs only once per session then is removed. Skills only, not agents. See [Hooks in skills and agents](#hooks-in-skills-and-agents) |271| `once` | no | If `true`, runs only once per session then is removed. Skills only, not agents. See [Hooks in skills and agents](#hooks-in-skills-and-agents) |
269| `command` | yes | Shell command to execute |279| `command` | yes | Shell command to execute |
270| `async` | no | If `true`, runs in the background without blocking. See [Run hooks in the background](#run-hooks-in-the-background) |280| `async` | no | If `true`, runs in the background without blocking. See [Run hooks in the background](#run-hooks-in-the-background) |
271 281
282#### HTTP hook fields
283
284In addition to the [common fields](#common-fields), HTTP hooks accept these fields:
285
286| Field | Required | Description |
287| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
288| `url` | yes | URL to send the POST request to |
289| `headers` | no | Additional HTTP headers as key-value pairs. Values support environment variable interpolation using `$VAR_NAME` or `${VAR_NAME}` syntax. Only variables listed in `allowedEnvVars` are resolved |
290| `allowedEnvVars` | no | List of environment variable names that may be interpolated into header values. References to unlisted variables are replaced with empty strings. Required for any env var interpolation to work |
291
292Claude Code sends the hook's [JSON input](#hook-input-and-output) as the POST request body with `Content-Type: application/json`. The response body uses the same [JSON output format](#json-output) as command hooks.
293
294Error handling differs from command hooks: non-2xx responses, connection failures, and timeouts all produce non-blocking errors that allow execution to continue. To block a tool call or deny a permission, return a 2xx response with a JSON body containing `decision: "block"` or a `hookSpecificOutput` with `permissionDecision: "deny"`.
295
296This example sends `PreToolUse` events to a local validation service, authenticating with a token from the `MY_TOKEN` environment variable:
297
298```json theme={null}
299{
300 "hooks": {
301 "PreToolUse": [
302 {
303 "matcher": "Bash",
304 "hooks": [
305 {
306 "type": "http",
307 "url": "http://localhost:8080/hooks/pre-tool-use",
308 "timeout": 30,
309 "headers": {
310 "Authorization": "Bearer $MY_TOKEN"
311 },
312 "allowedEnvVars": ["MY_TOKEN"]
313 }
314 ]
315 }
316 ]
317 }
318}
319```
320
272#### Prompt and agent hook fields321#### Prompt and agent hook fields
273 322
274In addition to the [common fields](#common-fields), prompt and agent hooks accept these fields:323In addition to the [common fields](#common-fields), prompt and agent hooks accept these fields:
278| `prompt` | yes | Prompt text to send to the model. Use `$ARGUMENTS` as a placeholder for the hook input JSON |327| `prompt` | yes | Prompt text to send to the model. Use `$ARGUMENTS` as a placeholder for the hook input JSON |
279| `model` | no | Model to use for evaluation. Defaults to a fast model |328| `model` | no | Model to use for evaluation. Defaults to a fast model |
280 329
281All matching hooks run in parallel, and identical handlers are deduplicated automatically. Handlers run in the current directory with Claude Code's environment. The `$CLAUDE_CODE_REMOTE` environment variable is set to `"true"` in remote web environments and not set in the local CLI.330All matching hooks run in parallel, and identical handlers are deduplicated automatically. Command hooks are deduplicated by command string, and HTTP hooks are deduplicated by URL. Handlers run in the current directory with Claude Code's environment. The `$CLAUDE_CODE_REMOTE` environment variable is set to `"true"` in remote web environments and not set in the local CLI.
282 331
283### Reference scripts by path332### Reference scripts by path
284 333
285Use environment variables to reference hook scripts relative to the project or plugin root, regardless of the working directory when the hook runs:334Use environment variables to reference hook scripts relative to the project or plugin root, regardless of the working directory when the hook runs:
286 335
287* `$CLAUDE_PROJECT_DIR`: the project root. Wrap in quotes to handle paths with spaces.336* `$CLAUDE_PROJECT_DIR`: the project root. Wrap in quotes to handle paths with spaces.
288* `${CLAUDE_PLUGIN_ROOT}`: the plugin's root directory, for scripts bundled with a [plugin](/en/plugins).337* `${CLAUDE_PLUGIN_ROOT}`: the plugin's installation directory, for scripts bundled with a [plugin](/en/plugins). Changes on each plugin update.
338* `${CLAUDE_PLUGIN_DATA}`: the plugin's [persistent data directory](/en/plugins-reference#persistent-data-directory), for dependencies and state that should survive plugin updates.
289 339
290<Tabs>340<Tabs>
291 <Tab title="Project scripts">341 <Tab title="Project scripts">
366 416
367### The `/hooks` menu417### The `/hooks` menu
368 418
369Type `/hooks` in Claude Code to open the interactive hooks manager, where you can view, add, and delete hooks without editing settings files directly. For a step-by-step walkthrough, see [Set up your first hook](/en/hooks-guide#set-up-your-first-hook) in the guide.419Type `/hooks` in Claude Code to open a read-only browser for your configured hooks. The menu shows every hook event with a count of configured hooks, lets you drill into matchers, and shows the full details of each hook handler. Use it to verify configuration, check which settings file a hook came from, or inspect a hook's command, prompt, or URL.
420
421The menu displays all four hook types: `command`, `prompt`, `agent`, and `http`. Each hook is labeled with a `[type]` prefix and a source indicating where it was defined:
370 422
371Each hook in the menu is labeled with a bracket prefix indicating its source:423* `User`: from `~/.claude/settings.json`
424* `Project`: from `.claude/settings.json`
425* `Local`: from `.claude/settings.local.json`
426* `Plugin`: from a plugin's `hooks/hooks.json`
427* `Session`: registered in memory for the current session
428* `Built-in`: registered internally by Claude Code
372 429
373* `[User]`: from `~/.claude/settings.json`430Selecting a hook opens a detail view showing its event, matcher, type, source file, and the full command, prompt, or URL. The menu is read-only: to add, modify, or remove hooks, edit the settings JSON directly or ask Claude to make the change.
374* `[Project]`: from `.claude/settings.json`
375* `[Local]`: from `.claude/settings.local.json`
376* `[Plugin]`: from a plugin's `hooks/hooks.json`, read-only
377 431
378### Disable or remove hooks432### Disable or remove hooks
379 433
380To remove a hook, delete its entry from the settings JSON file, or use the `/hooks` menu and select the hook to delete it.434To remove a hook, delete its entry from the settings JSON file.
381 435
382To temporarily disable all hooks without removing them, set `"disableAllHooks": true` in your settings file or use the toggle in the `/hooks` menu. There is no way to disable an individual hook while keeping it in the configuration.436To temporarily disable all hooks without removing them, set `"disableAllHooks": true` in your settings file. There is no way to disable an individual hook while keeping it in the configuration.
383 437
384The `disableAllHooks` setting respects the managed settings hierarchy. If an administrator has configured hooks through managed policy settings, `disableAllHooks` set in user, project, or local settings cannot disable those managed hooks. Only `disableAllHooks` set at the managed settings level can disable managed hooks.438The `disableAllHooks` setting respects the managed settings hierarchy. If an administrator has configured hooks through managed policy settings, `disableAllHooks` set in user, project, or local settings cannot disable those managed hooks. Only `disableAllHooks` set at the managed settings level can disable managed hooks.
385 439
386Direct edits to hooks in settings files don't take effect immediately. Claude Code captures a snapshot of hooks at startup and uses it throughout the session. This prevents malicious or accidental hook modifications from taking effect mid-session without your review. If hooks are modified externally, Claude Code warns you and requires review in the `/hooks` menu before changes apply.440Direct edits to hooks in settings files are normally picked up automatically by the file watcher.
387 441
388## Hook input and output442## Hook input and output
389 443
390Hooks receive JSON data via stdin and communicate results through exit codes, stdout, and stderr. This section covers fields and behavior common to all events. Each event's section under [Hook events](#hook-events) includes its specific input schema and decision control options.444Command hooks receive JSON data via stdin and communicate results through exit codes, stdout, and stderr. HTTP hooks receive the same JSON as the POST request body and communicate results through the HTTP response body. This section covers fields and behavior common to all events. Each event's section under [Hook events](#hook-events) includes its specific input schema and decision control options.
391 445
392### Common input fields446### Common input fields
393 447
394All hook events receive these fields via stdin as JSON, in addition to event-specific fields documented in each [hook event](#hook-events) section: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.
395 449
396| Field | Description |450| Field | Description |
397| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |451| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
398| `session_id` | Current session identifier |452| `session_id` | Current session identifier |
399| `transcript_path` | Path to conversation JSON |453| `transcript_path` | Path to conversation JSON |
400| `cwd` | Current working directory when the hook is invoked |454| `cwd` | Current working directory when the hook is invoked |
401| `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"`, `"auto"`, `"dontAsk"`, or `"bypassPermissions"`. Not all events receive this field: see each event's JSON example below to check |
402| `hook_event_name` | Name of the event that fired |456| `hook_event_name` | Name of the event that fired |
403 457
458When running with `--agent` or inside a subagent, two additional fields are included:
459
460| Field | Description |
461| :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
462| `agent_id` | Unique identifier for the subagent. Present only when the hook fires inside a subagent call. Use this to distinguish subagent hook calls from main-thread calls. |
463| `agent_type` | Agent name (for example, `"Explore"` or `"security-reviewer"`). Present when the session uses `--agent` or the hook fires inside a subagent. For subagents, the subagent's type takes precedence over the session's `--agent` value. |
464
404For example, a `PreToolUse` hook for a Bash command receives this on stdin:465For example, a `PreToolUse` hook for a Bash command receives this on stdin:
405 466
406```json theme={null}467```json theme={null}
458| `TeammateIdle` | Yes | Prevents the teammate from going idle (teammate continues working) |519| `TeammateIdle` | Yes | Prevents the teammate from going idle (teammate continues working) |
459| `TaskCompleted` | Yes | Prevents the task from being marked as completed |520| `TaskCompleted` | Yes | Prevents the task from being marked as completed |
460| `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 |
461| `PostToolUse` | No | Shows stderr to Claude (tool already ran) |523| `PostToolUse` | No | Shows stderr to Claude (tool already ran) |
462| `PostToolUseFailure` | No | Shows stderr to Claude (tool already failed) |524| `PostToolUseFailure` | No | Shows stderr to Claude (tool already failed) |
463| `Notification` | No | Shows stderr to user only |525| `Notification` | No | Shows stderr to user only |
465| `SessionStart` | No | Shows stderr to user only |527| `SessionStart` | No | Shows stderr to user only |
466| `SessionEnd` | No | Shows stderr to user only |528| `SessionEnd` | No | Shows stderr to user only |
467| `PreCompact` | No | Shows stderr to user only |529| `PreCompact` | No | Shows stderr to user only |
530| `PostCompact` | No | Shows stderr to user only |
531| `Elicitation` | Yes | Denies the elicitation |
532| `ElicitationResult` | Yes | Blocks the response (action becomes decline) |
468| `WorktreeCreate` | Yes | Any non-zero exit code causes worktree creation to fail |533| `WorktreeCreate` | Yes | Any non-zero exit code causes worktree creation to fail |
469| `WorktreeRemove` | No | Failures are logged in debug mode only |534| `WorktreeRemove` | No | Failures are logged in debug mode only |
535| `InstructionsLoaded` | No | Exit code is ignored |
536
537### HTTP response handling
538
539HTTP hooks use HTTP status codes and response bodies instead of exit codes and stdout:
540
541* **2xx with an empty body**: success, equivalent to exit code 0 with no output
542* **2xx with a plain text body**: success, the text is added as context
543* **2xx with a JSON body**: success, parsed using the same [JSON output](#json-output) schema as command hooks
544* **Non-2xx status**: non-blocking error, execution continues
545* **Connection failure or timeout**: non-blocking error, execution continues
546
547Unlike command hooks, HTTP hooks cannot signal a blocking error through status codes alone. To block a tool call or deny a permission, return a 2xx response with a JSON body containing the appropriate decision fields.
470 548
471### JSON output549### JSON output
472 550
502Not 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:
503 581
504| Events | Decision pattern | Key fields |582| Events | Decision pattern | Key fields |
505| :---------------------------------------------------------------------------------- | :------------------- | :-------------------------------------------------------------------------- |583| :------------------------------------------------------------------------------------------------- | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
506| 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` |
507| TeammateIdle, TaskCompleted | Exit code only | Exit code 2 blocks the action, stderr is fed back as feedback |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 |
508| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason` |586| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason` |
509| PermissionRequest | `hookSpecificOutput` | `decision.behavior` (allow/deny) |587| PermissionRequest | `hookSpecificOutput` | `decision.behavior` (allow/deny) |
510| 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 |
511| WorktreeRemove, Notification, SessionEnd, PreCompact | None | No decision control. Used for side effects like logging or cleanup |589| Elicitation | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values for accept) |
590| ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |
591| WorktreeRemove, Notification, SessionEnd, PreCompact, PostCompact, InstructionsLoaded, StopFailure | None | No decision control. Used for side effects like logging or cleanup |
512 592
513Here are examples of each pattern in action:593Here are examples of each pattern in action:
514 594
567 647
568Runs when Claude Code starts a new session or resumes an existing session. Useful for loading development context like existing issues or recent changes to your codebase, or setting up environment variables. For static context that does not require a script, use [CLAUDE.md](/en/memory) instead.648Runs when Claude Code starts a new session or resumes an existing session. Useful for loading development context like existing issues or recent changes to your codebase, or setting up environment variables. For static context that does not require a script, use [CLAUDE.md](/en/memory) instead.
569 649
570SessionStart runs on every session, so keep these hooks fast.650SessionStart runs on every session, so keep these hooks fast. Only `type: "command"` hooks are supported.
571 651
572The matcher value corresponds to how the session was initiated:652The matcher value corresponds to how the session was initiated:
573 653
587 "session_id": "abc123",667 "session_id": "abc123",
588 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",668 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
589 "cwd": "/Users/...",669 "cwd": "/Users/...",
590 "permission_mode": "default",
591 "hook_event_name": "SessionStart",670 "hook_event_name": "SessionStart",
592 "source": "startup",671 "source": "startup",
593 "model": "claude-sonnet-4-6"672 "model": "claude-sonnet-4-6"
654 `CLAUDE_ENV_FILE` is available for SessionStart hooks. Other hook types do not have access to this variable.733 `CLAUDE_ENV_FILE` is available for SessionStart hooks. Other hook types do not have access to this variable.
655</Note>734</Note>
656 735
736### InstructionsLoaded
737
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.
739
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.
741
742#### InstructionsLoaded input
743
744In addition to the [common input fields](#common-input-fields), InstructionsLoaded hooks receive these fields:
745
746| Field | Description |
747| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
748| `file_path` | Absolute path to the instruction file that was loaded |
749| `memory_type` | Scope of the file: `"User"`, `"Project"`, `"Local"`, or `"Managed"` |
750| `load_reason` | Why the file was loaded: `"session_start"`, `"nested_traversal"`, `"path_glob_match"`, `"include"`, or `"compact"`. The `"compact"` value fires when instruction files are re-loaded after a compaction event |
751| `globs` | Path glob patterns from the file's `paths:` frontmatter, if any. Present only for `path_glob_match` loads |
752| `trigger_file_path` | Path to the file whose access triggered this load, for lazy loads |
753| `parent_file_path` | Path to the parent instruction file that included this one, for `include` loads |
754
755```json theme={null}
756{
757 "session_id": "abc123",
758 "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
759 "cwd": "/Users/my-project",
760 "hook_event_name": "InstructionsLoaded",
761 "file_path": "/Users/my-project/CLAUDE.md",
762 "memory_type": "Project",
763 "load_reason": "session_start"
764}
765```
766
767#### InstructionsLoaded decision control
768
769InstructionsLoaded hooks have no decision control. They cannot block or modify instruction loading. Use this event for audit logging, compliance tracking, or observability.
770
657### UserPromptSubmit771### UserPromptSubmit
658 772
659Runs when the user submits a prompt, before Claude processes it. This allows you773Runs when the user submits a prompt, before Claude processes it. This allows you
712 826
713### PreToolUse827### PreToolUse
714 828
715Runs after Claude creates tool parameters and before processing the tool call. Matches on tool name: `Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `Task`, `WebFetch`, `WebSearch`, and any [MCP tool names](#match-mcp-tools).829Runs after Claude creates tool parameters and before processing the tool call. Matches on tool name: `Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `Agent`, `WebFetch`, `WebSearch`, and any [MCP tool names](#match-mcp-tools).
716 830
717Use [PreToolUse decision control](#pretooluse-decision-control) to allow, deny, or ask for permission to use the tool.831Use [PreToolUse decision control](#pretooluse-decision-control) to allow, deny, or ask for permission to use the tool.
718 832
802| `allowed_domains` | array | `["docs.example.com"]` | Optional: only include results from these domains |916| `allowed_domains` | array | `["docs.example.com"]` | Optional: only include results from these domains |
803| `blocked_domains` | array | `["spam.example.com"]` | Optional: exclude results from these domains |917| `blocked_domains` | array | `["spam.example.com"]` | Optional: exclude results from these domains |
804 918
805##### Task919##### Agent
806 920
807Spawns a [subagent](/en/sub-agents).921Spawns a [subagent](/en/sub-agents).
808 922
818`PreToolUse` hooks can control whether a tool call proceeds. Unlike other hooks that use a top-level `decision` field, PreToolUse returns its decision inside a `hookSpecificOutput` object. This gives it richer control: three outcomes (allow, deny, or ask) plus the ability to modify tool input before execution.932`PreToolUse` hooks can control whether a tool call proceeds. Unlike other hooks that use a top-level `decision` field, PreToolUse returns its decision inside a `hookSpecificOutput` object. This gives it richer control: three outcomes (allow, deny, or ask) plus the ability to modify tool input before execution.
819 933
820| Field | Description |934| Field | Description |
821| :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |935| :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
822| `permissionDecision` | `"allow"` bypasses the permission system, `"deny"` prevents the tool call, `"ask"` prompts the user to confirm |936| `permissionDecision` | `"allow"` skips the permission prompt. `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. [Deny and ask rules](/en/permissions#manage-permissions) still apply when a hook returns `"allow"` |
823| `permissionDecisionReason` | For `"allow"` and `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude |937| `permissionDecisionReason` | For `"allow"` and `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude |
824| `updatedInput` | Modifies the tool's input parameters before execution. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user |938| `updatedInput` | Modifies the tool's input parameters before execution. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user |
825| `additionalContext` | String added to Claude's context before the tool executes |939| `additionalContext` | String added to Claude's context before the tool executes |
826 940
941When a hook returns `"ask"`, the permission prompt displayed to the user includes a label identifying where the hook came from: for example, `[User]`, `[Project]`, `[Plugin]`, or `[Local]`. This helps users understand which configuration source is requesting confirmation.
942
827```json theme={null}943```json theme={null}
828{944{
829 "hookSpecificOutput": {945 "hookSpecificOutput": {
866 "description": "Remove node_modules directory"982 "description": "Remove node_modules directory"
867 },983 },
868 "permission_suggestions": [984 "permission_suggestions": [
869 { "type": "toolAlwaysAllow", "tool": "Bash" }985 {
986 "type": "addRules",
987 "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
988 "behavior": "allow",
989 "destination": "localSettings"
990 }
870 ]991 ]
871}992}
872```993```
876`PermissionRequest` hooks can allow or deny permission requests. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return a `decision` object with these event-specific fields:997`PermissionRequest` hooks can allow or deny permission requests. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return a `decision` object with these event-specific fields:
877 998
878| Field | Description |999| Field | Description |
879| :------------------- | :------------------------------------------------------------------------------------------------------------- |1000| :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
880| `behavior` | `"allow"` grants the permission, `"deny"` denies it |1001| `behavior` | `"allow"` grants the permission, `"deny"` denies it |
881| `updatedInput` | For `"allow"` only: modifies the tool's input parameters before execution |1002| `updatedInput` | For `"allow"` only: modifies the tool's input parameters before execution |
882| `updatedPermissions` | For `"allow"` only: applies permission rule updates, equivalent to the user selecting an "always allow" option |1003| `updatedPermissions` | For `"allow"` only: array of [permission update entries](#permission-update-entries) to apply, such as adding an allow rule or changing the session permission mode |
883| `message` | For `"deny"` only: tells Claude why the permission was denied |1004| `message` | For `"deny"` only: tells Claude why the permission was denied |
884| `interrupt` | For `"deny"` only: if `true`, stops Claude |1005| `interrupt` | For `"deny"` only: if `true`, stops Claude |
885 1006
897}1018}
898```1019```
899 1020
1021#### Permission update entries
1022
1023The `updatedPermissions` output field and the [`permission_suggestions` input field](#permissionrequest-input) both use the same array of entry objects. Each entry has a `type` that determines its other fields, and a `destination` that controls where the change is written.
1024
1025| `type` | Fields | Effect |
1026| :------------------ | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1027| `addRules` | `rules`, `behavior`, `destination` | Adds permission rules. `rules` is an array of `{toolName, ruleContent?}` objects. Omit `ruleContent` to match the whole tool. `behavior` is `"allow"`, `"deny"`, or `"ask"` |
1028| `replaceRules` | `rules`, `behavior`, `destination` | Replaces all rules of the given `behavior` at the `destination` with the provided `rules` |
1029| `removeRules` | `rules`, `behavior`, `destination` | Removes matching rules of the given `behavior` |
1030| `setMode` | `mode`, `destination` | Changes the permission mode. Valid modes are `default`, `acceptEdits`, `dontAsk`, `bypassPermissions`, and `plan` |
1031| `addDirectories` | `directories`, `destination` | Adds working directories. `directories` is an array of path strings |
1032| `removeDirectories` | `directories`, `destination` | Removes working directories |
1033
1034The `destination` field on every entry determines whether the change stays in memory or persists to a settings file.
1035
1036| `destination` | Writes to |
1037| :---------------- | :---------------------------------------------- |
1038| `session` | in-memory only, discarded when the session ends |
1039| `localSettings` | `.claude/settings.local.json` |
1040| `projectSettings` | `.claude/settings.json` |
1041| `userSettings` | `~/.claude/settings.json` |
1042
1043A hook can echo one of the `permission_suggestions` it received as its own `updatedPermissions` output, which is equivalent to the user selecting that "always allow" option in the dialog.
1044
900### PostToolUse1045### PostToolUse
901 1046
902Runs immediately after a tool completes successfully.1047Runs immediately after a tool completes successfully.
1041 "session_id": "abc123",1186 "session_id": "abc123",
1042 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1187 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1043 "cwd": "/Users/...",1188 "cwd": "/Users/...",
1044 "permission_mode": "default",
1045 "hook_event_name": "Notification",1189 "hook_event_name": "Notification",
1046 "message": "Claude needs your permission to use Bash",1190 "message": "Claude needs your permission to use Bash",
1047 "title": "Permission needed",1191 "title": "Permission needed",
1057 1201
1058### SubagentStart1202### SubagentStart
1059 1203
1060Runs when a Claude Code subagent is spawned via the Task tool. Supports matchers to filter by agent type name (built-in agents like `Bash`, `Explore`, `Plan`, or custom agent names from `.claude/agents/`).1204Runs when a Claude Code subagent is spawned via the Agent tool. Supports matchers to filter by agent type name (built-in agents like `Bash`, `Explore`, `Plan`, or custom agent names from `.claude/agents/`).
1061 1205
1062#### SubagentStart input1206#### SubagentStart input
1063 1207
1068 "session_id": "abc123",1212 "session_id": "abc123",
1069 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1213 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1070 "cwd": "/Users/...",1214 "cwd": "/Users/...",
1071 "permission_mode": "default",
1072 "hook_event_name": "SubagentStart",1215 "hook_event_name": "SubagentStart",
1073 "agent_id": "agent-abc123",1216 "agent_id": "agent-abc123",
1074 "agent_type": "Explore"1217 "agent_type": "Explore"
1118### Stop1261### Stop
1119 1262
1120Runs 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
1121the stoppage occurred due to a user interrupt.1264the stoppage occurred due to a user interrupt. API errors fire
1265[StopFailure](#stopfailure) instead.
1122 1266
1123#### Stop input1267#### Stop input
1124 1268
1152}1296}
1153```1297```
1154 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
1155### TeammateIdle1327### TeammateIdle
1156 1328
1157Runs 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.
1158 1330
1159When a `TeammateIdle` hook exits with code 2, the teammate receives the stderr message as feedback and continues working instead of going idle. TeammateIdle hooks do not support matchers and fire on every occurrence.1331When a `TeammateIdle` hook exits with code 2, the teammate receives the stderr message as feedback and continues working instead of going idle. To stop the teammate entirely instead of re-running it, return JSON with `{"continue": false, "stopReason": "..."}`. TeammateIdle hooks do not support matchers and fire on every occurrence.
1160 1332
1161#### TeammateIdle input1333#### TeammateIdle input
1162 1334
1181 1353
1182#### TeammateIdle decision control1354#### TeammateIdle decision control
1183 1355
1184TeammateIdle hooks use exit codes only, not JSON decision control. This example checks that a build artifact exists before allowing a teammate to go idle:1356TeammateIdle hooks support two ways to control teammate behavior:
1357
1358* **Exit code 2**: the teammate receives the stderr message as feedback and continues working instead of going idle.
1359* **JSON `{"continue": false, "stopReason": "..."}`**: stops the teammate entirely, matching `Stop` hook behavior. The `stopReason` is shown to the user.
1360
1361This example checks that a build artifact exists before allowing a teammate to go idle:
1185 1362
1186```bash theme={null}1363```bash theme={null}
1187#!/bin/bash1364#!/bin/bash
1198 1375
1199Runs when a task is being marked as completed. This fires in two situations: when any agent explicitly marks a task as completed through the TaskUpdate tool, or when an [agent team](/en/agent-teams) teammate finishes its turn with in-progress tasks. Use this to enforce completion criteria like passing tests or lint checks before a task can close.1376Runs when a task is being marked as completed. This fires in two situations: when any agent explicitly marks a task as completed through the TaskUpdate tool, or when an [agent team](/en/agent-teams) teammate finishes its turn with in-progress tasks. Use this to enforce completion criteria like passing tests or lint checks before a task can close.
1200 1377
1201When a `TaskCompleted` hook exits with code 2, the task is not marked as completed and the stderr message is fed back to the model as feedback. TaskCompleted hooks do not support matchers and fire on every occurrence.1378When a `TaskCompleted` hook exits with code 2, the task is not marked as completed and the stderr message is fed back to the model as feedback. To stop the teammate entirely instead of re-running it, return JSON with `{"continue": false, "stopReason": "..."}`. TaskCompleted hooks do not support matchers and fire on every occurrence.
1202 1379
1203#### TaskCompleted input1380#### TaskCompleted input
1204 1381
1229 1406
1230#### TaskCompleted decision control1407#### TaskCompleted decision control
1231 1408
1232TaskCompleted hooks use exit codes only, not JSON decision control. This example runs tests and blocks task completion if they fail:1409TaskCompleted hooks support two ways to control task completion:
1410
1411* **Exit code 2**: the task is not marked as completed and the stderr message is fed back to the model as feedback.
1412* **JSON `{"continue": false, "stopReason": "..."}`**: stops the teammate entirely, matching `Stop` hook behavior. The `stopReason` is shown to the user.
1413
1414This example runs tests and blocks task completion if they fail:
1233 1415
1234```bash theme={null}1416```bash theme={null}
1235#!/bin/bash1417#!/bin/bash
1289 "session_id": "abc123",1471 "session_id": "abc123",
1290 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1472 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1291 "cwd": "/Users/...",1473 "cwd": "/Users/...",
1292 "permission_mode": "default",
1293 "hook_event_name": "ConfigChange",1474 "hook_event_name": "ConfigChange",
1294 "source": "project_settings",1475 "source": "project_settings",
1295 "file_path": "/Users/.../my-project/.claude/settings.json"1476 "file_path": "/Users/.../my-project/.claude/settings.json"
1420 "session_id": "abc123",1601 "session_id": "abc123",
1421 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1602 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1422 "cwd": "/Users/...",1603 "cwd": "/Users/...",
1423 "permission_mode": "default",
1424 "hook_event_name": "PreCompact",1604 "hook_event_name": "PreCompact",
1425 "trigger": "manual",1605 "trigger": "manual",
1426 "custom_instructions": ""1606 "custom_instructions": ""
1427}1607}
1428```1608```
1429 1609
1610### PostCompact
1611
1612Runs after Claude Code completes a compact operation. Use this event to react to the new compacted state, for example to log the generated summary or update external state.
1613
1614The same matcher values apply as for `PreCompact`:
1615
1616| Matcher | When it fires |
1617| :------- | :------------------------------------------------- |
1618| `manual` | After `/compact` |
1619| `auto` | After auto-compact when the context window is full |
1620
1621#### PostCompact input
1622
1623In addition to the [common input fields](#common-input-fields), PostCompact hooks receive `trigger` and `compact_summary`. The `compact_summary` field contains the conversation summary generated by the compact operation.
1624
1625```json theme={null}
1626{
1627 "session_id": "abc123",
1628 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1629 "cwd": "/Users/...",
1630 "hook_event_name": "PostCompact",
1631 "trigger": "manual",
1632 "compact_summary": "Summary of the compacted conversation..."
1633}
1634```
1635
1636PostCompact hooks have no decision control. They cannot affect the compaction result but can perform follow-up tasks.
1637
1430### SessionEnd1638### SessionEnd
1431 1639
1432Runs when a Claude Code session ends. Useful for cleanup tasks, logging session1640Runs when a Claude Code session ends. Useful for cleanup tasks, logging session
1437| Reason | Description |1645| Reason | Description |
1438| :---------------------------- | :----------------------------------------- |1646| :---------------------------- | :----------------------------------------- |
1439| `clear` | Session cleared with `/clear` command |1647| `clear` | Session cleared with `/clear` command |
1648| `resume` | Session switched via interactive `/resume` |
1440| `logout` | User logged out |1649| `logout` | User logged out |
1441| `prompt_input_exit` | User exited while prompt input was visible |1650| `prompt_input_exit` | User exited while prompt input was visible |
1442| `bypass_permissions_disabled` | Bypass permissions mode was disabled |1651| `bypass_permissions_disabled` | Bypass permissions mode was disabled |
1451 "session_id": "abc123",1660 "session_id": "abc123",
1452 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",1661 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1453 "cwd": "/Users/...",1662 "cwd": "/Users/...",
1454 "permission_mode": "default",
1455 "hook_event_name": "SessionEnd",1663 "hook_event_name": "SessionEnd",
1456 "reason": "other"1664 "reason": "other"
1457}1665}
1459 1667
1460SessionEnd hooks have no decision control. They cannot block session termination but can perform cleanup tasks.1668SessionEnd hooks have no decision control. They cannot block session termination but can perform cleanup tasks.
1461 1669
1670SessionEnd hooks have a default timeout of 1.5 seconds. This applies to session exit, `/clear`, and switching sessions via interactive `/resume`. If your hooks need more time, set the `CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS` environment variable to a higher value in milliseconds. Any per-hook `timeout` setting is also capped by this value.
1671
1672```bash theme={null}
1673CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude
1674```
1675
1676### Elicitation
1677
1678Runs when an MCP server requests user input mid-task. By default, Claude Code shows an interactive dialog for the user to respond. Hooks can intercept this request and respond programmatically, skipping the dialog entirely.
1679
1680The matcher field matches against the MCP server name.
1681
1682#### Elicitation input
1683
1684In addition to the [common input fields](#common-input-fields), Elicitation hooks receive `mcp_server_name`, `message`, and optional `mode`, `url`, `elicitation_id`, and `requested_schema` fields.
1685
1686For form-mode elicitation (the most common case):
1687
1688```json theme={null}
1689{
1690 "session_id": "abc123",
1691 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1692 "cwd": "/Users/...",
1693 "permission_mode": "default",
1694 "hook_event_name": "Elicitation",
1695 "mcp_server_name": "my-mcp-server",
1696 "message": "Please provide your credentials",
1697 "mode": "form",
1698 "requested_schema": {
1699 "type": "object",
1700 "properties": {
1701 "username": { "type": "string", "title": "Username" }
1702 }
1703 }
1704}
1705```
1706
1707For URL-mode elicitation (browser-based authentication):
1708
1709```json theme={null}
1710{
1711 "session_id": "abc123",
1712 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1713 "cwd": "/Users/...",
1714 "permission_mode": "default",
1715 "hook_event_name": "Elicitation",
1716 "mcp_server_name": "my-mcp-server",
1717 "message": "Please authenticate",
1718 "mode": "url",
1719 "url": "https://auth.example.com/login"
1720}
1721```
1722
1723#### Elicitation output
1724
1725To respond programmatically without showing the dialog, return a JSON object with `hookSpecificOutput`:
1726
1727```json theme={null}
1728{
1729 "hookSpecificOutput": {
1730 "hookEventName": "Elicitation",
1731 "action": "accept",
1732 "content": {
1733 "username": "alice"
1734 }
1735 }
1736}
1737```
1738
1739| Field | Values | Description |
1740| :-------- | :---------------------------- | :--------------------------------------------------------------- |
1741| `action` | `accept`, `decline`, `cancel` | Whether to accept, decline, or cancel the request |
1742| `content` | object | Form field values to submit. Only used when `action` is `accept` |
1743
1744Exit code 2 denies the elicitation and shows stderr to the user.
1745
1746### ElicitationResult
1747
1748Runs after a user responds to an MCP elicitation. Hooks can observe, modify, or block the response before it is sent back to the MCP server.
1749
1750The matcher field matches against the MCP server name.
1751
1752#### ElicitationResult input
1753
1754In addition to the [common input fields](#common-input-fields), ElicitationResult hooks receive `mcp_server_name`, `action`, and optional `mode`, `elicitation_id`, and `content` fields.
1755
1756```json theme={null}
1757{
1758 "session_id": "abc123",
1759 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1760 "cwd": "/Users/...",
1761 "permission_mode": "default",
1762 "hook_event_name": "ElicitationResult",
1763 "mcp_server_name": "my-mcp-server",
1764 "action": "accept",
1765 "content": { "username": "alice" },
1766 "mode": "form",
1767 "elicitation_id": "elicit-123"
1768}
1769```
1770
1771#### ElicitationResult output
1772
1773To override the user's response, return a JSON object with `hookSpecificOutput`:
1774
1775```json theme={null}
1776{
1777 "hookSpecificOutput": {
1778 "hookEventName": "ElicitationResult",
1779 "action": "decline",
1780 "content": {}
1781 }
1782}
1783```
1784
1785| Field | Values | Description |
1786| :-------- | :---------------------------- | :--------------------------------------------------------------------- |
1787| `action` | `accept`, `decline`, `cancel` | Overrides the user's action |
1788| `content` | object | Overrides form field values. Only meaningful when `action` is `accept` |
1789
1790Exit code 2 blocks the response, changing the effective action to `decline`.
1791
1462## Prompt-based hooks1792## Prompt-based hooks
1463 1793
1464In addition to Bash command hooks (`type: "command"`), Claude Code supports prompt-based hooks (`type: "prompt"`) that use an LLM to evaluate whether to allow or block an action, and agent hooks (`type: "agent"`) that spawn an agentic verifier with tool access. Not all events support every hook type.1794In addition to command and HTTP hooks, Claude Code supports prompt-based hooks (`type: "prompt"`) that use an LLM to evaluate whether to allow or block an action, and agent hooks (`type: "agent"`) that spawn an agentic verifier with tool access. Not all events support every hook type.
1465 1795
1466Events that support all three hook types (`command`, `prompt`, and `agent`):1796Events that support all four hook types (`command`, `http`, `prompt`, and `agent`):
1467 1797
1468* `PermissionRequest`1798* `PermissionRequest`
1469* `PostToolUse`1799* `PostToolUse`
1477Events that only support `type: "command"` hooks:1807Events that only support `type: "command"` hooks:
1478 1808
1479* `ConfigChange`1809* `ConfigChange`
1810* `Elicitation`
1811* `ElicitationResult`
1812* `InstructionsLoaded`
1480* `Notification`1813* `Notification`
1814* `PostCompact`
1481* `PreCompact`1815* `PreCompact`
1482* `SessionEnd`1816* `SessionEnd`
1483* `SessionStart`1817* `SessionStart`
1818* `StopFailure`
1484* `SubagentStart`1819* `SubagentStart`
1485* `TeammateIdle`1820* `TeammateIdle`
1486* `WorktreeCreate`1821* `WorktreeCreate`
1648 1983
1649After the background process exits, if the hook produced a JSON response with a `systemMessage` or `additionalContext` field, that content is delivered to Claude as context on the next conversation turn.1984After the background process exits, if the hook produced a JSON response with a `systemMessage` or `additionalContext` field, that content is delivered to Claude as context on the next conversation turn.
1650 1985
1986Async hook completion notifications are suppressed by default. To see them, enable verbose mode with `Ctrl+O` or start Claude Code with `--verbose`.
1987
1651### Example: run tests after file changes1988### Example: run tests after file changes
1652 1989
1653This hook starts a test suite in the background whenever Claude writes a file, then reports the results back to Claude when the tests finish. Save this script to `.claude/hooks/run-tests-async.sh` in your project and make it executable with `chmod +x`:1990This hook starts a test suite in the background whenever Claude writes a file, then reports the results back to Claude when the tests finish. Save this script to `.claude/hooks/run-tests-async.sh` in your project and make it executable with `chmod +x`:
1711 2048
1712### Disclaimer2049### Disclaimer
1713 2050
1714Hooks run with your system user's full permissions.2051Command hooks run with your system user's full permissions.
1715 2052
1716<Warning>2053<Warning>
1717 Hooks execute shell commands with your full user permissions. They can modify, delete, or access any files your user account can access. Review and test all hook commands before adding them to your configuration.2054 Command hooks execute shell commands with your full user permissions. They can modify, delete, or access any files your user account can access. Review and test all hook commands before adding them to your configuration.
1718</Warning>2055</Warning>
1719 2056
1720### Security best practices2057### Security best practices
1731 2068
1732Run `claude --debug` to see hook execution details, including which hooks matched, their exit codes, and output. Toggle verbose mode with `Ctrl+O` to see hook progress in the transcript.2069Run `claude --debug` to see hook execution details, including which hooks matched, their exit codes, and output. Toggle verbose mode with `Ctrl+O` to see hook progress in the transcript.
1733 2070
1734```2071```text theme={null}
1735[DEBUG] Executing hooks for PostToolUse:Write2072[DEBUG] Executing hooks for PostToolUse:Write
1736[DEBUG] Getting matching hook commands for PostToolUse with query: Write2073[DEBUG] Getting matching hook commands for PostToolUse with query: Write
1737[DEBUG] Found 1 hook matchers in settings2074[DEBUG] Found 1 hook matchers in settings