Practitioners use them to format after Edit/Write, block or rewrite tool calls, inject context on session start or prompt submit, keep Claude working via Stop, and notify when Claude needs input—without waiting for the model to follow CLAUDE.md. Configure them under a hooks key in ~/.claude/settings.json, project .claude/settings.json, .claude/settings.local.json, managed policy, plugin hooks/hooks.json, or skill/subagent YAML frontmatter, then inspect with /hooks. They ship with Claude Code on paid Claude plans and are not a separate SKU.
*Field guide compiled 2026-11-06 from the official documentation, pricing pages and changelogs linked below; every line links to the page that states it.*
What it does
- PreToolUse. Fires before a tool call executes (except EndConversation). Can block the call, ask the user, allow it through the remaining permission flow, or rewrite arguments. How: In settings JSON: hooks.PreToolUse.matcher (tool name, e.g. Bash or Edit|Write) plus hooks.type command and command. Decision via exit 2, or exit 0 with hookSpecificOutput.permissionDecision allow|deny|ask|defer and optional updatedInput. source
- PostToolUse. Fires after a tool call succeeds. Used to format, lint, or inject additionalContext. Cannot undo the tool, which already ran. Exit 2 only shows stderr to Claude. How: hooks.PostToolUse with matcher Edit|Write. Example command: jq -r '.toolinput.filepath' | xargs npx prettier --write in .claude/settings.json. Optional hookSpecificOutput.updatedToolOutput replaces the tool result. source
- UserPromptSubmit. Fires when a prompt is submitted, before Claude processes it, including turns Claude Code starts on its own. Can block the prompt or inject additionalContext. No matcher support. How: hooks.UserPromptSubmit.hooks with type command. Exit 2 blocks the prompt. JSON: hookSpecificOutput.hookEventName UserPromptSubmit plus additionalContext. Nest additionalContext inside hookSpecificOutput; a top-level field is ignored. source
- Stop. Fires when Claude finishes responding, not only at task completion, and not on user interrupt (API errors fire StopFailure). Exit 2 prevents stopping and continues the conversation. Claude Code overrides after eight consecutive blocks with no tool call in between. How: hooks.Stop.hooks with type command (exit 2 to continue) or type prompt with a prompt that returns {ok:false, reason:...}. Check stophookactive in stdin JSON and exit 0 if true. Raise the cap with CLAUDECODESTOPHOOKBLOCKCAP. source
- SessionStart. Fires when a session begins or resumes. Matcher values: startup, resume, clear, compact, fork. Cannot block. Plain-text stdout is added to Claude's context. Supports additionalContext, initialUserMessage (-p), sessionTitle, watchPaths, reloadSkills, and CLAUDEENVFILE. How: hooks.SessionStart with optional matcher compact to re-inject context after compaction, e.g. command echo 'Reminder: use Bun, not npm.' Only type command and mcptool are supported; mcptool is skipped at launch before MCP is available. source
- Notification. Fires when Claude Code sends a notification (permission wait, idle, auth, elicitation, agent, quota auto-resume, plugin). Used for desktop alerts so you can leave the terminal. How: Add to ~/.claude/settings.json under hooks.Notification with matcher empty for all types, or permissionprompt / idleprompt. macOS: osascript display notification. Linux: notify-send. Verify with /hooks. source
- matcher. Filters when a matcher group runs. Omitted, empty, or matches all. Letters/digits//- /spaces/,/| are exact names or |/, lists (Edit|Write). Any other character is an unanchored JavaScript regex. Tool events match toolname; MCP tools are mcpservertool. How: Set matcher on the group, then optional if on a handler using permission-rule syntax such as Bash(git ) or Edit(.ts). if is only evaluated on PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, and PermissionDenied; on other events a hook with if never runs. source
- Exit code 2. Blocking error. On PreToolUse it blocks the tool call; on UserPromptSubmit it rejects the prompt; on Stop it prevents stopping. Stderr is the reason unless JSON supplies a blocking reason. Exit 0 with no JSON means no decision (normal permission flow). Other codes are non-blocking errors unless onFailure is block. How: In a command hook script: echo reason &2; exit 2. Do not use exit 1 for policy: exit 1 without valid JSON is a non-blocking error. Mix of exit 2 plus JSON still blocks; JSON cannot override the block. source
- permissionDecision. PreToolUse JSON field inside hookSpecificOutput. deny cancels the call and feeds permissionDecisionReason to Claude. allow skips the interactive prompt but still honors deny/ask rules and some forced prompts. ask shows the permission prompt. defer (in -p) ends the turn with the tool call preserved. deny holds even in bypassPermissions. How: Exit 0 and print one JSON object: {hookSpecificOutput:{hookEventName:PreToolUse,permissionDecision:deny,permissionDecisionReason:...}}. Optional updatedInput next to those fields rewrites tool arguments before run. source
- Hook locations. Where a hook is defined sets scope: user file all projects; project settings shareable in git; local settings gitignored; managed policy org-wide; plugin hooks/hooks.json when enabled; skill frontmatter for the rest of the session after invoke; subagent frontmatter while that subagent runs. Cloud sessions do not read local ~/.claude/settings.json. How: ~/.claude/settings.json, .claude/settings.json, .claude/settings.local.json, managed policy, plugin hooks/hooks.json, or YAML hooks: in skill/subagent frontmatter. Disable with disableAllHooks true (cannot disable managed hooks from user/project/local). Type /hooks to browse. source
- onFailure. On command and http hooks, continue or block the triggering action when the hook fails to start, times out, returns a non-0/2 exit, HTTP non-2xx, or invalid JSON. Default on most events is continue, so a mistyped path lets the action through. Requires Claude Code v2.1.295 or later. How: Set onFailure: block on the handler in settings.json, e.g. a PreToolUse Bash command hook. No effect on Stop/SubagentStop/TaskCompleted/TeammateIdle or async command hooks. source
- PermissionRequest. Fires when Claude Code is about to ask for permission (or would auto-deny a call that cannot prompt). JSON can allow or deny on the user's behalf. Exit code 2 is not honored; deny through the decision object. How: hooks.PermissionRequest matcher e.g. ExitPlanMode. stdout: {hookSpecificOutput:{hookEventName:PermissionRequest,decision:{behavior:allow}}}. Keep matchers narrow; empty or . auto-approves every prompt. source
Modes
- command. Runs a shell command. JSON event on stdin; results via exit codes and stdout. Default timeout 600 seconds (30s on UserPromptSubmit/PreModelSwitch/PostModelSwitch; 10s on MessageDisplay). Most common type for format/block/notify scripts. source
- http. POSTs the same JSON to a URL. Response body uses the same JSON output schema. Non-2xx is a non-blocking error unless onFailure is block. Header env interpolation requires allowedEnvVars. source
- mcptool. Calls a tool on a configured MCP server; text output is treated like command-hook stdout. Skipped on SessionStart at launch and on every Setup event because MCP is not ready yet. source
- prompt. Single-turn LLM evaluation. Model returns JSON with ok true/false. Not deterministic. On Stop, ok false feeds reason back so Claude keeps working unless impossible is true. source
- agent. Experimental. Spawns a subagent that can Read/Grep/Glob (up to 50 tool-use turns, default timeout 60s) then returns ok/reason. Prefer command hooks for production enforcement. source
- auto. Permission mode: classifier reviews actions. Deny/ask rules and PreToolUse hooks still apply before the classifier. A PreToolUse allow does not bypass deny rules. Users report permissionDecision ask can be ignored in this mode. source
- bypassPermissions. Skips permission prompts except actions no mode auto-approves. A hook permissionDecision deny still blocks. Only for isolated containers/VMs. source
- plan. Read/explore without editing source. PreToolUse allow has no effect on file edits and other non-read-only calls except ExitPlanMode; updatedInput still applies. Approve the plan or switch mode to lift that limit. source
Pricing and limits
- Free: $0. Limited usage every 5 hours and every week. Claude Code: No. source
- Pro: $17 per month with annual subscription discount ($200 billed up front). $20 if billed monthly.. Claude Code: Yes. More usage than Free, every 5 hours and every week. Limit every 5 hours: 1x. Limit every week: 1x. Claude Code shares the same usage pool as chat on web, desktop, and mobile. source
- Max: $100. Billed monthly. Everything in Pro, plus 5x Pro usage every 5 hours and every week. $100 a month in API credits to build on the Claude Platform. Fable included, up to half your weekly limit. Claude Code: Yes. source
- Ultra: $200. Billed monthly. Everything in Max, plus no 5-hour limit for verified accounts, 10x Pro usage every week, and $200 a month in API credits (up from $100 on Max). Claude Code: Yes. source
- Enterprise: $20 per seat per month plus usage billed at API rates. Billed annually. Claude Code: Yes. Self-serve and sales-assisted Enterprise both include Claude Code. source
Recent changes
- 2026-11-03: v2.1.317 added onFailure: "block" for Agent SDK hook callbacks so a callback that fails blocks the action instead of letting it run. source
- 2026-10-30: v2.1.314: hooks matching Bash now run on Monitor tool shell commands (can block or ask, not approve or rewrite). Hook if conditions such as Monitor(tail ) match Monitor the way Bash(tail ) matches Bash. PreCompact hook stdout cut at 10,000 characters. In Cowork cloud auto mode, a hook allow of an MCP tool counts as no answer so the classifier or a prompt decides. source
- 2026-10-23: v2.1.310 Windows: PreToolUse and PermissionRequest hooks no longer let a tool call through when Git Bash is missing and the hook command is only quoted strings, such as a quoted path. source
- 2026-10-22: v2.1.309: hooks that run in PowerShell now block when the program exits 2. On Windows, PreToolUse and PermissionRequest hooks with shell powershell no longer fail open when Git for Windows is not installed. source
Alternatives
- skills. Give Claude extra instructions and executable slash commands the model may choose to run. Non-deterministic: the model decides. Hooks fire at fixed points regardless of what the model chooses. source
- subagents. Run tasks in isolated contexts with their own tools and optional Stop/PreToolUse hooks in frontmatter (converted to SubagentStop). Cost extra tokens; they do not replace a deterministic PreToolUse deny. source
- plugins. Package hooks plus skills, MCP, mods, and monitors to share. Plugin command hooks run as shell commands with full user privileges outside the sandbox; review hooks/hooks.json before install. source
- Permission rules. deny/ask/allow in settings match tool name and argument patterns without spawning a process. A PreToolUse hook can inspect full command JSON and rewrite input; a blocking hook (exit 2) also beats allow rules. Use both: deny for .env, hooks for content checks. source
- CLAUDE.md. Static project context loaded every session. Claude may still skip a line. Best practices: if you keep adding emphasis and Claude still misses it, move the rule to a hook. source
Traps
- Exit code 1 without valid JSON is a non-blocking error on most events, so a policy script that uses the conventional Unix failure code lets the tool run. Use exit 2 to block, or set onFailure to block so crashes and missing files also stop the action. source
- A PreToolUse command/http/mcptool hook that hits timeout does not block the tool call unless onFailure is block (Agent SDK callback timeouts do block). Do not rely on a stalled hook as a gate. source
- Command hooks and plugin monitors run with full user permissions outside the sandbox. Permission rules cover Claude's tool calls, not the hook process itself. Server-managed hook definitions show a security approval dialog in interactive sessions. source
- Claude Code overrides a Stop hook after eight consecutive blocks with no tool call between them. Parse stophookactive from stdin and exit 0 when it is true, or raise CLAUDECODESTOPHOOKBLOCKCAP. source
- Matching only Bash misses PowerShell on Windows and file writes done via shell. For every on-disk change, also match Bash|PowerShell or use FileChanged/Stop. A Bash matcher on PreToolUse also sees Monitor commands as of v2.1.314 but allow and updatedInput have no effect unless the matcher includes Monitor. source
- If a PreToolUse command starts an interpreter whose target script is missing, CPython exits 2 and Claude Code blocks the tool as a deliberate deny. A missing executable instead fails open and the tool runs. Do not disable a guard by renaming its script while settings still point at it; remove the settings entry or make the script a no-op that exits 0. source
- Users report that a PreToolUse hook returning permissionDecision ask is silently ignored when permissions.defaultMode is auto: no prompt, the call proceeds, while exit 2 still blocks. A hook that only asks is not a reliable guardrail in auto mode. source
- The if Bash filter is best-effort: when Claude Code cannot parse the command it runs the hook anyway, and patterns with more than the command name also run on $() / backticks. Use the permission system for a hard allow/deny. extra stdout before JSON (shell profile echo) makes the decision a no-op with no transcript error. source