Hooks reference changedhooks
Nearest release: v2.1.284, published 16 hours after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Upstream edited this page at 28 Sep 2026 00:42 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 28 Sep 2026 23:37 UTC.
Upstream edited
Recorded here
Lines+492added
Lines−492removed
From line
28
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits64to this page, all time
The whole hunk
from line 28, old and new numbered
/
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.
from line 28
2828
2929The 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.
3030
31| Event | When it fires |
32| :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
33| `SessionStart` | When a session begins or resumes |
34| `Setup` | When you start Claude Code with `--init-only`, or with `--init` or `--maintenance` in `-p` mode. For one-time preparation in CI or scripts |
35| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
36| `UserPromptExpansion` | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
37| `PreToolUse` | Before a tool call executes. Can block it |
38| `PermissionRequest` | When a tool call needs a permission decision |
39| `PermissionDenied` | When auto mode denies a tool call, including denials without a classifier verdict. Use JSON `hookSpecificOutput.retry: true` to tell the model it may retry the denied tool call. Claude Code ignores `retry` when the classifier produced no verdict |
40| `PostToolUse` | After a tool call succeeds |
41| `PostToolUseFailure` | After a tool call fails |
42| `PostToolBatch` | After a full batch of parallel tool calls resolves, before the next model call |
43| `Notification` | When Claude Code sends a notification |
44| `MessageDisplay` | While assistant message text is displayed |
45| `SubagentStart` | When a subagent is spawned |
46| `SubagentStop` | When a subagent finishes |
47| `TaskCreated` | When a task is being created via `TaskCreate` |
48| `TaskCompleted` | When a task is being marked as completed |
49| `Stop` | When Claude finishes responding |
50| `StopFailure` | When the turn ends due to an API error |
51| `TeammateIdle` | When an [agent team](/docs/en/agent-teams) teammate is about to go idle |
52| `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 |
53| `ConfigChange` | When a configuration file changes during a session |
54| `CwdChanged` | When the working directory changes, for example when Claude executes a `cd` command. Useful for reactive environment management with tools like direnv |
55| `DirectoryAdded` | When a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request |
56| `FileChanged` | When a watched file changes on disk. The `matcher` field specifies which filenames to watch |
57| `WorktreeCreate` | When a worktree is being created via `--worktree`, `isolation: "worktree"`, or for a background session. Replaces default git behavior |
58| `WorktreeRemove` | When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session |
59| `PreCompact` | Before context compaction |
60| `PostCompact` | After context compaction completes |
61| `PreModelSwitch` | Before Claude Code applies a model switch that you or a client requested. Can block the switch |
62| `PostModelSwitch` | After the session's model changes, including changes Claude Code makes on its own, such as restoring the model when you resume a session |
63| `Elicitation` | When an MCP server requests user input during a tool call |
64| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
65| `SessionEnd` | When a session terminates |
31| Event | When it fires |
32| :- | :- |
33| `SessionStart` | When a session begins or resumes |
34| `Setup` | When you start Claude Code with `--init-only`, or with `--init` or `--maintenance` in `-p` mode. For one-time preparation in CI or scripts |
35| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
36| `UserPromptExpansion` | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
37| `PreToolUse` | Before a tool call executes. Can block it |
38| `PermissionRequest` | When a tool call needs a permission decision |
39| `PermissionDenied` | When auto mode denies a tool call, including denials without a classifier verdict. Use JSON `hookSpecificOutput.retry: true` to tell the model it may retry the denied tool call. Claude Code ignores `retry` when the classifier produced no verdict |
40| `PostToolUse` | After a tool call succeeds |
41| `PostToolUseFailure` | After a tool call fails |
42| `PostToolBatch` | After a full batch of parallel tool calls resolves, before the next model call |
43| `Notification` | When Claude Code sends a notification |
44| `MessageDisplay` | While assistant message text is displayed |
45| `SubagentStart` | When a subagent is spawned |
46| `SubagentStop` | When a subagent finishes |
47| `TaskCreated` | When a task is being created via `TaskCreate` |
48| `TaskCompleted` | When a task is being marked as completed |
49| `Stop` | When Claude finishes responding |
50| `StopFailure` | When the turn ends due to an API error |
51| `TeammateIdle` | When an [agent team](/docs/en/agent-teams) teammate is about to go idle |
52| `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 |
53| `ConfigChange` | When a configuration file changes during a session |
54| `CwdChanged` | When the working directory changes, for example when Claude executes a `cd` command. Useful for reactive environment management with tools like direnv |
55| `DirectoryAdded` | When a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request |
56| `FileChanged` | When a watched file changes on disk. The `matcher` field specifies which filenames to watch |
57| `WorktreeCreate` | When a worktree is being created via `--worktree`, `isolation: "worktree"`, or for a background session. Replaces default git behavior |
58| `WorktreeRemove` | When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session |
59| `PreCompact` | Before context compaction |
60| `PostCompact` | After context compaction completes |
61| `PreModelSwitch` | Before Claude Code applies a model switch that you or a client requested. Can block the switch |
62| `PostModelSwitch` | After the session's model changes, including changes Claude Code makes on its own, such as restoring the model when you resume a session |
63| `Elicitation` | When an MCP server requests user input during a tool call |
64| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
65| `SessionEnd` | When a session terminates |
6666
6767### How a hook resolves
6868
from line 246
246246
247247Where you define a hook determines its scope:
248248
249| Location | Scope | Shareable |
250| :------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------- |
251| `~/.claude/settings.json` | All your projects | No, local to your machine |
252| `.claude/settings.json` | Single project | Yes, can be committed to the repo |
253| `.claude/settings.local.json` | Single project | No, gitignored when Claude Code saves a setting to it |
254| Managed policy settings | Organization-wide | Yes, admin-controlled |
255| [Plugin](/docs/en/plugins/overview) `hooks/hooks.json` | When plugin is enabled | Yes, bundled with the plugin |
256| [Skill](/docs/en/skills) frontmatter | The rest of the session once the skill is invoked. See [Hooks in skills and agents](#hooks-in-skills-and-agents) | Yes, defined in the skill file |
257| [Subagent](/docs/en/sub-agents) frontmatter | While that subagent is running | Yes, defined in the subagent file |
249| Location | Scope | Shareable |
250| :- | :- | :- |
251| `~/.claude/settings.json` | All your projects | No, local to your machine |
252| `.claude/settings.json` | Single project | Yes, can be committed to the repo |
253| `.claude/settings.local.json` | Single project | No, gitignored when Claude Code saves a setting to it |
254| Managed policy settings | Organization-wide | Yes, admin-controlled |
255| [Plugin](/docs/en/plugins/overview) `hooks/hooks.json` | When plugin is enabled | Yes, bundled with the plugin |
256| [Skill](/docs/en/skills) frontmatter | The rest of the session once the skill is invoked. See [Hooks in skills and agents](#hooks-in-skills-and-agents) | Yes, defined in the skill file |
257| [Subagent](/docs/en/sub-agents) frontmatter | While that subagent is running | Yes, defined in the subagent file |
258258
259259[Cloud sessions](/docs/en/claude-code-on-the-web) don't read your local `~/.claude/settings.json`. In a [self-hosted environment](/docs/en/self-hosted-environments-configuration#permissions-and-tool-approval), Claude Code also runs the hooks the operator seeded from the runner host's `~/.claude/`, and it runs the hooks in the runner image's managed settings file when that file is among the [managed sources Claude Code applies](/docs/en/managed-settings#how-claude-code-combines-managed-sources), which by default means only when neither server-managed settings nor an MDM-delivered Claude Code policy supplies the managed tier. See [what carries over from your setup](/docs/en/cloud-environments#what-carries-over-from-your-setup) for which settings files and plugins, and so which hooks, reach a cloud session.
260260
from line 282
282282
283283The `matcher` field filters when hooks fire. How a matcher is evaluated depends on the characters it contains:
284284
285| Matcher value | Evaluated as | Example |
286| :---------------------------------------------------- | :--------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- |
287| `"*"`, `""`, or omitted | Match all | fires on every occurrence of the event |
285| Matcher value | Evaluated as | Example |
286| :- | :- | :- |
287| `"*"`, `""`, or omitted | Match all | fires on every occurrence of the event |
288288| Only letters, digits, `_`, `-`, spaces, `,`, and `\|` | Exact string, or list of exact strings separated by `\|` or `,` with optional surrounding whitespace | `Bash` matches only the Bash tool; `Edit\|Write` and `Edit, Write` each match either tool exactly; `code-reviewer` matches only that agent type |
289| Contains any other character | JavaScript regular expression, unanchored | `^Notebook` matches any tool whose name starts with `Notebook`; `mcp__memory__.*` matches every tool from the `memory` server |
289| Contains any other character | JavaScript regular expression, unanchored | `^Notebook` matches any tool whose name starts with `Notebook`; `mcp__memory__.*` matches every tool from the `memory` server |
290290
291291A matcher on the regular-expression path is tested with JavaScript's `RegExp.prototype.test`, which succeeds on a match anywhere in the value. `Edit.*` matches both `Edit` and `NotebookEdit`; wrap the pattern in `^` and `$`, as in `^Edit$`, when you need a whole-string match.
292292
from line 298
298298
299299Each event type matches on a different field:
300300
301| Event | What the matcher filters | Example matcher values |
302| :------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
303| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |
304| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact`, `fork` |
305| `Setup` | which CLI flag triggered setup | `init`, `maintenance` |
306| `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `other` |
307| `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`, `elicitation_url_dialog`, `elicitation_complete`, `elicitation_response`, `agent_needs_input`, `agent_completed`, `quota_auto_resume_fired`, `quota_auto_resume_stale`, `quota_auto_resume_disabled` |
308| `SubagentStart` | agent type | `general-purpose`, `Explore`, `Plan`, custom agent names, or plugin-scoped names like `^my-plugin:reviewer$` |
309| `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |
310| `PreModelSwitch`, `PostModelSwitch` | canonical name of the model the session switches to, as described under [PreModelSwitch](#premodelswitch) | `claude-opus-5`, `claude-opus-4-6\|claude-opus-5`, `.*opus.*` |
311| `SubagentStop` | agent type | same values as `SubagentStart` |
312| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` |
313| `CwdChanged` | no matcher support | always fires on every occurrence |
314| `DirectoryAdded` | how the directory was added | `slash_command`, `register_repo_root` |
315| `FileChanged` | literal filenames to watch (see [FileChanged](#filechanged)) | `.envrc\|.env` |
316| `StopFailure` | error type | `rate_limit`, `overloaded`, `authentication_failed`, `oauth_org_not_allowed`, `account_on_hold`, `billing_error`, `invalid_request`, `model_not_found`, `server_error`, `max_output_tokens`, `cloud_credential_error`, `unknown` |
317| `InstructionsLoaded` | load reason | `session_start`, `nested_traversal`, `path_glob_match`, `include`, `compact` |
318| `UserPromptExpansion` | command name | your skill or command names |
319| `Elicitation` | MCP server name | your configured MCP server names |
320| `ElicitationResult` | MCP server name | same values as `Elicitation` |
321| `UserPromptSubmit`, `PostToolBatch`, `Stop`, `TeammateIdle`, `TaskCreated`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, `MessageDisplay` | no matcher support | always fires on every occurrence |
301| Event | What the matcher filters | Example matcher values |
302| :- | :- | :- |
303| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |
304| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact`, `fork` |
305| `Setup` | which CLI flag triggered setup | `init`, `maintenance` |
306| `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `other` |
307| `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`, `elicitation_url_dialog`, `elicitation_complete`, `elicitation_response`, `agent_needs_input`, `agent_completed`, `quota_auto_resume_fired`, `quota_auto_resume_stale`, `quota_auto_resume_disabled` |
308| `SubagentStart` | agent type | `general-purpose`, `Explore`, `Plan`, custom agent names, or plugin-scoped names like `^my-plugin:reviewer$` |
309| `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |
310| `PreModelSwitch`, `PostModelSwitch` | canonical name of the model the session switches to, as described under [PreModelSwitch](#premodelswitch) | `claude-opus-5`, `claude-opus-4-6\|claude-opus-5`, `.*opus.*` |
311| `SubagentStop` | agent type | same values as `SubagentStart` |
312| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` |
313| `CwdChanged` | no matcher support | always fires on every occurrence |
314| `DirectoryAdded` | how the directory was added | `slash_command`, `register_repo_root` |
315| `FileChanged` | literal filenames to watch (see [FileChanged](#filechanged)) | `.envrc\|.env` |
316| `StopFailure` | error type | `rate_limit`, `overloaded`, `authentication_failed`, `oauth_org_not_allowed`, `account_on_hold`, `billing_error`, `invalid_request`, `model_not_found`, `server_error`, `max_output_tokens`, `cloud_credential_error`, `unknown` |
317| `InstructionsLoaded` | load reason | `session_start`, `nested_traversal`, `path_glob_match`, `include`, `compact` |
318| `UserPromptExpansion` | command name | your skill or command names |
319| `Elicitation` | MCP server name | your configured MCP server names |
320| `ElicitationResult` | MCP server name | same values as `Elicitation` |
321| `UserPromptSubmit`, `PostToolBatch`, `Stop`, `TeammateIdle`, `TaskCreated`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, `MessageDisplay` | no matcher support | always fires on every occurrence |
322322
323323Matching `StopFailure` on `cloud_credential_error` requires Claude Code v2.1.267 or later, the first version that reports credential-load failures under that value rather than `server_error` or `unknown`.
324324
from line 417
417417
418418These fields apply to all hook types:
419419
420| Field | Required | Description |
421| :-------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
422| `type` | yes | `"command"`, `"http"`, `"mcp_tool"`, `"prompt"`, or `"agent"` |
423| `if` | no | Permission rule syntax to filter when this hook runs, such as `"Bash(git *)"` or `"Edit(*.ts)"`. The hook command only runs if the tool call matches the pattern. See the [Bash matching table](#bash-if-matching) below for how Bash patterns evaluate against subcommands, `$()`, and backticks. Only evaluated on tool events: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, and `PermissionDenied`. On other events, a hook with `if` set never runs. Uses the same syntax as [permission rules](/docs/en/permissions) |
424| `timeout` | no | Seconds before canceling. Claude Code doesn't enforce it on a command hook you run with [`async: true`](#run-hooks-in-the-background). Defaults: 600 for `command`, `http`, and `mcp_tool`; 30 for `prompt`; 60 for `agent`. Claude Code lowers the `command`, `http`, and `mcp_tool` default to 30 on [`UserPromptSubmit`](#userpromptsubmit), [`PreModelSwitch`](#premodelswitch), and [`PostModelSwitch`](#postmodelswitch), and to 10 on [`MessageDisplay`](#messagedisplay). [`SessionEnd`](#sessionend) hooks share a 1.5-second budget; if your settings set a longer per-hook `timeout`, Claude Code raises the budget to match, up to 60 seconds |
425| `statusMessage` | no | Custom spinner message displayed while the hook runs |
426| `once` | no | If `true`, Claude Code removes the hook after its first successful run. A run that fails, blocks with exit code 2, or times out leaves the hook in place, so it runs again on the next matching event. Only honored for hooks declared in [skill frontmatter](#hooks-in-skills-and-agents); ignored in settings files and agent frontmatter |
420| Field | Required | Description |
421| :- | :- | :- |
422| `type` | yes | `"command"`, `"http"`, `"mcp_tool"`, `"prompt"`, or `"agent"` |
423| `if` | no | Permission rule syntax to filter when this hook runs, such as `"Bash(git *)"` or `"Edit(*.ts)"`. The hook command only runs if the tool call matches the pattern. See the [Bash matching table](#bash-if-matching) below for how Bash patterns evaluate against subcommands, `$()`, and backticks. Only evaluated on tool events: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, and `PermissionDenied`. On other events, a hook with `if` set never runs. Uses the same syntax as [permission rules](/docs/en/permissions) |
424| `timeout` | no | Seconds before canceling. Claude Code doesn't enforce it on a command hook you run with [`async: true`](#run-hooks-in-the-background). Defaults: 600 for `command`, `http`, and `mcp_tool`; 30 for `prompt`; 60 for `agent`. Claude Code lowers the `command`, `http`, and `mcp_tool` default to 30 on [`UserPromptSubmit`](#userpromptsubmit), [`PreModelSwitch`](#premodelswitch), and [`PostModelSwitch`](#postmodelswitch), and to 10 on [`MessageDisplay`](#messagedisplay). [`SessionEnd`](#sessionend) hooks share a 1.5-second budget; if your settings set a longer per-hook `timeout`, Claude Code raises the budget to match, up to 60 seconds |
425| `statusMessage` | no | Custom spinner message displayed while the hook runs |
426| `once` | no | If `true`, Claude Code removes the hook after its first successful run. A run that fails, blocks with exit code 2, or times out leaves the hook in place, so it runs again on the next matching event. Only honored for hooks declared in [skill frontmatter](#hooks-in-skills-and-agents); ignored in settings files and agent frontmatter |
427427
428428The `if` field holds exactly one permission rule. There is no `&&`, `||`, or list syntax for combining rules; to apply multiple conditions, define a separate hook handler for each.
429429
from line 431
431431
432432<span id="bash-if-matching" />For Bash patterns, whether your hook command runs depends on the shape of the pattern and the Bash command Claude is invoking. Leading `VAR=value` assignments are stripped before matching.
433433
434| `if` pattern | Bash command | Hook runs? | Why |
435| :----------------- | :-------------------------- | :--------- | :------------------------------------------------------------------------------------------------------------------------ |
436| `Bash(git *)` | `FOO=bar git push` | yes | leading assignments are stripped; `git push` matches |
437| `Bash(git *)` | `npm test && git push` | yes | each subcommand is checked; `git push` matches |
438| `Bash(rm *)` | `echo $(rm -rf /)` | yes | commands inside `$()` and backticks are checked; `rm -rf /` matches |
439| `Bash(rm *)` | `echo $(date)` | no | no subcommand matches `rm *` |
440| `Bash(cat *)` | `echo before $(date) after` | no | a substitution can sit at any argument position, so the full command and `date` are both checked; neither matches `cat *` |
441| `Bash(git *)` | `$TOOL git push` | yes | Claude Code can't tell what the command name expands to, so it runs the hook |
442| `Bash(git push *)` | `echo $(date)` | yes | patterns that specify more than the command name run the hook anyway on `$()`, backticks, or `$VAR` |
434| `if` pattern | Bash command | Hook runs? | Why |
435| :- | :- | :- | :- |
436| `Bash(git *)` | `FOO=bar git push` | yes | leading assignments are stripped; `git push` matches |
437| `Bash(git *)` | `npm test && git push` | yes | each subcommand is checked; `git push` matches |
438| `Bash(rm *)` | `echo $(rm -rf /)` | yes | commands inside `$()` and backticks are checked; `rm -rf /` matches |
439| `Bash(rm *)` | `echo $(date)` | no | no subcommand matches `rm *` |
440| `Bash(cat *)` | `echo before $(date) after` | no | a substitution can sit at any argument position, so the full command and `date` are both checked; neither matches `cat *` |
441| `Bash(git *)` | `$TOOL git push` | yes | Claude Code can't tell what the command name expands to, so it runs the hook |
442| `Bash(git push *)` | `echo $(date)` | yes | patterns that specify more than the command name run the hook anyway on `$()`, backticks, or `$VAR` |
443443
444444When Claude Code can't determine which commands the Bash input runs, it runs your hook regardless of the pattern. Because the `if` filter is best-effort, use the [permission system](/docs/en/permissions) rather than a hook to enforce a hard allow or deny.
445445
from line 447
447447
448448In addition to the [common fields](#common-fields), command hooks accept these fields:
449449
450| Field | Required | Description |
451| :------------ | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
452| `command` | yes | Shell command to execute. With `args`, the executable to spawn directly. See [Exec form and shell form](#exec-form-and-shell-form) |
453| `args` | no | Argument list. When present, `command` is resolved as an executable and spawned directly with `args` as the argument vector, with no shell involved. See [Exec form and shell form](#exec-form-and-shell-form) |
454| `async` | no | If `true`, runs in the background without blocking. See [Run hooks in the background](#run-hooks-in-the-background) |
455| `asyncRewake` | no | If `true`, runs in the background and wakes Claude on exit code 2. The hook's stderr, or stdout if stderr is empty, is shown to Claude as a [system reminder](/docs/en/glossary#system-reminder) so it can react to a long-running background failure |
456| `shell` | no | Shell to use for this hook. Accepts `"bash"` or `"powershell"`. Defaults to `"bash"`, or to `"powershell"` on Windows when Git Bash isn't installed. Setting `"powershell"` runs the command via PowerShell on Windows. Does not require `CLAUDE_CODE_USE_POWERSHELL_TOOL` since hooks spawn PowerShell directly. Ignored when `args` is set |
450| Field | Required | Description |
451| :- | :- | :- |
452| `command` | yes | Shell command to execute. With `args`, the executable to spawn directly. See [Exec form and shell form](#exec-form-and-shell-form) |
453| `args` | no | Argument list. When present, `command` is resolved as an executable and spawned directly with `args` as the argument vector, with no shell involved. See [Exec form and shell form](#exec-form-and-shell-form) |
454| `async` | no | If `true`, runs in the background without blocking. See [Run hooks in the background](#run-hooks-in-the-background) |
455| `asyncRewake` | no | If `true`, runs in the background and wakes Claude on exit code 2. The hook's stderr, or stdout if stderr is empty, is shown to Claude as a [system reminder](/docs/en/glossary#system-reminder) so it can react to a long-running background failure |
456| `shell` | no | Shell to use for this hook. Accepts `"bash"` or `"powershell"`. Defaults to `"bash"`, or to `"powershell"` on Windows when Git Bash isn't installed. Setting `"powershell"` runs the command via PowerShell on Windows. Does not require `CLAUDE_CODE_USE_POWERSHELL_TOOL` since hooks spawn PowerShell directly. Ignored when `args` is set |
457457
458458<a id="exec-form-and-shell-form" />
459459
from line 502
502502
503503In addition to the [common fields](#common-fields), HTTP hooks accept these fields:
504504
505| Field | Required | Description |
506| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
507| `url` | yes | URL to send the POST request to |
508| `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 |
509| `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 |
505| Field | Required | Description |
506| :- | :- | :- |
507| `url` | yes | URL to send the POST request to |
508| `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 |
509| `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 |
510510
511511Claude 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.
512512
from line 541
541541
542542In addition to the [common fields](#common-fields), MCP tool hooks accept these fields:
543543
544| Field | Required | Description |
545| :------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
546| `server` | yes | Name of a configured MCP server. For a [plugin-bundled server](/docs/en/mcp#plugin-provided-mcp-servers), this is the scoped name `plugin:<plugin-name>:<server-name>`, such as `plugin:my-plugin:db`, not the bare server key |
547| `tool` | yes | Name of the tool to call on that server |
548| `input` | no | Arguments passed to the tool. String values support `${path}` substitution from the hook's [JSON input](#hook-input-and-output), such as `"${tool_input.file_path}"` |
544| Field | Required | Description |
545| :- | :- | :- |
546| `server` | yes | Name of a configured MCP server. For a [plugin-bundled server](/docs/en/mcp#plugin-provided-mcp-servers), this is the scoped name `plugin:<plugin-name>:<server-name>`, such as `plugin:my-plugin:db`, not the bare server key |
547| `tool` | yes | Name of the tool to call on that server |
548| `input` | no | Arguments passed to the tool. String values support `${path}` substitution from the hook's [JSON input](#hook-input-and-output), such as `"${tool_input.file_path}"` |
549549
550550This example calls the `security_scan` tool on the `my_server` MCP server after each `Write` or `Edit`, passing the edited file's path:
551551
from line 587
587587
588588In addition to the [common fields](#common-fields), prompt and agent hooks accept these fields:
589589
590| Field | Required | Description |
591| :------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
592| `prompt` | yes | Prompt text to send to the model. Use `$ARGUMENTS` as a placeholder for the hook input JSON. Escape with a backslash to include literal text: `\$1.00` renders as `$1.00` |
593| `model` | no | Model to use for evaluation. Defaults to a fast model |
590| Field | Required | Description |
591| :- | :- | :- |
592| `prompt` | yes | Prompt text to send to the model. Use `$ARGUMENTS` as a placeholder for the hook input JSON. Escape with a backslash to include literal text: `\$1.00` renders as `$1.00` |
593| `model` | no | Model to use for evaluation. Defaults to a fast model |
594594
595595### Reference scripts by path
596596
from line 727
727727
728728Hook 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.
729729
730| Field | Description |
731| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
732| `session_id` | Current session identifier |
733| `prompt_id` | UUID identifying the user prompt currently being processed. Matches the [`prompt.id` attribute on OpenTelemetry events](/docs/en/monitoring-usage#event-correlation-attributes), so you can correlate hook output with telemetry for a single prompt. Absent until the first user input. Requires Claude Code v2.1.196 or later |
734| `transcript_path` | Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires. Hooks that need the final assistant text of the current turn should use `last_assistant_message` on [Stop](#stop) and [SubagentStop](#subagentstop) instead of reading the transcript |
735| `cwd` | Current working directory when the hook is invoked |
736| `scratchpad_dir` | Path to the session's [scratchpad directory](/docs/en/claude-directory#session-scratchpad-directory), where Claude keeps temporary working files. Absent when the session has no scratchpad or the temp directory is unavailable. Requires Claude Code v2.1.257 or later |
737| `permission_mode` | Current [permission mode](/docs/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"auto"`, `"dontAsk"`, or `"bypassPermissions"`. The mode labeled **Manual** arrives as `"default"`, never as `"manual"`, so scripts that match `"default"` keep working. Not all events receive this field. Check the JSON example in each [hook event](#hook-events) section |
738| `effort` | Object with a `level` field holding the [effort level](/docs/en/model-config#adjust-effort-level) in effect when the hook runs: `"low"`, `"medium"`, `"high"`, `"xhigh"`, or `"max"`. If you set a level the active model doesn't support, `level` reports the level Claude Code ran instead; [Adjust effort level](/docs/en/model-config#adjust-effort-level) says how it picks that level. Ultracode is not a distinct level and reports as `"xhigh"`. The object matches the [status line](/docs/en/statusline#available-data) `effort` field. Present for events that fire within a tool-use context, such as `PreToolUse`, `PostToolUse`, `Stop`, and `SubagentStop`, when the current model supports the effort parameter. The level is also available to hook commands and the Bash tool as the `$CLAUDE_EFFORT` environment variable. |
739| `hook_event_name` | Name of the event that fired |
730| Field | Description |
731| :- | :- |
732| `session_id` | Current session identifier |
733| `prompt_id` | UUID identifying the user prompt currently being processed. Matches the [`prompt.id` attribute on OpenTelemetry events](/docs/en/monitoring-usage#event-correlation-attributes), so you can correlate hook output with telemetry for a single prompt. Absent until the first user input. Requires Claude Code v2.1.196 or later |
734| `transcript_path` | Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires. Hooks that need the final assistant text of the current turn should use `last_assistant_message` on [Stop](#stop) and [SubagentStop](#subagentstop) instead of reading the transcript |
735| `cwd` | Current working directory when the hook is invoked |
736| `scratchpad_dir` | Path to the session's [scratchpad directory](/docs/en/claude-directory#session-scratchpad-directory), where Claude keeps temporary working files. Absent when the session has no scratchpad or the temp directory is unavailable. Requires Claude Code v2.1.257 or later |
737| `permission_mode` | Current [permission mode](/docs/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"auto"`, `"dontAsk"`, or `"bypassPermissions"`. The mode labeled **Manual** arrives as `"default"`, never as `"manual"`, so scripts that match `"default"` keep working. Not all events receive this field. Check the JSON example in each [hook event](#hook-events) section |
738| `effort` | Object with a `level` field holding the [effort level](/docs/en/model-config#adjust-effort-level) in effect when the hook runs: `"low"`, `"medium"`, `"high"`, `"xhigh"`, or `"max"`. If you set a level the active model doesn't support, `level` reports the level Claude Code ran instead; [Adjust effort level](/docs/en/model-config#adjust-effort-level) says how it picks that level. Ultracode is not a distinct level and reports as `"xhigh"`. The object matches the [status line](/docs/en/statusline#available-data) `effort` field. Present for events that fire within a tool-use context, such as `PreToolUse`, `PostToolUse`, `Stop`, and `SubagentStop`, when the current model supports the effort parameter. The level is also available to hook commands and the Bash tool as the `$CLAUDE_EFFORT` environment variable. |
739| `hook_event_name` | Name of the event that fired |
740740
741741When running with `--agent` or inside a subagent, two additional fields are included:
742742
743| Field | Description |
744| :----------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
745| `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. |
743| Field | Description |
744| :- | :- |
745| `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. |
746746| `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. See [SubagentStart](#subagentstart) for the values custom and plugin subagents report and how to write a matcher against a plugin-scoped name. |
747747
748748Only [`SessionStart`](#sessionstart) hooks can receive a `model` field, and Claude Code doesn't always include it. [`PreModelSwitch`](#premodelswitch) and [`PostModelSwitch`](#postmodelswitch) hooks receive `from_model` and `to_model` instead, so use a PostModelSwitch hook to follow the model as it changes during a session.
from line 855
855855
856856Exit code 2 is the way a hook signals "stop, don't do this." The effect depends on the event, because some events represent actions that can be blocked (like a tool call that hasn't happened yet) and others represent things that already happened or can't be prevented.
857857
858| Hook event | Can block? | What happens on exit 2 |
859| :-------------------- | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
860| `PreToolUse` | Yes | Blocks the tool call |
861| `PermissionRequest` | No | Exit code 2 isn't honored for this event and the permission flow proceeds unchanged. Deny through the [`decision` object](#permissionrequest-decision-control) instead |
862| `UserPromptSubmit` | Yes | Blocks prompt processing and erases the prompt |
863| `UserPromptExpansion` | Yes | Blocks the expansion |
864| `Stop` | Yes | Prevents Claude from stopping, continues the conversation |
865| `SubagentStop` | Yes | Prevents the subagent from stopping |
866| `TeammateIdle` | Yes | Prevents the teammate from going idle, so it continues working |
867| `TaskCreated` | Yes | Rolls back the task creation |
868| `TaskCompleted` | Yes | Prevents the task from being marked as completed |
869| `ConfigChange` | Yes | Blocks the configuration change from taking effect (except `policy_settings`) |
870| `StopFailure` | No | Output and exit code are ignored, except `terminalSequence` |
871| `PostToolUse` | No | Shows stderr to Claude; the tool already ran |
872| `PostToolUseFailure` | No | Shows stderr to Claude; the tool already failed |
873| `PostToolBatch` | Yes | Stops the agentic loop before the next model call |
874| `PermissionDenied` | No | Exit code and stderr are ignored because the denial already occurred. Use JSON `hookSpecificOutput.retry: true` to tell the model it may retry; Claude Code ignores `retry: true` for [no-verdict denials](#permissiondenied-decision-control) |
875| `Notification` | No | Exit code and stderr are ignored |
876| `SubagentStart` | No | Shows stderr to user only |
877| `SessionStart` | No | Shows stderr to user only |
878| `Setup` | No | Exit code and stderr are ignored |
879| `SessionEnd` | No | Shows stderr to user only |
880| `CwdChanged` | No | Shows stderr to user only |
881| `DirectoryAdded` | No | Stderr goes to the debug log; the directory is already added |
882| `FileChanged` | No | Shows stderr to user only |
883| `PreCompact` | Yes | Blocks compaction |
884| `PostCompact` | No | Shows stderr to user only |
885| `PreModelSwitch` | Yes | Blocks the model switch and shows stderr to the user |
886| `PostModelSwitch` | No | Shows stderr to user only; the model already switched |
887| `Elicitation` | Yes | Denies the elicitation |
888| `ElicitationResult` | Yes | Blocks the response (action becomes decline) |
889| `WorktreeCreate` | Yes | Any non-zero exit code causes worktree creation to fail |
890| `WorktreeRemove` | Yes | Any non-zero exit code causes worktree removal to fail if the directory still exists afterward. See [WorktreeRemove](#worktreeremove) for what happens to the directory |
891| `InstructionsLoaded` | No | Exit code is ignored |
892| `MessageDisplay` | No | The original text is displayed |
858| Hook event | Can block? | What happens on exit 2 |
859| :- | :- | :- |
860| `PreToolUse` | Yes | Blocks the tool call |
861| `PermissionRequest` | No | Exit code 2 isn't honored for this event and the permission flow proceeds unchanged. Deny through the [`decision` object](#permissionrequest-decision-control) instead |
862| `UserPromptSubmit` | Yes | Blocks prompt processing and erases the prompt |
863| `UserPromptExpansion` | Yes | Blocks the expansion |
864| `Stop` | Yes | Prevents Claude from stopping, continues the conversation |
865| `SubagentStop` | Yes | Prevents the subagent from stopping |
866| `TeammateIdle` | Yes | Prevents the teammate from going idle, so it continues working |
867| `TaskCreated` | Yes | Rolls back the task creation |
868| `TaskCompleted` | Yes | Prevents the task from being marked as completed |
869| `ConfigChange` | Yes | Blocks the configuration change from taking effect (except `policy_settings`) |
870| `StopFailure` | No | Output and exit code are ignored, except `terminalSequence` |
871| `PostToolUse` | No | Shows stderr to Claude; the tool already ran |
872| `PostToolUseFailure` | No | Shows stderr to Claude; the tool already failed |
873| `PostToolBatch` | Yes | Stops the agentic loop before the next model call |
874| `PermissionDenied` | No | Exit code and stderr are ignored because the denial already occurred. Use JSON `hookSpecificOutput.retry: true` to tell the model it may retry; Claude Code ignores `retry: true` for [no-verdict denials](#permissiondenied-decision-control) |
875| `Notification` | No | Exit code and stderr are ignored |
876| `SubagentStart` | No | Shows stderr to user only |
877| `SessionStart` | No | Shows stderr to user only |
878| `Setup` | No | Exit code and stderr are ignored |
879| `SessionEnd` | No | Shows stderr to user only |
880| `CwdChanged` | No | Shows stderr to user only |
881| `DirectoryAdded` | No | Stderr goes to the debug log; the directory is already added |
882| `FileChanged` | No | Shows stderr to user only |
883| `PreCompact` | Yes | Blocks compaction |
884| `PostCompact` | No | Shows stderr to user only |
885| `PreModelSwitch` | Yes | Blocks the model switch and shows stderr to the user |
886| `PostModelSwitch` | No | Shows stderr to user only; the model already switched |
887| `Elicitation` | Yes | Denies the elicitation |
888| `ElicitationResult` | Yes | Blocks the response (action becomes decline) |
889| `WorktreeCreate` | Yes | Any non-zero exit code causes worktree creation to fail |
890| `WorktreeRemove` | Yes | Any non-zero exit code causes worktree removal to fail if the directory still exists afterward. See [WorktreeRemove](#worktreeremove) for what happens to the directory |
891| `InstructionsLoaded` | No | Exit code is ignored |
892| `MessageDisplay` | No | The original text is displayed |
893893
894894For `SessionStart`, `SubagentStart`, and `PostModelSwitch`, Claude Code renders the exit code 2 stderr in the transcript as a `<hook name> hook error` notice, the same way it renders a [non-blocking error](#exit-code-output). Claude doesn't see it, and the session or subagent proceeds. For `SubagentStart`, the notice appears in the subagent's own transcript, not in the parent conversation.
895895
from line 928
928928* **Top-level `decision` and `reason`** are used by some events to block or provide feedback.
929929* **`hookSpecificOutput`** is a nested object for events that need richer control. It requires a `hookEventName` field set to the event name.
930930
931| Field | Default | Description |
932| :----------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
933| `continue` | `true` | If `false`, Claude stops processing entirely after the hook runs. Takes precedence over any event-specific decision fields |
934| `stopReason` | none | Message shown to the user when `continue` is `false`. It stays in the conversation, so Claude sees it if the conversation continues |
935| `suppressOutput` | `false` | Has no effect: Claude Code accepts the field but doesn't act on it. A successful hook's stdout is never shown in the transcript and is recorded in the debug log |
936| `systemMessage` | none | Warning message shown to the user. In [Agent SDK](/docs/en/agent-sdk/overview) and [`--output-format stream-json`](/docs/en/headless) output, it can arrive as an [`SDKInformationalMessage`](/docs/en/agent-sdk/typescript#sdkinformationalmessage) |
937| `terminalSequence` | none | A terminal escape sequence for Claude Code to emit on your behalf, such as a desktop notification, window title, or bell. Restricted to OSC `0`/`1`/`2`/`9`/`99`/`777` and BEL. If the value contains anything outside the allowlist, the field is ignored. Use this instead of writing to `/dev/tty`, which is unavailable to hooks |
931| Field | Default | Description |
932| :- | :- | :- |
933| `continue` | `true` | If `false`, Claude stops processing entirely after the hook runs. Takes precedence over any event-specific decision fields |
934| `stopReason` | none | Message shown to the user when `continue` is `false`. It stays in the conversation, so Claude sees it if the conversation continues |
935| `suppressOutput` | `false` | Has no effect: Claude Code accepts the field but doesn't act on it. A successful hook's stdout is never shown in the transcript and is recorded in the debug log |
936| `systemMessage` | none | Warning message shown to the user. In [Agent SDK](/docs/en/agent-sdk/overview) and [`--output-format stream-json`](/docs/en/headless) output, it can arrive as an [`SDKInformationalMessage`](/docs/en/agent-sdk/typescript#sdkinformationalmessage) |
937| `terminalSequence` | none | A terminal escape sequence for Claude Code to emit on your behalf, such as a desktop notification, window title, or bell. Restricted to OSC `0`/`1`/`2`/`9`/`99`/`777` and BEL. If the value contains anything outside the allowlist, the field is ignored. Use this instead of writing to `/dev/tty`, which is unavailable to hooks |
938938
939939To stop Claude entirely:
940940
from line 1020
10201020
10211021Not 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:
10221022
1023| Events | Decision pattern | Key fields |
1024| :---------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1025| UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact | Top-level `decision` | `decision: "block"`, `reason`. Stop and SubagentStop also accept `hookSpecificOutput.additionalContext` for [non-error feedback that continues the conversation](#stop-decision-control) |
1026| 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; [TaskCompleted ignores it when the `TaskUpdate` tool triggered the event](#taskcompleted-decision-control) |
1027| TaskCreated | Exit code or top-level `decision` | Exit code 2 or `decision: "block"` [cancels the task](#taskcreated-decision-control) and returns the message to Claude. `continue: false` is ignored |
1028| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask/defer), `permissionDecisionReason` |
1029| PreModelSwitch | `hookSpecificOutput` or top-level `decision` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason`. `decision: "block"` also [cancels the switch](#premodelswitch-decision-control) |
1030| PermissionRequest | `hookSpecificOutput` | `decision.behavior` (allow/deny) |
1031| PermissionDenied | `hookSpecificOutput` | `retry: true` tells the model it may retry the denied tool call; Claude Code ignores it for [no-verdict denials](#permissiondenied-decision-control) |
1032| WorktreeCreate | path return | Command hook prints path on stdout; HTTP hook returns `hookSpecificOutput.worktreePath`. Hook failure or missing path fails creation |
1033| WorktreeRemove | Exit code | Any non-zero exit code makes the removal fail if the directory still exists afterward. JSON output is discarded |
1034| Elicitation | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values for accept) |
1035| ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |
1036| MessageDisplay | `hookSpecificOutput` | `displayContent` replaces the displayed text on screen. Display-only: the transcript and what Claude sees keep the original |
1037| SessionStart, SubagentStart, PostModelSwitch | Context only | `hookSpecificOutput.additionalContext` adds context for Claude. SessionStart also accepts [`initialUserMessage`, `watchPaths`, `sessionTitle`, and `reloadSkills`](#sessionstart-decision-control). No blocking or decision control |
1038| Setup, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged | None | No decision control. Used for side effects like logging or cleanup |
1023| Events | Decision pattern | Key fields |
1024| :- | :- | :- |
1025| UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact | Top-level `decision` | `decision: "block"`, `reason`. Stop and SubagentStop also accept `hookSpecificOutput.additionalContext` for [non-error feedback that continues the conversation](#stop-decision-control) |
1026| 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; [TaskCompleted ignores it when the `TaskUpdate` tool triggered the event](#taskcompleted-decision-control) |
1027| TaskCreated | Exit code or top-level `decision` | Exit code 2 or `decision: "block"` [cancels the task](#taskcreated-decision-control) and returns the message to Claude. `continue: false` is ignored |
1028| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask/defer), `permissionDecisionReason` |
1029| PreModelSwitch | `hookSpecificOutput` or top-level `decision` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason`. `decision: "block"` also [cancels the switch](#premodelswitch-decision-control) |
1030| PermissionRequest | `hookSpecificOutput` | `decision.behavior` (allow/deny) |
1031| PermissionDenied | `hookSpecificOutput` | `retry: true` tells the model it may retry the denied tool call; Claude Code ignores it for [no-verdict denials](#permissiondenied-decision-control) |
1032| WorktreeCreate | path return | Command hook prints path on stdout; HTTP hook returns `hookSpecificOutput.worktreePath`. Hook failure or missing path fails creation |
1033| WorktreeRemove | Exit code | Any non-zero exit code makes the removal fail if the directory still exists afterward. JSON output is discarded |
1034| Elicitation | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values for accept) |
1035| ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |
1036| MessageDisplay | `hookSpecificOutput` | `displayContent` replaces the displayed text on screen. Display-only: the transcript and what Claude sees keep the original |
1037| SessionStart, SubagentStart, PostModelSwitch | Context only | `hookSpecificOutput.additionalContext` adds context for Claude. SessionStart also accepts [`initialUserMessage`, `watchPaths`, `sessionTitle`, and `reloadSkills`](#sessionstart-decision-control). No blocking or decision control |
1038| Setup, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged | None | No decision control. Used for side effects like logging or cleanup |
10391039
10401040A few events can also rewrite content rather than only allow or block it:
10411041
from line 1107
11071107
11081108The matcher value corresponds to how the session was initiated:
11091109
1110| Matcher | When it fires |
1111| :-------- | :------------------------------------------------------------------------------------------------------------------------------------- |
1112| `startup` | New session |
1113| `resume` | `--resume`, `--continue`, or `/resume` |
1114| `clear` | `/clear` |
1115| `compact` | Auto or manual compaction |
1116| `fork` | A new session forked from an existing one: `--fork-session` with `--resume` or `--continue`, the `/fork` background copy, or `/branch` |
1110| Matcher | When it fires |
1111| :- | :- |
1112| `startup` | New session |
1113| `resume` | `--resume`, `--continue`, or `/resume` |
1114| `clear` | `/clear` |
1115| `compact` | Auto or manual compaction |
1116| `fork` | A new session forked from an existing one: `--fork-session` with `--resume` or `--continue`, the `/fork` background copy, or `/branch` |
11171117
11181118Before v2.1.214, forked sessions reported source `"resume"`.
11191119
from line 1129
11291129
11301130In addition to the [common input fields](#common-input-fields), SessionStart hooks receive `source` and optionally `model`, `agent_type`, and `session_title`:
11311131
1132| Field | Description |
1133| :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1134| `source` | How the session started: `"startup"` for new sessions, `"resume"` for resumed sessions, `"clear"` after `/clear`, `"compact"` after compaction, or `"fork"` for a new session forked from an existing one |
1135| `model` | The active model identifier. It can be omitted, for example after `/clear` or when a session is restored through conversation recovery, so check for the field before reading it |
1136| `agent_type` | The agent name, present when you start Claude Code with `claude --agent <name>` |
1132| Field | Description |
1133| :- | :- |
1134| `source` | How the session started: `"startup"` for new sessions, `"resume"` for resumed sessions, `"clear"` after `/clear`, `"compact"` after compaction, or `"fork"` for a new session forked from an existing one |
1135| `model` | The active model identifier. It can be omitted, for example after `/clear` or when a session is restored through conversation recovery, so check for the field before reading it |
1136| `agent_type` | The agent name, present when you start Claude Code with `claude --agent <name>` |
11371137| `session_title` | The current session title if one is already set, for example via `--name` or `/rename`. A hook that emits `sessionTitle` can check `session_title` first to avoid overwriting a title the user set explicitly |
11381138
11391139When `source` is `"resume"` or `"fork"` and the transcript contains at least one response from Claude, SessionStart hooks also receive the four fields below. Your hook can use them to report what resuming a stale conversation costs before the first request, for example in a [`systemMessage`](#json-output). These fields require Claude Code v2.1.251 or later.
11401140
1141| Field | Description |
1142| :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1143| `seconds_since_last_response` | Wall-clock seconds since the last response in the resumed transcript |
1144| `context_tokens` | Tokens the first request of the resumed session re-sends as its prompt |
1141| Field | Description |
1142| :- | :- |
1143| `seconds_since_last_response` | Wall-clock seconds since the last response in the resumed transcript |
1144| `context_tokens` | Tokens the first request of the resumed session re-sends as its prompt |
11451145| `prompt_cache_likely_expired` | `true` when the last response is older than the session's [prompt cache lifetime](/docs/en/prompt-caching#cache-lifetime) or a later compaction replaced the cached conversation |
1146| `estimated_cache_write_usd` | Estimated cost in US dollars of writing `context_tokens` to the prompt cache on the session's model, excluding the response |
1146| `estimated_cache_write_usd` | Estimated cost in US dollars of writing `context_tokens` to the prompt cache on the session's model, excluding the response |
11471147
11481148This example shows the input for a session resumed 90 minutes after its last response:
11491149
from line 1166
11661166
11671167Claude Code adds stdout it [treats as plain text](#exit-code-0) to Claude's context. In addition to the [JSON output fields](#json-output) available to all hooks, you can return these event-specific fields:
11681168
1169| Field | Description |
1170| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1171| `additionalContext` | String added to Claude's context at the start of the conversation, before the first prompt. See [Add context for Claude](#add-context-for-claude) for how the text is delivered and what to put in it |
1169| Field | Description |
1170| :- | :- |
1171| `additionalContext` | String added to Claude's context at the start of the conversation, before the first prompt. See [Add context for Claude](#add-context-for-claude) for how the text is delivered and what to put in it |
11721172| `initialUserMessage` | String used as the first user message of the session. Applies in [non-interactive mode](/docs/en/headless) with the `-p` flag, where it becomes the first turn even if no prompt is provided. If a prompt is provided, it follows as the next turn. Unlike `additionalContext`, which attaches to an existing turn, this creates the turn |
1173| `sessionTitle` | Sets the session title, with the same effect as `/rename`. Use to name sessions automatically from the launch folder, git branch, or worktree name. Applies when `source` is `"startup"`, `"resume"`, or `"fork"`; ignored on `"clear"` and `"compact"` |
1174| `watchPaths` | Array of absolute paths to watch for [FileChanged](#filechanged) events during this session |
1175| `reloadSkills` | Boolean. When `true`, Claude Code re-scans the [skill](/docs/en/skills) and command directories after the SessionStart hooks complete, so skills the hook installed are available in the same session, starting with the first prompt |
1173| `sessionTitle` | Sets the session title, with the same effect as `/rename`. Use to name sessions automatically from the launch folder, git branch, or worktree name. Applies when `source` is `"startup"`, `"resume"`, or `"fork"`; ignored on `"clear"` and `"compact"` |
1174| `watchPaths` | Array of absolute paths to watch for [FileChanged](#filechanged) events during this session |
1175| `reloadSkills` | Boolean. When `true`, Claude Code re-scans the [skill](/docs/en/skills) and command directories after the SessionStart hooks complete, so skills the hook installed are available in the same session, starting with the first prompt |
11761176
11771177```json theme={null}
11781178{
from line 1246
12461246
12471247The matcher value corresponds to the CLI flag that triggered the hook:
12481248
1249| Matcher | When it fires |
1250| :------------ | :----------------------------------------- |
1251| `init` | `claude --init-only` or `claude -p --init` |
1252| `maintenance` | `claude -p --maintenance` |
1249| Matcher | When it fires |
1250| :- | :- |
1251| `init` | `claude --init-only` or `claude -p --init` |
1252| `maintenance` | `claude -p --maintenance` |
12531253
12541254When you run `claude --init-only`, Claude Code runs Setup hooks and `SessionStart` hooks with the `startup` matcher, then exits without starting a conversation.
12551255
from line 1291
12911291
12921292In addition to the [common input fields](#common-input-fields), InstructionsLoaded hooks receive these fields:
12931293
1294| Field | Description |
1295| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1296| `file_path` | Absolute path to the instruction file that was loaded |
1297| `memory_type` | Scope of the file: `"User"`, `"Project"`, `"Local"`, or `"Managed"` |
1298| `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 |
1299| `globs` | Path glob patterns from the file's `paths:` frontmatter, if any. Present only for `path_glob_match` loads |
1300| `trigger_file_path` | Path to the file whose access triggered this load, for lazy loads |
1301| `parent_file_path` | Path to the parent instruction file that included this one, for `include` loads |
1294| Field | Description |
1295| :- | :- |
1296| `file_path` | Absolute path to the instruction file that was loaded |
1297| `memory_type` | Scope of the file: `"User"`, `"Project"`, `"Local"`, or `"Managed"` |
1298| `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 |
1299| `globs` | Path glob patterns from the file's `paths:` frontmatter, if any. Present only for `path_glob_match` loads |
1300| `trigger_file_path` | Path to the file whose access triggered this load, for lazy loads |
1301| `parent_file_path` | Path to the parent instruction file that included this one, for `include` loads |
13021302
13031303```json theme={null}
13041304{
from line 1356
13561356
13571357To block a prompt, return a JSON object with `decision` set to `"block"`:
13581358
1359| Field | Description |
1360| :----------------------- | :--------------------------------------------------------------------------------------------------------------------- |
1361| `decision` | `"block"` prevents the prompt from being processed and erases it from context. Omit to allow the prompt to proceed |
1362| `reason` | Shown to the user when `decision` is `"block"`. Not added to context |
1363| `additionalContext` | String added to Claude's context alongside the submitted prompt. See [Add context for Claude](#add-context-for-claude) |
1364| `sessionTitle` | Sets the session title. Use to name sessions automatically based on the prompt content |
1365| `suppressOriginalPrompt` | If `true` when `decision` is `"block"`, omits the original prompt text from the block message shown to the user |
1359| Field | Description |
1360| :- | :- |
1361| `decision` | `"block"` prevents the prompt from being processed and erases it from context. Omit to allow the prompt to proceed |
1362| `reason` | Shown to the user when `decision` is `"block"`. Not added to context |
1363| `additionalContext` | String added to Claude's context alongside the submitted prompt. See [Add context for Claude](#add-context-for-claude) |
1364| `sessionTitle` | Sets the session title. Use to name sessions automatically based on the prompt content |
1365| `suppressOriginalPrompt` | If `true` when `decision` is `"block"`, omits the original prompt text from the block message shown to the user |
13661366
13671367A hook that blocks by exiting 2 routes the same way as `reason`: the block message shows the stderr text to the user, and it isn't added to context.
13681368
from line 1409
14091409
14101410`UserPromptExpansion` hooks can block the expansion or add context. All [JSON output fields](#json-output) are available.
14111411
1412| Field | Description |
1413| :------------------ | :-------------------------------------------------------------------------------------------------------------------- |
1414| `decision` | `"block"` prevents the command from expanding. Omit to allow it to proceed |
1415| `reason` | Shown to the user when `decision` is `"block"` |
1412| Field | Description |
1413| :- | :- |
1414| `decision` | `"block"` prevents the command from expanding. Omit to allow it to proceed |
1415| `reason` | Shown to the user when `decision` is `"block"` |
14161416| `additionalContext` | String added to Claude's context alongside the expanded prompt. See [Add context for Claude](#add-context-for-claude) |
14171417
14181418A hook that blocks by exiting 2 routes the same way as `reason`: the block message shows the stderr text to the user.
from line 1450
14501450
14511451In addition to the [common input fields](#common-input-fields), MessageDisplay hooks receive identifiers for the turn and message, the position of this call within the message, and the new text in `delta`. Batch boundaries depend on how the text streams, so use `index` and `final` to track progress through a message rather than expecting lines to be grouped a particular way.
14521452
1453| Field | Description |
1454| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1455| `turn_id` | UUID of the current turn |
1456| `message_id` | UUID of the assistant message being displayed. Stable across every batch of the same message. This is not the API `msg_…` id, so it can't be correlated with transcript message ids |
1457| `index` | Zero-based index of this batch within the message |
1458| `final` | `true` on the message's last batch. Each message has exactly one final batch |
1459| `delta` | The newly completed lines since the prior batch, terminating newlines included. Always whole lines, except the final batch which may end mid-line. In interactive runs, the final batch's delta is empty when the message ends on a newline, so treat `final`, not a non-empty delta, as the end-of-message signal. In Agent SDK and `claude -p` runs, the single call carries the entire message |
1453| Field | Description |
1454| :- | :- |
1455| `turn_id` | UUID of the current turn |
1456| `message_id` | UUID of the assistant message being displayed. Stable across every batch of the same message. This is not the API `msg_…` id, so it can't be correlated with transcript message ids |
1457| `index` | Zero-based index of this batch within the message |
1458| `final` | `true` on the message's last batch. Each message has exactly one final batch |
1459| `delta` | The newly completed lines since the prior batch, terminating newlines included. Always whole lines, except the final batch which may end mid-line. In interactive runs, the final batch's delta is empty when the message ends on a newline, so treat `final`, not a non-empty delta, as the end-of-message signal. In Agent SDK and `claude -p` runs, the single call carries the entire message |
14601460
14611461```json theme={null}
14621462{
from line 1476
14761476
14771477In addition to the [JSON output fields](#json-output) available to all hooks, MessageDisplay hooks can return `displayContent` to replace the delta on screen:
14781478
1479| Field | Description |
1480| :--------------- | :-------------------------------------------------------------------- |
1479| Field | Description |
1480| :- | :- |
14811481| `displayContent` | Text displayed in place of the delta. Omit it to display the original |
14821482
14831483MessageDisplay hooks have no decision control. They can't block the message or change what is stored in the transcript or sent to Claude. Claude Code acts on `displayContent` from their JSON output and discards `systemMessage` and `continue`.
from line 1611
16111611
16121612Executes shell commands.
16131613
1614| Field | Type | Example | Description |
1615| :------------------ | :------ | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
1616| `command` | string | `"npm test"` | The shell command to execute |
1617| `description` | string | `"Run test suite"` | Optional description of what the command does |
1618| `timeout` | number | `120000` | Optional timeout in milliseconds. Values above the [maximum](/docs/en/tools-reference#bash-tool-behavior) are reduced to the maximum rather than rejected |
1619| `run_in_background` | boolean | `false` | Whether to run the command in background |
1614| Field | Type | Example | Description |
1615| :- | :- | :- | :- |
1616| `command` | string | `"npm test"` | The shell command to execute |
1617| `description` | string | `"Run test suite"` | Optional description of what the command does |
1618| `timeout` | number | `120000` | Optional timeout in milliseconds. Values above the [maximum](/docs/en/tools-reference#bash-tool-behavior) are reduced to the maximum rather than rejected |
1619| `run_in_background` | boolean | `false` | Whether to run the command in background |
16201620
16211621When a Bash command changes files in a Git repository, Claude Code can record what changed. It records the changes in every permission mode when the [`bashEditDiffEnabled`](/docs/en/settings-reference#basheditdiffenabled) setting turns recording on; that setting's entry says which files can set it. Otherwise it records them only in auto mode and `bypassPermissions` mode, and only when Claude Code directs Claude to edit files through Bash. Set `bashEditDiffEnabled` to `false` to turn the recording off. Background commands and read-only commands carry no diff.
16221622
from line 1628
16281628
16291629`changedFiles` and `files` list what the command changed; the remaining fields say how complete and how reliable that list is.
16301630
1631| Field | Type | Example | Description |
1632| :------------- | :------ | :------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- |
1633| `changedFiles` | array | `["/path/to/src/app.ts"]` | Absolute paths of the files the command changed, at most 200. Present whenever `files` holds a diff or `moreFiles` is above zero |
1634| `files` | array | `[{"filePath": "/path/to/src/app.ts", "hunks": [...]}]` | Diffs of up to 5 changed files, for display. `created` or `deleted` is `true` for a file the command added or removed |
1635| `moreFiles` | number | `2` | Count of changed files with no diff in `files` |
1636| `unavailable` | boolean | `true` | Set when the diff is incomplete or couldn't be taken |
1637| `skipped` | boolean | `true` | Set for a Git command that moves the working tree, such as `git checkout` or `git stash`, so Claude Code takes no diff |
1638| `shared` | boolean | `true` | Set when another Bash tool call, such as a subagent's, ran in the same repository at the same time, so some listed changes may be that command's |
1631| Field | Type | Example | Description |
1632| :- | :- | :- | :- |
1633| `changedFiles` | array | `["/path/to/src/app.ts"]` | Absolute paths of the files the command changed, at most 200. Present whenever `files` holds a diff or `moreFiles` is above zero |
1634| `files` | array | `[{"filePath": "/path/to/src/app.ts", "hunks": [...]}]` | Diffs of up to 5 changed files, for display. `created` or `deleted` is `true` for a file the command added or removed |
1635| `moreFiles` | number | `2` | Count of changed files with no diff in `files` |
1636| `unavailable` | boolean | `true` | Set when the diff is incomplete or couldn't be taken |
1637| `skipped` | boolean | `true` | Set for a Git command that moves the working tree, such as `git checkout` or `git stash`, so Claude Code takes no diff |
1638| `shared` | boolean | `true` | Set when another Bash tool call, such as a subagent's, ran in the same repository at the same time, so some listed changes may be that command's |
16391639
16401640<a id="powershell" />
16411641
from line 1645
16451645
16461646The fields match the Bash tool, with the command string in `command`:
16471647
1648| Field | Type | Example | Description |
1649| :------------------ | :------ | :------------------------- | :-------------------------------------------- |
1650| `command` | string | `"Get-ChildItem -Recurse"` | The PowerShell command to execute |
1651| `description` | string | `"List files recursively"` | Optional description of what the command does |
1652| `timeout` | number | `120000` | Optional timeout in milliseconds |
1653| `run_in_background` | boolean | `false` | Whether to run the command in background |
1648| Field | Type | Example | Description |
1649| :- | :- | :- | :- |
1650| `command` | string | `"Get-ChildItem -Recurse"` | The PowerShell command to execute |
1651| `description` | string | `"List files recursively"` | Optional description of what the command does |
1652| `timeout` | number | `120000` | Optional timeout in milliseconds |
1653| `run_in_background` | boolean | `false` | Whether to run the command in background |
16541654
16551655Match `Bash|PowerShell` in hooks that inspect shell commands, so they cover both tools:
16561656
from line 1662
16621662
16631663Creates or overwrites a file.
16641664
1665| Field | Type | Example | Description |
1666| :---------- | :----- | :-------------------- | :--------------------------------- |
1665| Field | Type | Example | Description |
1666| :- | :- | :- | :- |
16671667| `file_path` | string | `"/path/to/file.txt"` | Absolute path to the file to write |
1668| `content` | string | `"file content"` | Content to write to the file |
1668| `content` | string | `"file content"` | Content to write to the file |
16691669
16701670##### Edit
16711671
16721672Replaces a string in an existing file.
16731673
1674| Field | Type | Example | Description |
1675| :------------ | :------ | :-------------------- | :--------------------------------- |
1676| `file_path` | string | `"/path/to/file.txt"` | Absolute path to the file to edit |
1677| `old_string` | string | `"original text"` | Text to find and replace |
1678| `new_string` | string | `"replacement text"` | Replacement text |
1679| `replace_all` | boolean | `false` | Whether to replace all occurrences |
1674| Field | Type | Example | Description |
1675| :- | :- | :- | :- |
1676| `file_path` | string | `"/path/to/file.txt"` | Absolute path to the file to edit |
1677| `old_string` | string | `"original text"` | Text to find and replace |
1678| `new_string` | string | `"replacement text"` | Replacement text |
1679| `replace_all` | boolean | `false` | Whether to replace all occurrences |
16801680
16811681##### Read
16821682
16831683Reads file contents.
16841684
1685| Field | Type | Example | Description |
1686| :---------- | :----- | :-------------------- | :----------------------------------------- |
1687| `file_path` | string | `"/path/to/file.txt"` | Absolute path to the file to read |
1688| `offset` | number | `10` | Optional line number to start reading from |
1689| `limit` | number | `50` | Optional number of lines to read |
1685| Field | Type | Example | Description |
1686| :- | :- | :- | :- |
1687| `file_path` | string | `"/path/to/file.txt"` | Absolute path to the file to read |
1688| `offset` | number | `10` | Optional line number to start reading from |
1689| `limit` | number | `50` | Optional number of lines to read |
16901690
16911691##### Glob
16921692
16931693Finds files matching a glob pattern.
16941694
1695| Field | Type | Example | Description |
1696| :-------- | :----- | :--------------- | :--------------------------------------------------------------------- |
1697| `pattern` | string | `"**/*.ts"` | Glob pattern to match files against |
1698| `path` | string | `"/path/to/dir"` | Optional directory to search in. Defaults to current working directory |
1695| Field | Type | Example | Description |
1696| :- | :- | :- | :- |
1697| `pattern` | string | `"**/*.ts"` | Glob pattern to match files against |
1698| `path` | string | `"/path/to/dir"` | Optional directory to search in. Defaults to current working directory |
16991699
17001700##### Grep
17011701
17021702Searches file contents with regular expressions.
17031703
1704| Field | Type | Example | Description |
1705| :------------ | :------ | :--------------- | :------------------------------------------------------------------------------------ |
1706| `pattern` | string | `"TODO.*fix"` | Regular expression pattern to search for |
1707| `path` | string | `"/path/to/dir"` | Optional file or directory to search in |
1708| `glob` | string | `"*.ts"` | Optional glob pattern to filter files |
1709| `output_mode` | string | `"content"` | `"content"`, `"files_with_matches"`, or `"count"`. Defaults to `"files_with_matches"` |
1710| `-i` | boolean | `true` | Case insensitive search |
1711| `multiline` | boolean | `false` | Enable multiline matching |
1704| Field | Type | Example | Description |
1705| :- | :- | :- | :- |
1706| `pattern` | string | `"TODO.*fix"` | Regular expression pattern to search for |
1707| `path` | string | `"/path/to/dir"` | Optional file or directory to search in |
1708| `glob` | string | `"*.ts"` | Optional glob pattern to filter files |
1709| `output_mode` | string | `"content"` | `"content"`, `"files_with_matches"`, or `"count"`. Defaults to `"files_with_matches"` |
1710| `-i` | boolean | `true` | Case insensitive search |
1711| `multiline` | boolean | `false` | Enable multiline matching |
17121712
17131713##### WebFetch
17141714
17151715Fetches and processes web content.
17161716
1717| Field | Type | Example | Description |
1718| :------- | :----- | :---------------------------- | :----------------------------------- |
1719| `url` | string | `"https://example.com/api"` | URL to fetch content from |
1717| Field | Type | Example | Description |
1718| :- | :- | :- | :- |
1719| `url` | string | `"https://example.com/api"` | URL to fetch content from |
17201720| `prompt` | string | `"Extract the API endpoints"` | Prompt to run on the fetched content |
17211721
17221722##### WebSearch
17231723
17241724Searches the web.
17251725
1726| Field | Type | Example | Description |
1727| :---------------- | :----- | :----------------------------- | :------------------------------------------------ |
1728| `query` | string | `"react hooks best practices"` | Search query |
1729| `allowed_domains` | array | `["docs.example.com"]` | Optional: only include results from these domains |
1730| `blocked_domains` | array | `["spam.example.com"]` | Optional: exclude results from these domains |
1726| Field | Type | Example | Description |
1727| :- | :- | :- | :- |
1728| `query` | string | `"react hooks best practices"` | Search query |
1729| `allowed_domains` | array | `["docs.example.com"]` | Optional: only include results from these domains |
1730| `blocked_domains` | array | `["spam.example.com"]` | Optional: exclude results from these domains |
17311731
17321732##### Agent
17331733
17341734Spawns a [subagent](/docs/en/sub-agents).
17351735
1736| Field | Type | Example | Description |
1737| :-------------- | :----- | :------------------------- | :------------------------------------------- |
1738| `prompt` | string | `"Find all API endpoints"` | The task for the agent to perform |
1739| `description` | string | `"Find API endpoints"` | Short description of the task |
1740| `subagent_type` | string | `"Explore"` | Type of specialized agent to use |
1741| `model` | string | `"sonnet"` | Optional model alias to override the default |
1736| Field | Type | Example | Description |
1737| :- | :- | :- | :- |
1738| `prompt` | string | `"Find all API endpoints"` | The task for the agent to perform |
1739| `description` | string | `"Find API endpoints"` | Short description of the task |
1740| `subagent_type` | string | `"Explore"` | Type of specialized agent to use |
1741| `model` | string | `"sonnet"` | Optional model alias to override the default |
17421742
17431743When a foreground Agent call completes, your [PostToolUse hook](#posttooluse) receives the subagent's result and run telemetry in `tool_response`. Read these fields to inspect the run; for token and cost rollups across subagents, use the [token and cost counters](/docs/en/monitoring-usage#token-counter) filtered to `query_source` `"subagent"`, since `totalTokens` and `usage` cover the final request only:
17441744
1745| Field | Type | Example | Description |
1746| :------------------ | :----- | :---------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1747| `status` | string | `"completed"` | `"completed"` for foreground subagents, `"async_launched"` for background subagents. As of v2.1.198, subagents run in the background by default, so an omitted `run_in_background` also produces `"async_launched"` |
1748| `agentId` | string | `"a4d2c8f1e0b3a297"` | Identifier for the subagent run |
1749| `content` | array | `[{"type": "text", "text": "Found 12 endpoints..."}]` | The subagent's final text blocks, or, for a subagent whose report goes through `SubagentHandback`, a short note about that hand-back in their place |
1750| `resolvedModel` | string | `"claude-sonnet-4-5"` | Model the subagent started on, which may differ from the requested model |
1751| `modelsUsed` | array | `["claude-sonnet-4-5", "claude-haiku-4-5"]` | Models used in order, with consecutive repeats collapsed; set only when the model was swapped mid-run. Requires Claude Code v2.1.212 or later |
1752| `totalTokens` | number | `12450` | Token count from the subagent's final API request: input, output, and cache tokens combined. This isn't a total across the whole run |
1753| `totalDurationMs` | number | `48211` | Wall-clock duration of the subagent run |
1754| `totalToolUseCount` | number | `7` | Count of tool calls the subagent made |
1755| `usage` | object | `{"input_tokens": 8320, ...}` | Per-type token breakdown of the final API request: `input_tokens`, `output_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens` |
1745| Field | Type | Example | Description |
1746| :- | :- | :- | :- |
1747| `status` | string | `"completed"` | `"completed"` for foreground subagents, `"async_launched"` for background subagents. As of v2.1.198, subagents run in the background by default, so an omitted `run_in_background` also produces `"async_launched"` |
1748| `agentId` | string | `"a4d2c8f1e0b3a297"` | Identifier for the subagent run |
1749| `content` | array | `[{"type": "text", "text": "Found 12 endpoints..."}]` | The subagent's final text blocks, or, for a subagent whose report goes through `SubagentHandback`, a short note about that hand-back in their place |
1750| `resolvedModel` | string | `"claude-sonnet-4-5"` | Model the subagent started on, which may differ from the requested model |
1751| `modelsUsed` | array | `["claude-sonnet-4-5", "claude-haiku-4-5"]` | Models used in order, with consecutive repeats collapsed; set only when the model was swapped mid-run. Requires Claude Code v2.1.212 or later |
1752| `totalTokens` | number | `12450` | Token count from the subagent's final API request: input, output, and cache tokens combined. This isn't a total across the whole run |
1753| `totalDurationMs` | number | `48211` | Wall-clock duration of the subagent run |
1754| `totalToolUseCount` | number | `7` | Count of tool calls the subagent made |
1755| `usage` | object | `{"input_tokens": 8320, ...}` | Per-type token breakdown of the final API request: `input_tokens`, `output_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens` |
17561756
17571757On Claude Code v2.1.271 or later, a subagent that runs with the [`SubagentHandback`](/docs/en/tools-reference) tool, which Claude Code provides in [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), delivers its report through that tool rather than returning it as text. The `content` field of its `completed` result then carries a short note about that hand-back rather than the report itself. To read the report, match a `PreToolUse` or `PostToolUse` hook on `SubagentHandback` and read `tool_input.message`.
17581758
from line 1766
17661766
17671767Asks the user one to four multiple-choice questions.
17681768
1769| Field | Type | Example | Description |
1770| :---------- | :----- | :----------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1771| `questions` | array | `[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}]` | Questions to present, each with a `question` string, short `header`, `options` array, and optional `multiSelect` flag |
1772| `answers` | object | `{"Which framework?": "React"}` | Optional. Maps question text to the selected option label. Multi-select answers join labels with commas. Claude doesn't set this field; supply it via `updatedInput` to answer programmatically |
1769| Field | Type | Example | Description |
1770| :- | :- | :- | :- |
1771| `questions` | array | `[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}]` | Questions to present, each with a `question` string, short `header`, `options` array, and optional `multiSelect` flag |
1772| `answers` | object | `{"Which framework?": "React"}` | Optional. Maps question text to the selected option label. Multi-select answers join labels with commas. Claude doesn't set this field; supply it via `updatedInput` to answer programmatically |
17731773
17741774##### ExitPlanMode
17751775
17761776Presents a plan and asks the user to approve it before Claude leaves [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode). Claude writes the plan to a file on disk before calling the tool, so the literal `tool_input` from the model is typically empty. Claude Code injects the plan content and file path before passing the input to hooks.
17771777
1778| Field | Type | Example | Description |
1779| :--------------- | :----- | :------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
1780| `plan` | string | `"## Refactor auth\n1. Extract..."` | Plan content in Markdown. Injected from the plan file on disk |
1781| `planFilePath` | string | `"/Users/.../plans/refactor-auth.md"` | Path to the plan file. Injected |
1782| `allowedPrompts` | array | `[{"tool": "Bash", "prompt": "run tests"}]` | Deprecated. Claude Code accepts the field but ignores it. Before v2.1.205, it carried prompt-based permissions Claude requested to implement the plan |
1778| Field | Type | Example | Description |
1779| :- | :- | :- | :- |
1780| `plan` | string | `"## Refactor auth\n1. Extract..."` | Plan content in Markdown. Injected from the plan file on disk |
1781| `planFilePath` | string | `"/Users/.../plans/refactor-auth.md"` | Path to the plan file. Injected |
1782| `allowedPrompts` | array | `[{"tool": "Bash", "prompt": "run tests"}]` | Deprecated. Claude Code accepts the field but ignores it. Before v2.1.205, it carried prompt-based permissions Claude requested to implement the plan |
17831783
17841784In `PostToolUse`, `tool_response` is an object with `plan` and `filePath` fields holding the approved plan, plus internal status flags. Read `tool_response.plan` for the plan content rather than re-reading the file from disk.
17851785
from line 1787
17871787
17881788`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: four outcomes (allow, deny, ask, or defer) plus the ability to modify tool input before execution.
17891789
1790| Field | Description |
1791| :------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1792| `permissionDecision` | `"allow"` skips the permission prompt, except for the [actions no mode auto-approves](/docs/en/permission-modes#actions-no-mode-auto-approves) and for `AskUserQuestion` and `ExitPlanMode`, which need [`updatedInput` paired with it](#allow-with-updatedinput). `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. `"defer"` exits gracefully so the tool can be resumed later. [Deny and ask rules](/docs/en/permissions#manage-permissions) are still evaluated regardless of what the hook returns |
1793| `permissionDecisionReason` | For `"allow"` and `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude. For `"defer"`, ignored |
1794| `updatedInput` | Modifies the tool's input parameters before execution. Replaces the entire input object, so include unchanged fields alongside modified ones. Claude Code evaluates permission rules and a Bash command's [auto-background eligibility](/docs/en/tools-reference#background-commands) against the input your hook returns, not the input Claude sent. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user. For `"defer"`, ignored |
1795| `additionalContext` | String added to Claude's context alongside the tool result. Ignored when `permissionDecision` is `"defer"`. See [Add context for Claude](#add-context-for-claude) |
1790| Field | Description |
1791| :- | :- |
1792| `permissionDecision` | `"allow"` skips the permission prompt, except for the [actions no mode auto-approves](/docs/en/permission-modes#actions-no-mode-auto-approves) and for `AskUserQuestion` and `ExitPlanMode`, which need [`updatedInput` paired with it](#allow-with-updatedinput). `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. `"defer"` exits gracefully so the tool can be resumed later. [Deny and ask rules](/docs/en/permissions#manage-permissions) are still evaluated regardless of what the hook returns |
1793| `permissionDecisionReason` | For `"allow"` and `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude. For `"defer"`, ignored |
1794| `updatedInput` | Modifies the tool's input parameters before execution. Replaces the entire input object, so include unchanged fields alongside modified ones. Claude Code evaluates permission rules and a Bash command's [auto-background eligibility](/docs/en/tools-reference#background-commands) against the input your hook returns, not the input Claude sent. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user. For `"defer"`, ignored |
1795| `additionalContext` | String added to Claude's context alongside the tool result. Ignored when `permissionDecision` is `"defer"`. See [Add context for Claude](#add-context-for-claude) |
17961796
17971797When multiple PreToolUse hooks return different decisions, precedence is `deny` > `defer` > `ask` > `allow`.
17981798
from line 1912
19121912
19131913`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:
19141914
1915| Field | Description |
1916| :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1917| `behavior` | `"allow"` grants the permission, `"deny"` denies it. [Deny and ask rules](/docs/en/permissions#manage-permissions) are still evaluated, so a hook returning `"allow"` doesn't override a matching deny rule |
1918| `updatedInput` | For `"allow"` only: modifies the tool's input parameters before execution. Replaces the entire input object, so include unchanged fields alongside modified ones. The modified input is re-evaluated against deny and ask rules |
1919| `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 |
1920| `message` | For `"deny"` only: tells Claude why the permission was denied |
1921| `interrupt` | For `"deny"` only: if `true`, stops Claude |
1915| Field | Description |
1916| :- | :- |
1917| `behavior` | `"allow"` grants the permission, `"deny"` denies it. [Deny and ask rules](/docs/en/permissions#manage-permissions) are still evaluated, so a hook returning `"allow"` doesn't override a matching deny rule |
1918| `updatedInput` | For `"allow"` only: modifies the tool's input parameters before execution. Replaces the entire input object, so include unchanged fields alongside modified ones. The modified input is re-evaluated against deny and ask rules |
1919| `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 |
1920| `message` | For `"deny"` only: tells Claude why the permission was denied |
1921| `interrupt` | For `"deny"` only: if `true`, stops Claude |
19221922
19231923A hook that exits 2 without a `decision` object leaves the permission flow unchanged, and its stderr is discarded. Only the `decision` object can grant or deny the request.
19241924
from line 1940
19401940
19411941The `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.
19421942
1943| `type` | Fields | Effect |
1944| :------------------ | :--------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1945| `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"` |
1946| `replaceRules` | `rules`, `behavior`, `destination` | Replaces all rules of the given `behavior` at the `destination` with the provided `rules` |
1947| `removeRules` | `rules`, `behavior`, `destination` | Removes matching rules of the given `behavior` |
1948| `setMode` | `mode`, `destination` | Changes the permission mode. Valid modes are `default`, `auto`, `acceptEdits`, `dontAsk`, `bypassPermissions`, `plan`, and `manual` as an alias for `default`. The `manual` alias requires Claude Code v2.1.200 or later |
1949| `addDirectories` | `directories`, `destination` | Adds working directories. `directories` is an array of path strings |
1950| `removeDirectories` | `directories`, `destination` | Removes working directories |
1943| `type` | Fields | Effect |
1944| :- | :- | :- |
1945| `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"` |
1946| `replaceRules` | `rules`, `behavior`, `destination` | Replaces all rules of the given `behavior` at the `destination` with the provided `rules` |
1947| `removeRules` | `rules`, `behavior`, `destination` | Removes matching rules of the given `behavior` |
1948| `setMode` | `mode`, `destination` | Changes the permission mode. Valid modes are `default`, `auto`, `acceptEdits`, `dontAsk`, `bypassPermissions`, `plan`, and `manual` as an alias for `default`. The `manual` alias requires Claude Code v2.1.200 or later |
1949| `addDirectories` | `directories`, `destination` | Adds working directories. `directories` is an array of path strings |
1950| `removeDirectories` | `directories`, `destination` | Removes working directories |
19511951
19521952<Note>
19531953 `setMode` with `bypassPermissions` only takes effect if you launched the session with bypass mode already available: `--dangerously-skip-permissions`, `--permission-mode bypassPermissions`, `--allow-dangerously-skip-permissions`, or `permissions.defaultMode: "bypassPermissions"` in [user, `--settings`, or managed settings](/docs/en/settings-reference#permissions-defaultmode). Otherwise the update is a no-op. The update is also a no-op when [`permissions.disableBypassPermissionsMode`](/docs/en/permissions#managed-settings) disables the mode, or when the session starts in [restricted mode](/docs/en/cli-reference#cli-flags).
from line 1957
19571957
19581958The `destination` field on every entry determines whether the change stays in memory or persists to a settings file.
19591959
1960| `destination` | Writes to |
1961| :---------------- | :---------------------------------------------- |
1962| `session` | in-memory only, discarded when the session ends |
1963| `localSettings` | `.claude/settings.local.json` |
1964| `projectSettings` | `.claude/settings.json` |
1965| `userSettings` | `~/.claude/settings.json` |
1960| `destination` | Writes to |
1961| :- | :- |
1962| `session` | in-memory only, discarded when the session ends |
1963| `localSettings` | `.claude/settings.local.json` |
1964| `projectSettings` | `.claude/settings.json` |
1965| `userSettings` | `~/.claude/settings.json` |
19661966
19671967A hook can echo one of the `permission_suggestions` it received as its own `updatedPermissions` output.
19681968
from line 2002
20022002}
20032003```
20042004
2005| Field | Description |
2006| :------------ | :------------------------------------------------------------------------------------------------------------ |
2005| Field | Description |
2006| :- | :- |
20072007| `duration_ms` | Optional. Tool execution time in milliseconds. Excludes time spent in permission prompts and PreToolUse hooks |
20082008
20092009#### PostToolUse decision control
20102010
20112011`PostToolUse` hooks can provide feedback to Claude after tool execution. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return these event-specific fields:
20122012
2013| Field | Description |
2014| :--------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2015| `decision` | `"block"` adds the `reason` next to the tool result. Claude still sees the original output; to replace it, use `updatedToolOutput` |
2016| `reason` | Explanation shown to Claude when `decision` is `"block"` |
2017| `additionalContext` | String added to Claude's context alongside the tool result. See [Add context for Claude](#add-context-for-claude) |
2018| `classifierContext` | Short note about this call's result for the [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier rather than for Claude. See [Annotate a result for the auto mode classifier](#annotate-a-result-for-the-auto-mode-classifier). Requires Claude Code v2.1.236 or later |
2019| `updatedToolOutput` | Replaces the tool's output with the provided value before it is sent to Claude. The value must match the tool's output shape |
2020| `updatedMCPToolOutput` | Replaces the output for [MCP tools](#match-mcp-tools) only. Prefer `updatedToolOutput`, which works for all tools |
2013| Field | Description |
2014| :- | :- |
2015| `decision` | `"block"` adds the `reason` next to the tool result. Claude still sees the original output; to replace it, use `updatedToolOutput` |
2016| `reason` | Explanation shown to Claude when `decision` is `"block"` |
2017| `additionalContext` | String added to Claude's context alongside the tool result. See [Add context for Claude](#add-context-for-claude) |
2018| `classifierContext` | Short note about this call's result for the [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier rather than for Claude. See [Annotate a result for the auto mode classifier](#annotate-a-result-for-the-auto-mode-classifier). Requires Claude Code v2.1.236 or later |
2019| `updatedToolOutput` | Replaces the tool's output with the provided value before it is sent to Claude. The value must match the tool's output shape |
2020| `updatedMCPToolOutput` | Replaces the output for [MCP tools](#match-mcp-tools) only. Prefer `updatedToolOutput`, which works for all tools |
20212021
20222022The example below replaces the output of a `Bash` call. The replacement value matches the `Bash` tool's output shape:
20232023
from line 2106
21062106}
21072107```
21082108
2109| Field | Description |
2110| :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2111| `error` | String describing what went wrong. The format depends on the tool that failed |
2109| Field | Description |
2110| :- | :- |
2111| `error` | String describing what went wrong. The format depends on the tool that failed |
21122112| `is_interrupt` | Optional boolean. True when the failure reached Claude Code as an abort rather than as an error the tool reported. Cancelling a running tool does not fire this hook; the tool result carries the interruption message instead |
2113| `duration_ms` | Optional. Tool execution time in milliseconds. Excludes time spent in permission prompts and PreToolUse hooks |
2113| `duration_ms` | Optional. Tool execution time in milliseconds. Excludes time spent in permission prompts and PreToolUse hooks |
21142114
21152115The `error` string is generally the same text Claude receives as the failed tool's result. Its format varies by tool and failure. Key your hook on `tool_name`, `is_interrupt`, and the `Exit code N` first line; treat the rest of the string as display text, not a stable format.
21162116
from line 2122
21222122
21232123`PostToolUseFailure` hooks can provide context to Claude after a tool failure. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return these event-specific fields:
21242124
2125| Field | Description |
2126| :------------------ | :---------------------------------------------------------------------------------------------------------- |
2125| Field | Description |
2126| :- | :- |
21272127| `additionalContext` | String added to Claude's context alongside the error. See [Add context for Claude](#add-context-for-claude) |
21282128
21292129```json theme={null}
from line 2177
21772177
21782178`PostToolBatch` hooks can inject context for Claude. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return these event-specific fields:
21792179
2180| Field | Description |
2181| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2180| Field | Description |
2181| :- | :- |
21822182| `additionalContext` | Context string injected once before the next model call. See [Add context for Claude](#add-context-for-claude) for delivery details, what to put in it, and how resumed sessions handle past values |
21832183
21842184```json theme={null}
from line 2219
22192219}
22202220```
22212221
2222| Field | Description |
2223| :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2222| Field | Description |
2223| :- | :- |
22242224| `reason` | The denial reason. For a classifier verdict, in most sessions it names the matched rule in square brackets, such as `[Data Exfiltration]`; see [Review denials](/docs/en/auto-mode-config#review-denials) for the other forms. For a [no-verdict denial](#permissiondenied-decision-control), it starts with `Auto mode could not evaluate this action and is blocking it for safety`. For a denial because the classifier model was unavailable, it is the fixed text `Classifier unavailable` |
22252225
22262226#### PermissionDenied decision control
from line 2246
22462246
22472247You receive these hook events even with desktop notifications turned off: the `preferredNotifChannel` setting, including `notifications_disabled`, changes only how you're alerted, not whether your hook runs.
22482248
2249| Matcher | When it fires |
2250| :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2251| `permission_prompt` | Claude needs you to approve a tool use or a sandboxed command's [network request](/docs/en/sandboxing#network-isolation), and the prompt has waited about six seconds |
2252| `idle_prompt` | Claude finished responding about 60 seconds ago and you haven't typed since |
2253| `auth_success` | Authentication completes |
2254| `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about six seconds |
2255| `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about six seconds |
2256| `elicitation_complete` | An MCP server reports that a [URL-mode elicitation](#elicitation-input) is complete |
2257| `elicitation_response` | An MCP elicitation response is sent back to the server |
2258| `agent_needs_input` | A background session starts waiting on your input while [agent view](/docs/en/agent-view) is open in a terminal, or the current session asks you an [agent team teammate's terminal setup question](/docs/en/agent-teams#choose-a-display-mode) and you haven't typed for about six seconds |
2259| `agent_completed` | A background session finishes or fails. Fires only while [agent view](/docs/en/agent-view) is open in a terminal |
2260| `quota_auto_resume_fired` | Claude Code continues your task after a claude.ai usage limit paused it: at the reset, or sooner when something you do in Claude Code during the wait, such as adding usage credits, upgrading your plan, or switching models, makes usage available again, with the [model-setting exception](/docs/en/interactive-mode#wait-for-a-usage-limit-to-reset) |
2261| `quota_auto_resume_stale` | A claude.ai usage limit reset while your computer slept for more than about 30 minutes. Claude Code waits for you to press `Enter` instead of continuing. After a shorter sleep it continues and fires `quota_auto_resume_fired` instead |
2249| Matcher | When it fires |
2250| :- | :- |
2251| `permission_prompt` | Claude needs you to approve a tool use or a sandboxed command's [network request](/docs/en/sandboxing#network-isolation), and the prompt has waited about six seconds |
2252| `idle_prompt` | Claude finished responding about 60 seconds ago and you haven't typed since |
2253| `auth_success` | Authentication completes |
2254| `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about six seconds |
2255| `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about six seconds |
2256| `elicitation_complete` | An MCP server reports that a [URL-mode elicitation](#elicitation-input) is complete |
2257| `elicitation_response` | An MCP elicitation response is sent back to the server |
2258| `agent_needs_input` | A background session starts waiting on your input while [agent view](/docs/en/agent-view) is open in a terminal, or the current session asks you an [agent team teammate's terminal setup question](/docs/en/agent-teams#choose-a-display-mode) and you haven't typed for about six seconds |
2259| `agent_completed` | A background session finishes or fails. Fires only while [agent view](/docs/en/agent-view) is open in a terminal |
2260| `quota_auto_resume_fired` | Claude Code continues your task after a claude.ai usage limit paused it: at the reset, or sooner when something you do in Claude Code during the wait, such as adding usage credits, upgrading your plan, or switching models, makes usage available again, with the [model-setting exception](/docs/en/interactive-mode#wait-for-a-usage-limit-to-reset) |
2261| `quota_auto_resume_stale` | A claude.ai usage limit reset while your computer slept for more than about 30 minutes. Claude Code waits for you to press `Enter` instead of continuing. After a shorter sleep it continues and fires `quota_auto_resume_fired` instead |
22622262| `quota_auto_resume_disabled` | Claude Code ends its wait for a claude.ai usage limit without continuing your task: [`autoContinueAtUsageLimit`](/docs/en/settings-reference#autocontinueatusagelimit) turned off or the reset moved more than 24 hours away during a wait Claude Code started on its own, the continued task kept hitting the limit, or the continuation was blocked before it reached the model. Doesn't fire when you press `Esc` or `Ctrl+C`, or pick **Don't continue automatically** |
22632263
22642264The `agent_needs_input` and `agent_completed` types require Claude Code v2.1.198 or later.
from line 2357
23572357
23582358SubagentStart hooks can't block subagent creation, but they can inject context into the subagent. In addition to the [JSON output fields](#json-output) available to all hooks, you can return:
23592359
2360| Field | Description |
2361| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
2360| Field | Description |
2361| :- | :- |
23622362| `additionalContext` | String added to the subagent's context at the start of its conversation, before its first prompt. See [Add context for Claude](#add-context-for-claude) |
23632363
23642364```json theme={null}
from line 2431
24312431}
24322432```
24332433
2434| Field | Description |
2435| :----------------- | :------------------------------------------------------------------------- |
2436| `task_id` | Identifier of the task being created |
2437| `task_subject` | Title of the task |
2438| `task_description` | Detailed description of the task. May be absent |
2439| `teammate_name` | Name of the teammate creating the task. May be absent |
2440| `team_name` | Deprecated. Session-derived team name; will be removed in a future release |
2434| Field | Description |
2435| :- | :- |
2436| `task_id` | Identifier of the task being created |
2437| `task_subject` | Title of the task |
2438| `task_description` | Detailed description of the task. May be absent |
2439| `teammate_name` | Name of the teammate creating the task. May be absent |
2440| `team_name` | Deprecated. Session-derived team name; will be removed in a future release |
24412441
24422442#### TaskCreated decision control
24432443
from line 2486
24862486}
24872487```
24882488
2489| Field | Description |
2490| :----------------- | :------------------------------------------------------------------------- |
2491| `task_id` | Identifier of the task being completed |
2492| `task_subject` | Title of the task |
2493| `task_description` | Detailed description of the task. May be absent |
2494| `teammate_name` | Name of the teammate completing the task. May be absent |
2495| `team_name` | Deprecated. Session-derived team name; will be removed in a future release |
2489| Field | Description |
2490| :- | :- |
2491| `task_id` | Identifier of the task being completed |
2492| `task_subject` | Title of the task |
2493| `task_description` | Detailed description of the task. May be absent |
2494| `teammate_name` | Name of the teammate completing the task. May be absent |
2495| `team_name` | Deprecated. Session-derived team name; will be removed in a future release |
24962496
24972497#### TaskCompleted decision control
24982498
from line 2537
25372537
25382538Each entry in `background_tasks` describes one in-flight task and uses these fields:
25392539
2540| Field | Description |
2541| :------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2542| `id` | Task identifier |
2543| `type` | Friendly task-type label such as `shell`, `subagent`, `monitor`, `workflow`, `teammate`, `cloud session`, or `MCP task`. Each label identifies which Claude Code feature created the task. Falls back to the raw discriminant for unrecognized types |
2544| `status` | Current task status |
2545| `description` | Free-text description, capped at 1000 characters with an in-string `… [+N chars]` marker when clipped |
2546| `command` | Shell command line, capped at 1000 characters. Present only for `shell` tasks |
2547| `agent_type` | Subagent type name. Present only for `subagent` tasks |
2548| `server` | MCP server name. Present only for `monitor` and `MCP task` tasks |
2549| `tool` | MCP tool name. Present only for `monitor` and `MCP task` tasks |
2550| `name` | Workflow name. Present only for `workflow` tasks |
2540| Field | Description |
2541| :- | :- |
2542| `id` | Task identifier |
2543| `type` | Friendly task-type label such as `shell`, `subagent`, `monitor`, `workflow`, `teammate`, `cloud session`, or `MCP task`. Each label identifies which Claude Code feature created the task. Falls back to the raw discriminant for unrecognized types |
2544| `status` | Current task status |
2545| `description` | Free-text description, capped at 1000 characters with an in-string `… [+N chars]` marker when clipped |
2546| `command` | Shell command line, capped at 1000 characters. Present only for `shell` tasks |
2547| `agent_type` | Subagent type name. Present only for `subagent` tasks |
2548| `server` | MCP server name. Present only for `monitor` and `MCP task` tasks |
2549| `tool` | MCP tool name. Present only for `monitor` and `MCP task` tasks |
2550| `name` | Workflow name. Present only for `workflow` tasks |
25512551
25522552Each entry in `session_crons` describes one session-scoped scheduled wakeup, sourced from `CronCreate`, `ScheduleWakeup`, and `/loop`:
25532553
2554| Field | Description |
2555| :---------- | :------------------------------------------------------------------------------------------------------------------- |
2556| `id` | Cron task identifier |
2557| `schedule` | Cron expression, for example `0 9 * * 1-5` |
2554| Field | Description |
2555| :- | :- |
2556| `id` | Cron task identifier |
2557| `schedule` | Cron expression, for example `0 9 * * 1-5` |
25582558| `recurring` | `false` for one-shot wakeups whose schedule encodes a single fire time, `true` for tasks that re-fire on every match |
2559| `prompt` | Prompt submitted when the cron fires, capped at 1000 characters with the same `… [+N chars]` marker |
2559| `prompt` | Prompt submitted when the cron fires, capped at 1000 characters with the same `… [+N chars]` marker |
25602560
25612561This example shows a Stop input with one in-flight shell task and one recurring cron:
25622562
from line 2593
25932593
25942594`Stop` and `SubagentStop` hooks can control whether Claude continues. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return these event-specific fields:
25952595
2596| Field | Description |
2597| :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2598| `decision` | `"block"` prevents Claude from stopping. Omit to allow Claude to stop |
2599| `reason` | Required when `decision` is `"block"`. Tells Claude why it should continue |
2596| Field | Description |
2597| :- | :- |
2598| `decision` | `"block"` prevents Claude from stopping. Omit to allow Claude to stop |
2599| `reason` | Required when `decision` is `"block"`. Tells Claude why it should continue |
26002600| `hookSpecificOutput.additionalContext` | Non-error feedback for Claude. The conversation continues so Claude can act on it, but unlike `decision: "block"` it is shown in the transcript as hook feedback rather than a hook error |
26012601
26022602A hook that blocks by exiting 2 routes the same way as `reason`: Claude receives the stderr message as the explanation for why it should continue.
from line 2627
26272627
26282628In 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.
26292629
2630| Field | Description |
2631| :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2632| `error` | Error type: `rate_limit`, `overloaded`, `authentication_failed`, `oauth_org_not_allowed`, `account_on_hold`, `billing_error`, `invalid_request`, `model_not_found`, `server_error`, `max_output_tokens`, `cloud_credential_error`, or `unknown` |
2633| `error_details` | Additional details about the error, when available |
2630| Field | Description |
2631| :- | :- |
2632| `error` | Error type: `rate_limit`, `overloaded`, `authentication_failed`, `oauth_org_not_allowed`, `account_on_hold`, `billing_error`, `invalid_request`, `model_not_found`, `server_error`, `max_output_tokens`, `cloud_credential_error`, or `unknown` |
2633| `error_details` | Additional details about the error, when available |
26342634| `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"` |
26352635
26362636```json theme={null}
from line 2669
26692669}
26702670```
26712671
2672| Field | Description |
2673| :-------------- | :------------------------------------------------------------------------- |
2674| `teammate_name` | Name of the teammate that is about to go idle |
2675| `team_name` | Deprecated. Session-derived team name; will be removed in a future release |
2672| Field | Description |
2673| :- | :- |
2674| `teammate_name` | Name of the teammate that is about to go idle |
2675| `team_name` | Deprecated. Session-derived team name; will be removed in a future release |
26762676
26772677#### TeammateIdle decision control
26782678
from line 2702
27022702
27032703The matcher filters on the configuration source:
27042704
2705| Matcher | When it fires |
2706| :----------------- | :----------------------------------------------------------------- |
2707| `user_settings` | `~/.claude/settings.json` changes |
2708| `project_settings` | `.claude/settings.json` changes |
2709| `local_settings` | `.claude/settings.local.json` changes |
2710| `policy_settings` | `managed-settings.json` or a file in `managed-settings.d/` changes |
2711| `skills` | A skill file in `.claude/skills/` changes |
2705| Matcher | When it fires |
2706| :- | :- |
2707| `user_settings` | `~/.claude/settings.json` changes |
2708| `project_settings` | `.claude/settings.json` changes |
2709| `local_settings` | `.claude/settings.local.json` changes |
2710| `policy_settings` | `managed-settings.json` or a file in `managed-settings.d/` changes |
2711| `skills` | A skill file in `.claude/skills/` changes |
27122712
27132713This example logs all configuration changes for security auditing:
27142714
from line 2749
27492749
27502750ConfigChange hooks can block configuration changes from taking effect. Use exit code 2 or a JSON `decision` to prevent the change. When blocked, the new settings are not applied to the running session.
27512751
2752| Field | Description |
2753| :--------- | :--------------------------------------------------------------------------------------- |
2752| Field | Description |
2753| :- | :- |
27542754| `decision` | `"block"` prevents the configuration change from being applied. Omit to allow the change |
2755| `reason` | Accepted but never shown |
2755| `reason` | Accepted but never shown |
27562756
27572757```json theme={null}
27582758{
from line 2792
27922792
27932793In addition to the [JSON output fields](#json-output) available to all hooks, CwdChanged hooks can return `watchPaths` to dynamically set which file paths [FileChanged](#filechanged) watches:
27942794
2795| Field | Description |
2796| :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2795| Field | Description |
2796| :- | :- |
27972797| `watchPaths` | Array of absolute paths. Replaces the current dynamic watch list. Paths from your `matcher` configuration are always watched. Returning an empty array clears the dynamic list, which is typical when entering a new directory |
27982798
27992799CwdChanged hooks have no decision control. They can't block the directory change.
from line 2816
28162816
28172817The matcher filters on how the directory was added:
28182818
2819| Matcher | When it fires |
2820| :------------------- | :--------------------------------------------------------------------------- |
2821| `slash_command` | You add a directory with `/add-dir` |
2819| Matcher | When it fires |
2820| :- | :- |
2821| `slash_command` | You add a directory with `/add-dir` |
28222822| `register_repo_root` | An SDK client adds a directory with the `register_repo_root` control request |
28232823
28242824#### DirectoryAdded input
28252825
28262826In addition to the [common input fields](#common-input-fields), DirectoryAdded hooks receive `directory` and `source`.
28272827
2828| Field | Description |
2829| :---------- | :------------------------------------------------------------------------------------------------------------------ |
2830| `directory` | Absolute path of the directory that was added |
2831| `source` | How the directory was added, `"slash_command"` for `/add-dir` or `"register_repo_root"` for the SDK control request |
2828| Field | Description |
2829| :- | :- |
2830| `directory` | Absolute path of the directory that was added |
2831| `source` | How the directory was added, `"slash_command"` for `/add-dir` or `"register_repo_root"` for the SDK control request |
28322832
28332833```json theme={null}
28342834{
from line 2895
28952895
28962896In addition to the [common input fields](#common-input-fields), FileChanged hooks receive `file_path` and `event`.
28972897
2898| Field | Description |
2899| :---------- | :---------------------------------------------------------------------------------------------------------- |
2900| `file_path` | Absolute path to the file that changed |
2901| `event` | What happened: `"change"` for a modified file, `"add"` for a created file, or `"unlink"` for a deleted file |
2898| Field | Description |
2899| :- | :- |
2900| `file_path` | Absolute path to the file that changed |
2901| `event` | What happened: `"change"` for a modified file, `"add"` for a created file, or `"unlink"` for a deleted file |
29022902
29032903```json theme={null}
29042904{
from line 2915
29152915
29162916In addition to the [JSON output fields](#json-output) available to all hooks, FileChanged hooks can return `watchPaths` to dynamically update which file paths are watched:
29172917
2918| Field | Description |
2919| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2918| Field | Description |
2919| :- | :- |
29202920| `watchPaths` | Array of absolute paths. Replaces the current dynamic watch list. Paths from your `matcher` configuration are always watched. Use this when your hook script discovers additional files to watch based on the changed file |
29212921
29222922FileChanged hooks have no decision control. They can't block the file change from occurring.
from line 3039
30393039
30403040The matcher value indicates whether compaction was triggered manually or automatically:
30413041
3042| Matcher | When it fires |
3043| :------- | :----------------------------------------------------------------------------------------------------------------- |
3044| `manual` | `/compact` |
3045| `auto` | Auto-compact when the conversation reaches the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) |
3042| Matcher | When it fires |
3043| :- | :- |
3044| `manual` | `/compact` |
3045| `auto` | Auto-compact when the conversation reaches the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) |
30463046
30473047Exit with code 2 to block compaction. For a manual `/compact`, the stderr message is shown to the user. You can also block by returning JSON with `"decision": "block"`.
30483048
from line 3071
30713071
30723072The same matcher values apply as for `PreCompact`:
30733073
3074| Matcher | When it fires |
3075| :------- | :----------------------------------------------------------------------------------------------------------------------- |
3076| `manual` | After `/compact` |
3077| `auto` | After auto-compact when the conversation reaches the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) |
3074| Matcher | When it fires |
3075| :- | :- |
3076| `manual` | After `/compact` |
3077| `auto` | After auto-compact when the conversation reaches the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) |
30783078
30793079#### PostCompact input
30803080
from line 3183
31833183
31843184In addition to the [common input fields](#common-input-fields), PreModelSwitch hooks receive the fields in this table. The last five describe what re-sending the conversation to the new model costs, so a hook can show that figure before the switch happens.
31853185
3186| Field | Type | Description |
3187| :-------------------------- | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3188| `from_model` | string | Model ID the switch changes from |
3189| `to_model` | string | Model ID the switch changes to. The matcher compares against this model's canonical name |
3190| `requested_model` | string or `null` | The model the request named: an alias such as `opus`, a full model ID, or `null` when the request was for the default model |
3191| `source` | string | Where the request came from: `"command"` for `/model <name>`, the Model setting in `/config`, or turning on fast mode; `"picker"` for a model picker; `"sdk"` for a `set_model` request, or a model change in an `apply_flag_settings` request, from an Agent SDK host or Remote Control |
3192| `context_tokens` | number | Tokens the next request re-sends as its prompt: the input, cache read, cache creation, and output tokens of the last response in the main conversation, combined. `0` before the first response |
3193| `prompt_cache_warm` | boolean | Whether the current model's prompt cache is likely still warm, meaning the switch forfeits it |
3194| `cache_ttl` | string | [Prompt cache lifetime](/docs/en/prompt-caching#cache-lifetime) Claude Code requests for this session: `"5m"` or `"1h"` |
3195| `estimated_cache_write_usd` | number | Estimated cost in US dollars of writing `context_tokens` to the prompt cache on `to_model` at the `cache_ttl` rate, excluding the next response. The server may not need to re-cache the whole context, so treat it as an estimate |
3196| `pricing` | string | How Claude Code priced `estimated_cache_write_usd`: `"configured"` at your organization's own rates when it has configured them, `"catalog"` at list price, or `"default"` when `to_model` has no known price and Claude Code assumed a default rate |
3186| Field | Type | Description |
3187| :- | :- | :- |
3188| `from_model` | string | Model ID the switch changes from |
3189| `to_model` | string | Model ID the switch changes to. The matcher compares against this model's canonical name |
3190| `requested_model` | string or `null` | The model the request named: an alias such as `opus`, a full model ID, or `null` when the request was for the default model |
3191| `source` | string | Where the request came from: `"command"` for `/model <name>`, the Model setting in `/config`, or turning on fast mode; `"picker"` for a model picker; `"sdk"` for a `set_model` request, or a model change in an `apply_flag_settings` request, from an Agent SDK host or Remote Control |
3192| `context_tokens` | number | Tokens the next request re-sends as its prompt: the input, cache read, cache creation, and output tokens of the last response in the main conversation, combined. `0` before the first response |
3193| `prompt_cache_warm` | boolean | Whether the current model's prompt cache is likely still warm, meaning the switch forfeits it |
3194| `cache_ttl` | string | [Prompt cache lifetime](/docs/en/prompt-caching#cache-lifetime) Claude Code requests for this session: `"5m"` or `"1h"` |
3195| `estimated_cache_write_usd` | number | Estimated cost in US dollars of writing `context_tokens` to the prompt cache on `to_model` at the `cache_ttl` rate, excluding the next response. The server may not need to re-cache the whole context, so treat it as an estimate |
3196| `pricing` | string | How Claude Code priced `estimated_cache_write_usd`: `"configured"` at your organization's own rates when it has configured them, `"catalog"` at list price, or `"default"` when `to_model` has no known price and Claude Code assumed a default rate |
31973197
31983198This example shows the input for `/model opus` in a session running Sonnet 5:
31993199
from line 3221
32213221
32223222For finer control, return `permissionDecision` and `permissionDecisionReason` in a `hookSpecificOutput` object, as on [PreToolUse](#pretooluse-decision-control). `PreModelSwitch` accepts `"allow"`, `"deny"`, and `"ask"`. It doesn't accept `"defer"`, `updatedInput`, or `additionalContext`. The table below describes both fields:
32233223
3224| Field | Description |
3225| :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3226| `permissionDecision` | `"allow"` proceeds and skips the [confirmation Claude Code shows while the prompt cache is warm](/docs/en/prompt-caching#switching-models). `"deny"` cancels the switch. `"ask"` prompts the user to confirm it |
3227| `permissionDecisionReason` | For `"deny"`, shown to the user as the reason the switch was blocked, or returned as the error for a `set_model` request. For `"ask"`, shown in the confirmation prompt. Ignored for `"allow"` |
3224| Field | Description |
3225| :- | :- |
3226| `permissionDecision` | `"allow"` proceeds and skips the [confirmation Claude Code shows while the prompt cache is warm](/docs/en/prompt-caching#switching-models). `"deny"` cancels the switch. `"ask"` prompts the user to confirm it |
3227| `permissionDecisionReason` | For `"deny"`, shown to the user as the reason the switch was blocked, or returned as the error for a `set_model` request. For `"ask"`, shown in the confirmation prompt. Ignored for `"allow"` |
32283228
32293229Only `/model` in an interactive session can show the `"ask"` prompt. On every other surface, including non-interactive mode with the `-p` flag, `/config`, and `set_model` requests, Claude Code treats `"ask"` as a refusal.
32303230
from line 3295
32953295
32963296Claude Code takes your hook's [plain-text stdout](#exit-code-0) on exit 0, or `additionalContext` from JSON output, and delivers it to Claude with the next request after the switch. In addition to the [JSON output fields](#json-output) available to all hooks, you can return:
32973297
3298| Field | Description |
3299| :------------------ | :------------------------------------------------------------------------------------------------------------ |
3298| Field | Description |
3299| :- | :- |
33003300| `additionalContext` | String added to Claude's context with the next request. See [Add context for Claude](#add-context-for-claude) |
33013301
33023302If the hook hasn't finished within five seconds after you send the next prompt, Claude Code sends that request without the output and attaches it to the following request instead. If the model changes several times before the next request, Claude Code delivers only the output for the last switch's target model.
from line 3308
33083308
33093309The `reason` field in the hook input indicates why the session ended:
33103310
3311| Reason | Description |
3312| :---------------------------- | :---------------------------------------------------------------------------------------- |
3313| `clear` | Session cleared with `/clear` command |
3314| `resume` | Session switched via interactive `/resume` |
3315| `logout` | User logged out |
3316| `prompt_input_exit` | User exited while prompt input was visible |
3317| `other` | Other exit reasons |
3311| Reason | Description |
3312| :- | :- |
3313| `clear` | Session cleared with `/clear` command |
3314| `resume` | Session switched via interactive `/resume` |
3315| `logout` | User logged out |
3316| `prompt_input_exit` | User exited while prompt input was visible |
3317| `other` | Other exit reasons |
33183318| `bypass_permissions_disabled` | Removed in v2.1.234; Claude Code doesn't send it. Drop it from your `SessionEnd` matchers |
33193319
33203320#### SessionEnd input
from line 3407
34073407}
34083408```
34093409
3410| Field | Values | Description |
3411| :-------- | :---------------------------- | :--------------------------------------------------------------- |
3412| `action` | `accept`, `decline`, `cancel` | Whether to accept, decline, or cancel the request |
3413| `content` | object | Form field values to submit. Only used when `action` is `accept` |
3410| Field | Values | Description |
3411| :- | :- | :- |
3412| `action` | `accept`, `decline`, `cancel` | Whether to accept, decline, or cancel the request |
3413| `content` | object | Form field values to submit. Only used when `action` is `accept` |
34143414
34153415Exit code 2 denies the elicitation. Claude Code doesn't show your stderr message anywhere.
34163416
from line 3454
34543454}
34553455```
34563456
3457| Field | Values | Description |
3458| :-------- | :---------------------------- | :--------------------------------------------------------------------- |
3459| `action` | `accept`, `decline`, `cancel` | Overrides the user's action |
3460| `content` | object | Overrides form field values. Only meaningful when `action` is `accept` |
3457| Field | Values | Description |
3458| :- | :- | :- |
3459| `action` | `accept`, `decline`, `cancel` | Overrides the user's action |
3460| `content` | object | Overrides form field values. Only meaningful when `action` is `accept` |
34613461
34623462Exit code 2 blocks the response, changing the effective action to `decline`. Claude Code doesn't show your stderr message anywhere.
34633463
from line 3538
35383538}
35393539```
35403540
3541| Field | Required | Description |
3542| :---------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3543| `type` | yes | Must be `"prompt"` |
3544| `prompt` | yes | The prompt text to send to the LLM. Use `$ARGUMENTS` as a placeholder for the hook input JSON. If `$ARGUMENTS` is not present, input JSON is appended to the prompt |
3545| `model` | no | Model to use for evaluation. Defaults to a fast model |
3546| `timeout` | no | Timeout in seconds. Default: 30 |
3547| `continueOnBlock` | no | On the events it applies to, `true` feeds an `ok: false` reason back to Claude and continues instead of ending the turn. Default: `false`. See [Response schema](#response-schema) for per-event behavior |
3541| Field | Required | Description |
3542| :- | :- | :- |
3543| `type` | yes | Must be `"prompt"` |
3544| `prompt` | yes | The prompt text to send to the LLM. Use `$ARGUMENTS` as a placeholder for the hook input JSON. If `$ARGUMENTS` is not present, input JSON is appended to the prompt |
3545| `model` | no | Model to use for evaluation. Defaults to a fast model |
3546| `timeout` | no | Timeout in seconds. Default: 30 |
3547| `continueOnBlock` | no | On the events it applies to, `true` feeds an `ok: false` reason back to Claude and continues instead of ending the turn. Default: `false`. See [Response schema](#response-schema) for per-event behavior |
35483548
35493549### Response schema
35503550
from line 3558
35583558}
35593559```
35603560
3561| Field | Description |
3562| :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3563| `ok` | `true` to allow. For `false`, see the per-event behavior below |
3564| `reason` | Required when `ok` is `false` |
3561| Field | Description |
3562| :- | :- |
3563| `ok` | `true` to allow. For `false`, see the per-event behavior below |
3564| `reason` | Required when `ok` is `false` |
35653565| `impossible` | Optional. The model returns it with `ok: false` when it judges the condition can never be satisfied. On `Stop` and `SubagentStop`, Claude Code then lets the turn end instead of feeding the reason back. Agent hooks and other events ignore it |
35663566
35673567What happens on `ok: false` depends on the event:
35683568
No line in this hunk matches that.