| Event | When it fires | Matcher filters | Exit code 2 |
|---|---|---|---|
| SessionStart | When a session begins or resumes | how the session startedstartup, resume, clear, compact, fork | No Shows stderr to user only |
| 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 | which CLI flag triggered setupinit, maintenance | No Exit code and stderr are ignored |
| UserPromptSubmit | When a prompt is submitted, before Claude processes it. Also fires on turns Claude Code starts on its own | no matcher support | Yes Blocks the prompt, so it never reaches Claude. See What a blocked prompt leaves behind |
| UserPromptExpansion | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion | command name your skill or command names | Yes Blocks the expansion |
| PreToolUse | Before a tool call executes. Can block it | tool nameBash, Edit|Write, mcp__.* | Yes Blocks the tool call |
| PermissionRequest | When a tool call needs a permission decision | tool nameBash, Edit|Write, mcp__.* | No Exit code 2 isn't honored for this event and the permission flow proceeds unchanged. Deny through the decision object instead |
| 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 | tool nameBash, Edit|Write, mcp__.* | 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 |
| PostToolUse | After a tool call succeeds | tool nameBash, Edit|Write, mcp__.* | No Shows stderr to Claude; the tool already ran |
| PostToolUseFailure | After a tool call fails | tool nameBash, Edit|Write, mcp__.* | No Shows stderr to Claude; the tool already failed |
| PostToolBatch | After a full batch of parallel tool calls resolves, before the next model call | no matcher support | Yes Stops the agentic loop before the next model call |
| Notification | When Claude Code sends a notification | notification typepermission_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, plugin_notification | No Exit code and stderr are ignored |
| MessageDisplay | While assistant message text is displayed | no matcher support | No The original text is displayed |
| SubagentStart | When a subagent is spawned | agent typegeneral-purpose, Explore, Plan, custom agent names, or plugin-scoped names like ^my-plugin:reviewer$ | No Shows stderr to user only |
| SubagentStop | When a subagent finishes | agent type same values as SubagentStart | Yes Prevents the subagent from stopping |
| TaskCreated | When a task is being created via TaskCreate | no matcher support | Yes Rolls back the task creation |
| TaskCompleted | When a task is being marked as completed | no matcher support | Yes Prevents the task from being marked as completed |
| Stop | When Claude finishes responding | no matcher support | Yes Prevents Claude from stopping, continues the conversation |
| StopFailure | When the turn ends due to an API error | error typerate_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 | No Output and exit code are ignored, except terminalSequence |
| TeammateIdle | When an agent team teammate is about to go idle | no matcher support | Yes Prevents the teammate from going idle, so it continues working |
| 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 | load reasonsession_start, nested_traversal, path_glob_match, include, compact | No Exit code is ignored |
| ConfigChange | When a configuration file changes during a session | configuration sourceuser_settings, project_settings, local_settings, policy_settings, skills | Yes Blocks the configuration change from taking effect (except policy_settings) |
| CwdChanged | When the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnv | no matcher support | No Shows stderr to user only |
| DirectoryAdded | When a working directory is added mid-session via /add-dir or the SDK register_repo_root control request | how the directory was addedslash_command, register_repo_root | No Stderr goes to the debug log; the directory is already added |
| FileChanged | When a watched file changes on disk. The matcher field specifies which filenames to watch | literal filenames to watch (see FileChanged).envrc|.env | No Shows stderr to user only |
| WorktreeCreate | When a worktree is being created via --worktree, isolation: "worktree", or for a background session. Replaces default git behavior | no matcher support | Yes Any non-zero exit code causes worktree creation to fail |
| WorktreeRemove | When a worktree that a WorktreeCreate hook created is being removed | no matcher support | Yes Any non-zero exit code causes worktree removal to fail if the directory still exists afterward. See WorktreeRemove for what happens to the directory |
| PreCompact | Before context compaction | what triggered compactionmanual, auto | Yes Blocks compaction |
| PostCompact | After context compaction completes | what triggered compactionmanual, auto | No Shows stderr to user only |
| PreModelSwitch | Before Claude Code applies a model switch that you or a client requested. Can block the switch | canonical name of the model the session switches to, as described under PreModelSwitchclaude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.* | Yes Blocks the model switch and shows stderr to the user |
| 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 | canonical name of the model the session switches to, as described under PreModelSwitchclaude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.* | No Shows stderr to user only; the model already switched |
| Elicitation | When an MCP server requests user input during a tool call | MCP server name your configured MCP server names | Yes Declines the request, and no dialog appears |
| ElicitationResult | After a user responds to an MCP elicitation, before the response is sent back to the server | MCP server name same values as Elicitation | Yes Blocks the response (action becomes decline) |
| SessionEnd | When a session terminates | why the session endedclear, resume, logout, prompt_input_exit, other | No Shows stderr to user only |
Where hooks live
| Location | Scope | Shareable |
|---|---|---|
~/.claude/settings.json | All your projects | No, local to your machine |
.claude/settings.json | Single project | Yes, can be committed to the repo |
.claude/settings.local.json | Single project | No, gitignored when Claude Code saves a setting to it |
| Managed policy settings | Organization-wide | Yes, admin-controlled |
Plugin hooks/hooks.json | When plugin is enabled | Yes, bundled with the plugin |
| Skill frontmatter | The rest of the session once the skill is invoked. See Hooks in skills and agents | Yes, defined in the skill file |
| Subagent frontmatter | While that subagent is running | Yes, defined in the subagent file |
Handler fields
Common fields
- type required
"command","http","mcp_tool","prompt", or"agent"- if optional
- 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 for how Bash patterns evaluate against subcommands,$(), and backticks. Only evaluated on tool events:PreToolUse,PostToolUse,PostToolUseFailure,PermissionRequest, andPermissionDenied. On other events, a hook withifset never runs - timeout optional
- Seconds before canceling. Claude Code doesn't enforce it on a command hook you run with
async: true. Defaults: 600 forcommand,http, andmcp_tool; 30 forprompt; 60 foragent. Claude Code lowers thecommand,http, andmcp_tooldefault to 30 onUserPromptSubmit,PreModelSwitch, andPostModelSwitch, and to 10 onMessageDisplay.SessionEndhooks share a 1.5-second budget; if your settings set a longer per-hooktimeout, Claude Code raises the budget to match, up to 60 seconds - statusMessage optional
- Custom spinner message displayed while the hook runs
- once optional
- 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; ignored in settings files and agent frontmatter
Command hook fields
- command required
- Shell command to execute. With
args, the executable to spawn directly. See Exec form and shell form - args optional
- Argument list. When present,
commandis resolved as an executable and spawned directly withargsas the argument vector, with no shell involved. See Exec form and shell form - async optional
- If
true, runs in the background without blocking. See Run hooks in the background - asyncRewake optional
- 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 so it can react to a long-running background failure - shell optional
- 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 requireCLAUDE_CODE_USE_POWERSHELL_TOOLsince hooks spawn PowerShell directly. Ignored whenargsis set - onFailure optional
- Whether the action that triggered the hook goes ahead when the hook fails:
"continue"or"block". See Block the action when a hook fails for"block"and for leaving the field unset. Requires Claude Code v2.1.295 or later
HTTP hook fields
- url required
- URL to send the POST request to
- headers optional
- Additional HTTP headers as key-value pairs. Values support environment variable interpolation using
$VAR_NAMEor${VAR_NAME}syntax. Only variables listed inallowedEnvVarsare resolved - allowedEnvVars optional
- 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
- onFailure optional
- What happens to the action when the hook fails:
"continue", the default, or"block". See Block the action when a hook fails. Requires Claude Code v2.1.295 or later
MCP tool hook fields
- server required
- Name of a configured MCP server. For a plugin-bundled server, this is the scoped name
plugin:<plugin-name>:<server-name>, such asplugin:my-plugin:db, not the bare server key - tool required
- Name of the tool to call on that server
- input optional
- Arguments passed to the tool. String values support
${path}substitution from the hook's JSON input, such as"${tool_input.file_path}"
Prompt and agent hook fields
- prompt required
- Prompt text to send to the model. Use
$ARGUMENTSas a placeholder for the hook input JSON. Escape with a backslash to include literal text:\$1.00renders as$1.00 - model optional
- Model to use for evaluation. Defaults to the model Claude Code uses for background functionality
Input every hook receives
session_idCurrent session identifierprompt_idUUID identifying the user prompt currently being processed. Matches the prompt.id attribute on OpenTelemetry events, so you can correlate hook output with telemetry for a single prompt. Absent until the first user inputtranscript_pathPath 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 and SubagentStop instead of reading the transcriptcwdCurrent working directory when the hook is invokedscratchpad_dirPath to the session's 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 laterpermission_modeCurrent permission mode: "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 sectioneffortObject whose level is the effort level setting when the hook runs, including a skill's, subagent's, or ultrathink level: "low", "medium", "high", "xhigh", or "max". Present for PreToolUse, PostToolUse, Stop, SubagentStop, and other events with a tool-use context, on models with effort. If the model doesn't support your level, level is the level that ran. A level you changed can show here before it starts. $CLAUDE_EFFORT holds the same level.hook_event_nameName of the event that firedHow a hook is shaped
Each event holds a list of matcher groups, and each group holds one or more handlers:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "npx prettier --write \"$(jq -r '.tool_input.file_path')\"" }
]
}
]
}
}A matcher of only letters, digits, _, -, spaces, , and | is an exact match or list of exact matches. Anything else is an unanchored JavaScript regular expression, so Edit.* also matches NotebookEdit; use ^Edit$ for a whole-name match.
Frequently asked questions
What hook events does Claude Code support?
Claude Code currently fires 33 hook events, from SessionStart and UserPromptSubmit through PreToolUse, PostToolUse and Stop to SessionEnd. The table on this page lists each one, what its matcher filters and whether it can block.
How does a hook block an action?
A command hook that exits with code 2 blocks the action for events that support blocking, such as PreToolUse, UserPromptSubmit and Stop, and its stderr is fed back to Claude. For finer control, exit 0 and print JSON, for example hookSpecificOutput.permissionDecision: "deny" on PreToolUse.
Where do I put hooks?
Under the "hooks" key of ~/.claude/settings.json (all projects), .claude/settings.json (shared with the repo) or .claude/settings.local.json (just you). Plugins ship hooks in hooks/hooks.json, and skills and subagents can declare hooks in their frontmatter.
What is the difference between PreToolUse and PermissionRequest?
PreToolUse runs before every matching tool call and can allow, deny or ask. PermissionRequest only runs when Claude Code is about to show you a permission dialog, so it is the place to auto-approve or auto-deny prompts.
Which hook types are there?
command (runs a shell command with the event JSON on stdin), http (POSTs the JSON to a URL), mcp_tool (calls a tool on a connected MCP server), prompt (asks a model to decide) and agent (runs a subagent to decide).
Sources: Claude Code hooks reference. Claude Code Agents is an independent site, not affiliated with Anthropic.