Skip to main content
Featured Hooks Automation Security Configuration

Claude Code Hooks: Complete Guide to Automated Policies

Master Claude Code's Hooks system. Learn how to automatically enforce policies, validate actions, and create guardrails with PreToolUse, PostToolUse, and Stop hooks.

January 10, 2026 18 min read By ClaudeWorld

Hooks are Claude Code’s automation layer—they let you intercept, validate, and modify Claude’s actions automatically. This guide covers every hook type, configuration patterns, and practical use cases.

What are Hooks?

According to the official documentation, Hooks are:

  • Event interceptors that run before/after Claude’s actions
  • Policy enforcers that automatically validate behavior
  • Guardrails that prevent dangerous operations
  • Automations that trigger custom logic

Think of Hooks as middleware for Claude Code—they sit between Claude’s intentions and actual execution.

Hook Architecture

┌────────────────────────────────────────────────────────────┐
│                     Claude Code CLI                         │
│                                                            │
│  User Prompt ──────────────────────────────────────►       │
│       │                                                    │
│       ▼                                                    │
│  ┌─────────────────┐                                       │
│  │ UserPromptSubmit│  Hook: Validate/modify prompt         │
│  │ Hook            │                                       │
│  └────────┬────────┘                                       │
│           │                                                │
│           ▼                                                │
│  ┌─────────────────┐                                       │
│  │ PreToolUse      │  Hook: Approve/deny/modify action     │
│  │ Hook            │                                       │
│  └────────┬────────┘                                       │
│           │                                                │
│           ▼                                                │
│  ┌─────────────────┐                                       │
│  │ Tool Execution  │  Actual action (Read, Write, Bash)    │
│  └────────┬────────┘                                       │
│           │                                                │
│           ▼                                                │
│  ┌─────────────────┐                                       │
│  │ PostToolUse     │  Hook: React to results               │
│  │ Hook            │                                       │
│  └────────┬────────┘                                       │
│           │                                                │
│           ▼                                                │
│  ┌─────────────────┐                                       │
│  │ Stop Hook       │  Hook: Verify completion              │
│  └────────┬────────┘                                       │
│           │                                                │
│           ▼                                                │
│      Response ◄────────────────────────────────────────    │
└────────────────────────────────────────────────────────────┘

Hook Events

EventWhen TriggeredCommon Use Cases
SessionStartSession beginsLoad context, verify setup
UserPromptSubmitUser sends promptAdd context, validate input
PreToolUseBefore tool runsApprove/deny actions
PostToolUseAfter tool runsAudit, react to results
StopClaude stopsVerify completion
SubagentStopSubagent completesEnsure task completion

Hook Configuration

Hooks are configured in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [...],
    "PostToolUse": [...],
    "Stop": [...],
    "SessionStart": [...],
    "UserPromptSubmit": [...],
    "SubagentStop": [...]
  }
}

Hook Structure

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolName or *",
        "hooks": [
          {
            "type": "prompt | command",
            "prompt": "Instructions for Claude",
            "command": "shell command to run",
            "onFailure": "block | warn | ignore"
          }
        ]
      }
    ]
  }
}

PreToolUse Hooks

Purpose: Intercept actions before they execute. Can approve, deny, or modify.

Block Dangerous Commands

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "prompt",
        "prompt": "Check if command contains destructive operations (rm -rf, DROP TABLE, git push --force). If yes, return 'ask' for confirmation. Otherwise return 'approve'."
      }]
    }]
  }
}

Validate File Access

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Read",
      "hooks": [{
        "type": "prompt",
        "prompt": "If the file path contains .env, secrets, or credentials, return 'block' with explanation. Otherwise return 'approve'."
      }]
    }]
  }
}

Security Scan Before Write

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "prompt",
        "prompt": "Scan the content for potential secrets (API keys, passwords, tokens). If found, return 'block' with the line numbers. Otherwise return 'approve'."
      }]
    }]
  }
}

SQL Injection Prevention

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "prompt",
        "prompt": "If the code contains string concatenation with SQL keywords (SELECT, INSERT, UPDATE, DELETE), return 'block' with SQL injection warning. Otherwise return 'approve'."
      }]
    }]
  }
}

PostToolUse Hooks

Purpose: React after an action completes. Good for logging, auditing, and triggering follow-up.

