Hooks in Claude Code
Run shell scripts, call HTTP endpoints, or invoke an AI model automatically at key points in Claude Code's lifecycle — without changing a single prompt.
What Are Hooks?
Hooks are user-defined automations that Claude Code calls at specific points during a session. You define them in your settings.json. When an event fires — before a tool runs, after a file is written, when a session starts — Claude Code passes JSON context to your handler and optionally acts on what it returns.
Hook Lifecycle
Events fire in a predictable order. Each event is a named point where Claude Code can call your hook.
SessionStart—Session begins or resumesUserPromptSubmit—User sends a promptPreToolUse—Before any tool callPostToolUse—After a tool succeedsStop—Claude finishes respondingSessionEnd—Session terminatesConfiguration Structure
Hooks live in .claude/settings.json (or your user-level settings). The JSON has three nesting levels: event → matcher → handler.
PostToolUse"Write|Edit""command"Hook Types
Each handler has a type field. Choose the right type based on what you want the hook to do.
commandRun a shell script. Input arrives on stdin; control Claude via exit code and stdout JSON.
type: "command", command: "./hook.sh"httpPOST the hook input to a URL. Your service responds with the same JSON schema as command hooks.
type: "http", url: "http://localhost:8080/hook"promptAsk a Claude model to evaluate the event and return {ok: true/false}. No script needed.
type: "prompt", prompt: "Should Claude stop? $ARGUMENTS"agentSpawn a mini sub-agent that can read files, search code, then return a decision. Experimental.
type: "agent", prompt: "Verify tests pass. $ARGUMENTS"mcp_toolCall a tool on an already-connected MCP server. The tool's text output is treated like command stdout.
type: "mcp_tool", server: "audit", tool: "log_event"Exit Codes (Command Hooks)
How your script exits tells Claude Code what to do next.
Worked Example: Auto-Format Python Files
Every time Claude writes or edits a .py file, automatically run the Black formatter on it — so the codebase stays clean without any manual steps.
.claude/hooks/format-python.sh and make it executable with chmod +x..claude/settings.jsonPostToolUse, matched to Write or Edit tool calls./hooks menu/hooks inside Claude Code to inspect every configured hook — the event, matcher, command, and which settings file it came from.Key Events Reference
* PostToolUse can return decision: "block" in JSON to prompt Claude with a reason — the tool already ran but Claude is asked to reconsider.
Where to Define Hooks
The location of the settings file determines the scope of the hook.
~/.claude/settings.json.claude/settings.json.claude/settings.local.jsonQuick Reference: 3 Useful Patterns
Return exit 2 from a script when the command is unsafe. Claude sees the stderr message as the reason.
Print plain text to stdout on exit 0 — Claude Code adds it to Claude's context before the first prompt.
Return { "decision": "block", "reason": "..." } to keep Claude working until your quality gate passes.
$CLAUDE_PROJECT_DIR in your command paths so they work regardless of the current working directory when the hook fires.claude --debug-file /tmp/claude-debug.txt. Every hook execution, exit code, stdout, and stderr is written to the log file."async": true to a command hook to run it in the background. Claude keeps working immediately — ideal for long-running tasks like test suites.What's Next
Hooks give you fine-grained control over Claude's actions. The next lesson covers Memory Management — how Claude remembers context across sessions.