Hook Types and Events
Complete reference for all Claude Code hook events.
Overview
Hook Types and Events
Complete reference for all Claude Code hook events.
PreToolUse
When it fires: Before any tool is executed
Can block: Yes
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm install",
"description": "Install dependencies"
}
}
Output schema (to control execution):
{
"decision": "approve" | "block",
"reason": "Explanation",
"permissionDecision": "allow" | "deny" | "ask",
"permissionDecisionReason": "Why",
"updatedInput": {
"command": "npm install --save-exact"
}
}
Use cases:
- Validate commands before execution
- Block dangerous operations
- Modify tool inputs
- Log command attempts
- Ask user for confirmation
Example: Block force pushes to main
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "prompt",
"prompt": "Check if this git command is safe: $ARGUMENTS\
\
Block if: force push to main/master\
\
Return: {\\"decision\\": \\"approve\\" or \\"block\\", \\"reason\\": \\"explanation\\"}"
}
]
}
]
}
}
PostToolUse
When it fires: After a tool completes execution
Can block: No (tool already executed)
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.js",
"content": "..."
},
"tool_output": "File created successfully"
}
Output schema:
{
"systemMessage": "Optional message to display",
"suppressOutput": false
}
Use cases:
- Auto-format code after Write/Edit
- Run tests after code changes
- Update documentation
- Trigger CI builds
- Send notifications
Example: Auto-format after edits
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "prettier --write $CLAUDE_PROJECT_DIR",
"timeout": 10000
}
]
}
]
}
}
UserPromptSubmit
When it fires: User submits a prompt to Claude
Can block: Yes
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate factorial"
}
Output schema:
{
"decision": "approve" | "block",
"reason": "Explanation",
"systemMessage": "Message to user"
}
Use cases:
- Validate prompt format
- Block inappropriate requests
- Preprocess user input
- Add context to prompts
- Enforce prompt templates
Example: Require issue numbers in prompts
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if prompt mentions an issue number (e.g., #123 or PROJ-456): $ARGUMENTS\
\
If no issue number: {\\"decision\\": \\"block\\", \\"reason\\": \\"Please include issue number\\"}\
Otherwise: {\\"decision\\": \\"approve\\", \\"reason\\": \\"ok\\"}"
}
]
}
]
}
}
Stop
When it fires: Claude attempts to stop working
Can block: Yes
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": false
}
Output schema:
{
"decision": "block" | undefined,
"reason": "Why Claude should continue",
"continue": true,
"systemMessage": "Additional instructions"
}
Use cases:
- Verify all tasks completed
- Check for errors that need fixing
- Ensure tests pass before stopping
- Validate deliverables
- Custom completion criteria
Example: Verify tests pass before stopping
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "npm test && echo '{\\"decision\\": \\"approve\\"}' || echo '{\\"decision\\": \\"block\\", \\"reason\\": \\"Tests failing\\"}'"
}
]
}
]
}
}
Important: Check stop_hook_active to avoid infinite loops. If true, don't block again.
SubagentStop
When it fires: A subagent attempts to stop
Can block: Yes
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "SubagentStop",
"stop_hook_active": false
}
Output schema: Same as Stop
Use cases:
- Verify subagent completed its task
- Check for errors in subagent output
- Validate subagent deliverables
- Ensure quality before accepting results
Example: Check if code-reviewer provided feedback
{
"hooks": {
"SubagentStop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review the subagent transcript: $ARGUMENTS\
\
Did the code-reviewer provide:\
1. Specific issues found\
2. Severity ratings\
3. Remediation steps\
\
If missing: {\\"decision\\": \\"block\\", \\"reason\\": \\"Incomplete review\\"}\
Otherwise: {\\"decision\\": \\"approve\\", \\"reason\\": \\"Complete\\"}"
}
]
}
]
}
}
SessionStart
When it fires: At the beginning of a Claude session
Can block: No
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "SessionStart",
"source": "startup"
}
Output schema:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Context to inject into session"
}
}
Use cases:
- Load project context
- Inject sprint information
- Set environment variables
- Initialize state
- Display welcome messages
Example: Load current sprint context
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "cat $CLAUDE_PROJECT_DIR/.sprint-context.txt | jq -Rs '{\\"hookSpecificOutput\\": {\\"hookEventName\\": \\"SessionStart\\", \\"additionalContext\\": .}}'"
}
]
}
]
}
}
SessionEnd
When it fires: When a Claude session ends
Can block: No (cannot prevent session end)
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "SessionEnd",
"reason": "exit" | "error" | "timeout"
}
Output schema: None (hook output ignored)
Use cases:
- Save session state
- Cleanup temporary files
- Update logs
- Send analytics
- Archive transcripts
Example: Archive session transcript
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "cp $transcript_path $CLAUDE_PROJECT_DIR/.claude/archives/$(date +%Y%m%d-%H%M%S).jsonl"
}
]
}
]
}
}
PreCompact
When it fires: Before context window compaction
Can block: Yes
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "PreCompact",
"trigger": "manual" | "auto",
"custom_instructions": "User's compaction instructions"
}
Output schema:
{
"decision": "approve" | "block",
"reason": "Explanation"
}
Use cases:
- Validate state before compaction
- Save important context
- Custom compaction logic
- Prevent compaction at critical moments
Notification
When it fires: Claude needs user input (awaiting response)
Can block: No
Input schema:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "Notification"
}
Output schema: None
Use cases:
- Desktop notifications
- Sound alerts
- Status bar updates
- External notifications (Slack, etc.)
Example: macOS notification
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \\"Claude needs input\\" with title \\"Claude Code\\"'"
}
]
}
]
}
}