Audit File Changes

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "prompt",
        "prompt": "Log the file path and a summary of changes. If the file is in auth/, payment/, or admin/, note that security review is recommended."
      }]
    }]
  }
}

Auto-Run Tests After Edit

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit",
      "hooks": [{
        "type": "command",
        "command": "npm test --passWithNoTests",
        "onFailure": "warn"
      }]
    }]
  }
}

Trigger Security Scan

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "prompt",
        "prompt": "If the written file is in auth/, payment/, or admin/ directories, trigger security-auditor agent for review."
      }]
    }]
  }
}

Stop Hooks

Purpose: Verify completion criteria before Claude stops. Essential for quality gates.

Verify Tests Run

{
  "hooks": {
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "Check if code was modified (Write or Edit used). If yes, verify tests were run. If no tests ran, block and explain that tests are required before completion."
      }]
    }]
  }
}

Security Review Required

{
  "hooks": {
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "If code in auth/, payment/, or admin/ was modified, verify security-auditor was run. If not, block and explain security review requirement."
      }]
    }]
  }
}

Documentation Check

{
  "hooks": {
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "If public API was modified, verify documentation was updated. If not, remind about documentation update before approving."
      }]
    }]
  }
}

Coverage Gate

{
  "hooks": {
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "npm run test:coverage -- --coverageThreshold='{\"global\":{\"lines\":80}}'",
        "onFailure": "block"
      }]
    }]
  }
}

SessionStart Hooks

Purpose: Initialize context when a session begins.

Load Project Context

{
  "hooks": {
    "SessionStart": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "Read CLAUDE.md and summarize key project policies. Load any saved memory entities related to recent work."
      }]
    }]
  }
}

Environment Check

{
  "hooks": {
    "SessionStart": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "node --version && npm --version",
        "onFailure": "warn"
      }]
    }]
  }
}

UserPromptSubmit Hooks

Purpose: Process user input before Claude acts.

Add Context

{
  "hooks": {
    "UserPromptSubmit": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "If the prompt mentions 'deploy' or 'release', add reminder about running full test suite first."
      }]
    }]
  }
}

Keyword Detection

{
  "hooks": {
    "UserPromptSubmit": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "If prompt contains 'urgent' or 'quickly', add reminder to maintain code quality despite time pressure."
      }]
    }]
  }
}

SubagentStop Hooks

Purpose: Verify subagent task completion.

Verify Subagent Results

{
  "hooks": {
    "SubagentStop": [{
      "matcher": "*",
      "hooks": [{
        "type": "prompt",
        "prompt": "Review subagent results. If the task was security-related and no security-auditor was used, flag for review."
      }]
    }]
  }
}

Hook Types

Prompt Hooks

Use Claude to evaluate conditions:

{
  "type": "prompt",
  "prompt": "Your evaluation instructions..."
}

Prompt Hook Responses:

  • approve - Allow the action
  • deny or block - Block the action
  • ask - Ask user for confirmation
  • Any other text - Treated as modification/context

Command Hooks

Run shell commands:

{
  "type": "command",
  "command": "npm test",
  "onFailure": "block | warn | ignore"
}

onFailure Options:

  • block - Stop if command fails
  • warn - Show warning but continue
  • ignore - Silently ignore failure

Matchers

Specific Tool Matcher

{
  "matcher": "Bash"
}

Wildcard Matcher

{
  "matcher": "*"
}

Multiple Tools

Create separate entries for each tool:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Write", "hooks": [...] },
      { "matcher": "Edit", "hooks": [...] },
      { "matcher": "Bash", "hooks": [...] }
    ]
  }
}

Practical Examples

Example 1: Security-First Configuration

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "prompt",
          "prompt": "Block if command contains: rm -rf, DROP, TRUNCATE, --force, sudo rm. Return 'block' with explanation, or 'approve'."
        }]
      },
      {
        "matcher": "Read",
        "hooks": [{
          "type": "prompt",
          "prompt": "Block if path contains: .env, secret, credential, private, key. Return 'block' or 'approve'."
        }]
      },
      {
        "matcher": "Write",
        "hooks": [{
          "type": "prompt",
          "prompt": "Scan for secrets (API keys starting with sk_, tokens, passwords). Block if found."
        }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [{
          "type": "prompt",
          "prompt": "If file in auth/payment/admin, recommend security-auditor review."
        }]
      }
    ],
    "Stop": [
      {
        "matcher": "*",
        "hooks": [{
          "type": "prompt",
          "prompt": "If auth/payment code modified, verify security-auditor ran. Block if not."
        }]
      }
    ]
  }
}

