What is a hook
A hook is a shell script (or any command) executed automatically when Claude Code triggers an event. Comparable to git hooks, but for the agent environment. The script receives a JSON via stdin describing the event context.
Main events
| Event | Moment | Can block |
|---|---|---|
SessionStart | Startup or resume | No |
SessionEnd | Session ends | No |
UserPromptSubmit | Before prompt processing | Yes |
PreToolUse | Before a tool call | Yes |
PostToolUse | After a tool succeeds | No |
PreCompact | Before context compaction | Yes |
Notification | Claude sends a notification | No |
Stop | Claude finishes responding | Yes |
SubagentStop | A sub-agent ends | Yes |
Agent Teams events (v2.1.32+)
| Event | Use |
|---|---|
TeammateIdle | A team member is about to go idle |
TaskCompleted | A task is about to be marked complete |
ConfigChange | A config file changed during the session |
These events allow implementing quality gates at the agent team level: preventing a task from being marked completed if tests fail, for example.
Configuration in settings.json
{ "hooks": { "PostToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": ".claude/hooks/auto-format.sh", "timeout": 10000, "async": true }] }], "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": ".claude/hooks/security.sh" }] }] }}Configuration fields
| Field | Usage |
|---|---|
command | Shell command executed via a shell interpreter |
args | Exec form: array of strings spawned directly without a shell. Avoids injection risks and quoting issues. Overrides command when present. (v2.1.139) |
timeout | Max duration in ms (default: 60000) |
async | If true, Claude continues without waiting for completion |
continueOnBlock | PostToolUse only: if true, a block feeds Claude the rejection reason and continues the turn instead of stopping it. Claude can then self-correct. (v2.1.139) |
Exit codes
| Code | Meaning |
|---|---|
0 | Success, continue normally |
2 | Block the action (stderr message visible to Claude) |
| Other non-0 | Error surfaced to Claude |
For PreToolUse hooks, exit 2 prevents the tool from executing. Claude reads the stderr message and can adapt its behavior.
Synchronous vs Asynchronous
Synchronous hooks (default): Claude waits for the script to finish. Suited for validation and type checking. Asynchronous hooks (async: true): Claude continues immediately. Suited for formatting, notifications, and logging, where feedback is not needed for the next step.
stdin data flow
# Example JSON received on stdin (PostToolUse){ "tool_name": "Edit", "tool_input": { "file_path": "src/auth.ts" }, "tool_response": { "success": true }, "hook_event_name": "PostToolUse"}The Stop event also includes a last_assistant_message field since v2.1.47, giving direct access to Claude’s last response without parsing transcripts.
Enter your email to read the full card and get the complete PDF bundle.
All content is free and open-source. We just ask for your email.