Reference· Updated 2026-11-03

Claude Code Hook Events Reference

All 33 Claude Code hook events in one table: when each fires, what its matcher filters, and what exit code 2 does. Want the JSON instead? Use the hooks generator.
EventWhen it firesMatcher filtersExit code 2
SessionStartWhen a session begins or resumeshow the session started
startup, resume, clear, compact, fork
No
Shows stderr to user only
SetupWhen you start Claude Code with --init-only, or with --init or --maintenance in -p mode. For one-time preparation in CI or scriptswhich CLI flag triggered setup
init, maintenance
No
Exit code and stderr are ignored
UserPromptSubmitWhen a prompt is submitted, before Claude processes it. Also fires on turns Claude Code starts on its ownno matcher supportYes
Blocks the prompt, so it never reaches Claude. See What a blocked prompt leaves behind
UserPromptExpansionWhen a user-typed command expands into a prompt, before it reaches Claude. Can block the expansioncommand name
your skill or command names
Yes
Blocks the expansion
PreToolUseBefore a tool call executes. Can block ittool name
Bash, Edit|Write, mcp__.*
Yes
Blocks the tool call
PermissionRequestWhen a tool call needs a permission decisiontool name
Bash, 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
PermissionDeniedWhen 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 verdicttool name
Bash, 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
PostToolUseAfter a tool call succeedstool name
Bash, Edit|Write, mcp__.*
No
Shows stderr to Claude; the tool already ran
PostToolUseFailureAfter a tool call failstool name
Bash, Edit|Write, mcp__.*
No
Shows stderr to Claude; the tool already failed
PostToolBatchAfter a full batch of parallel tool calls resolves, before the next model callno matcher supportYes
Stops the agentic loop before the next model call
NotificationWhen Claude Code sends a notificationnotification 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, plugin_notification
No
Exit code and stderr are ignored
MessageDisplayWhile assistant message text is displayedno matcher supportNo
The original text is displayed
SubagentStartWhen a subagent is spawnedagent type
general-purpose, Explore, Plan, custom agent names, or plugin-scoped names like ^my-plugin:reviewer$
No
Shows stderr to user only
SubagentStopWhen a subagent finishesagent type
same values as SubagentStart
Yes
Prevents the subagent from stopping
TaskCreatedWhen a task is being created via TaskCreateno matcher supportYes
Rolls back the task creation
TaskCompletedWhen a task is being marked as completedno matcher supportYes
Prevents the task from being marked as completed
StopWhen Claude finishes respondingno matcher supportYes
Prevents Claude from stopping, continues the conversation
StopFailureWhen the turn ends due to an API errorerror 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
No
Output and exit code are ignored, except terminalSequence
TeammateIdleWhen an agent team teammate is about to go idleno matcher supportYes
Prevents the teammate from going idle, so it continues working
InstructionsLoadedWhen a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded during a sessionload reason
session_start, nested_traversal, path_glob_match, include, compact
No
Exit code is ignored
ConfigChangeWhen a configuration file changes during a sessionconfiguration source
user_settings, project_settings, local_settings, policy_settings, skills
Yes
Blocks the configuration change from taking effect (except policy_settings)
CwdChangedWhen the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnvno matcher supportNo
Shows stderr to user only
DirectoryAddedWhen a working directory is added mid-session via /add-dir or the SDK register_repo_root control requesthow the directory was added
slash_command, register_repo_root
No
Stderr goes to the debug log; the directory is already added
FileChangedWhen a watched file changes on disk. The matcher field specifies which filenames to watchliteral filenames to watch (see FileChanged)
.envrc|.env
No
Shows stderr to user only
WorktreeCreateWhen a worktree is being created via --worktree, isolation: "worktree", or for a background session. Replaces default git behaviorno matcher supportYes
Any non-zero exit code causes worktree creation to fail
WorktreeRemoveWhen a worktree that a WorktreeCreate hook created is being removedno matcher supportYes
Any non-zero exit code causes worktree removal to fail if the directory still exists afterward. See WorktreeRemove for what happens to the directory
PreCompactBefore context compactionwhat triggered compaction
manual, auto
Yes
Blocks compaction
PostCompactAfter context compaction completeswhat triggered compaction
manual, auto
No
Shows stderr to user only
PreModelSwitchBefore Claude Code applies a model switch that you or a client requested. Can block the switchcanonical name of the model the session switches to, as described under PreModelSwitch
claude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.*
Yes
Blocks the model switch and shows stderr to the user
PostModelSwitchAfter the session's model changes, including changes Claude Code makes on its own, such as restoring the model when you resume a sessioncanonical name of the model the session switches to, as described under PreModelSwitch
claude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.*
No
Shows stderr to user only; the model already switched
ElicitationWhen an MCP server requests user input during a tool callMCP server name
your configured MCP server names
Yes
Declines the request, and no dialog appears
ElicitationResultAfter a user responds to an MCP elicitation, before the response is sent back to the serverMCP server name
same values as Elicitation
Yes
Blocks the response (action becomes decline)
SessionEndWhen a session terminateswhy the session ended
clear, resume, logout, prompt_input_exit, other
No
Shows stderr to user only

Where hooks live

LocationScopeShareable
~/.claude/settings.jsonAll your projectsNo, local to your machine
.claude/settings.jsonSingle projectYes, can be committed to the repo
.claude/settings.local.jsonSingle projectNo, gitignored when Claude Code saves a setting to it
Managed policy settingsOrganization-wideYes, admin-controlled
Plugin hooks/hooks.jsonWhen plugin is enabledYes, bundled with the plugin
Skill frontmatterThe rest of the session once the skill is invoked. See Hooks in skills and agentsYes, defined in the skill file
Subagent frontmatterWhile that subagent is runningYes, 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, and PermissionDenied. On other events, a hook with if set never runs
timeout optional
Seconds before canceling. Claude Code doesn't enforce it on a command hook you run with async: true. 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, PreModelSwitch, and PostModelSwitch, and to 10 on MessageDisplay. 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
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, 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
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 require CLAUDE_CODE_USE_POWERSHELL_TOOL since hooks spawn PowerShell directly. Ignored when args is 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_NAME or ${VAR_NAME} syntax. Only variables listed in allowedEnvVars are 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 as plugin: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 $ARGUMENTS as a placeholder for the hook input JSON. Escape with a backslash to include literal text: \$1.00 renders 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 identifier
prompt_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 input
transcript_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 transcript
cwdCurrent working directory when the hook is invoked
scratchpad_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 later
permission_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 section
effortObject 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 fired

How 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.

More free Claude Code tools

All tools →