Example 2: Quality Gate Configuration

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [{
          "type": "command",
          "command": "npm run lint -- --quiet",
          "onFailure": "warn"
        }]
      },
      {
        "matcher": "Edit",
        "hooks": [{
          "type": "command",
          "command": "npm run typecheck",
          "onFailure": "warn"
        }]
      }
    ],
    "Stop": [
      {
        "matcher": "*",
        "hooks": [{
          "type": "prompt",
          "prompt": "Verify: 1) Tests ran and passed, 2) Lint passed, 3) Types check. Block if any failed."
        }]
      },
      {
        "matcher": "*",
        "hooks": [{
          "type": "command",
          "command": "npm test -- --passWithNoTests",
          "onFailure": "block"
        }]
      }
    ]
  }
}

Example 3: Audit Trail Configuration

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [{
          "type": "command",
          "command": "echo \"Session started: $(date)\" >> .claude/audit.log",
          "onFailure": "ignore"
        }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [{
          "type": "prompt",
          "prompt": "Log: file path, action type, timestamp. Append to .claude/audit.log"
        }]
      },
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "prompt",
          "prompt": "Log: command executed, exit status. Append to .claude/audit.log"
        }]
      }
    ]
  }
}

Example 4: Team Workflow Configuration

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "prompt",
          "prompt": "If command is 'git push', verify: 1) Tests pass, 2) Lint passes, 3) PR exists or being created. Block push without PR."
        }]
      }
    ],
    "Stop": [
      {
        "matcher": "*",
        "hooks": [{
          "type": "prompt",
          "prompt": "If feature work completed, remind to: 1) Update documentation, 2) Create/update PR, 3) Request review."
        }]
      }
    ]
  }
}

Hooks vs Other Tools

FeatureHooksPermissionsCLAUDE.md
Automatic✅ Yes✅ Yes❌ Manual
Conditional✅ Yes❌ No❌ No
Custom logic✅ Yes❌ No❌ No
Shell commands✅ Yes❌ No❌ No
Easy configMediumEasyEasy

When to Use Hooks

Use Hooks when:

  • You need conditional logic
  • You want automatic enforcement
  • You need to run external commands
  • You want audit trails

Use Permissions when:

  • Simple allow/deny rules
  • No conditional logic needed
  • Static file access control

Use CLAUDE.md when:

  • Guidelines and preferences
  • Context and documentation
  • Non-enforced recommendations

Debugging Hooks

Hook Not Triggering

  1. Check matcher matches the tool name exactly
  2. Verify JSON syntax is correct
  3. Check hooks array structure

Command Hook Failing

  1. Test command manually in terminal
  2. Check command path and permissions
  3. Review onFailure setting

Prompt Hook Not Working

  1. Make prompt instructions clear
  2. Ensure response options are explicit
  3. Test with simpler prompt first

Best Practices

1. Start Simple

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "prompt",
        "prompt": "Block rm -rf commands. Return 'block' or 'approve'."
      }]
    }]
  }
}

2. Be Explicit About Responses

{
  "type": "prompt",
  "prompt": "Evaluate X. Return exactly one of: 'approve', 'block', or 'ask'."
}

3. Use onFailure Appropriately

// Critical: block on failure
{ "command": "npm test", "onFailure": "block" }

// Important: warn on failure
{ "command": "npm run lint", "onFailure": "warn" }

// Optional: ignore on failure
{ "command": "npm run optional-check", "onFailure": "ignore" }

4. Layer Your Hooks

PreToolUse → Prevent bad actions
PostToolUse → Audit and react
Stop → Verify completion

Getting Started

Today:

  1. Add one PreToolUse hook for dangerous commands
  2. Add one Stop hook for test verification
  3. Test with simple scenarios

This week:

  1. Build out security hooks
  2. Add quality gate hooks
  3. Test with real workflows

This month:

  1. Develop comprehensive hook configuration
  2. Add team-specific policies
  3. Create audit trail system

Hooks transform Claude Code from a reactive assistant to a policy-enforcing partner. Set them once, trust them always.

Sources: Claude Code Documentation, Claude Code GitHub