Skip to main content
Hooks let you attach commands, HTTP calls, or LLM prompts to lifecycle events inside Claude Code. Whenever a matching event fires—such as before a tool runs or when the session ends—your hook executes. Use hooks for audit logging, enforcing team policies, sending notifications, or any side effect that should run automatically alongside Claude.

Hook events

Each hook is keyed by an event name. The full list of available events is:
EventWhen it fires
PreToolUseBefore a tool call is executed
PostToolUseAfter a tool call completes successfully
PostToolUseFailureAfter a tool call fails
NotificationWhen Claude Code emits a user-visible notification
UserPromptSubmitWhen the user submits a prompt
SessionStartAt the start of a new session (also fires on resume, clear, compact)
SessionEndWhen the session ends
StopWhen Claude finishes a response turn
StopFailureWhen a response turn ends in error
SubagentStartWhen a sub-agent is spawned
SubagentStopWhen a sub-agent finishes
PreCompactBefore context compaction
PostCompactAfter context compaction
PermissionRequestWhen a permission prompt is about to be shown
PermissionDeniedWhen a permission request is denied
SetupEarly one-time setup phase
TeammateIdleWhen a teammate agent becomes idle
TaskCreatedWhen a background task is created
TaskCompletedWhen a background task completes
ElicitationWhen an elicitation is triggered
ElicitationResultWhen an elicitation result is received
ConfigChangeWhen the configuration changes
WorktreeCreateWhen a git worktree is created
WorktreeRemoveWhen a git worktree is removed
InstructionsLoadedWhen CLAUDE.md instructions are loaded
CwdChangedWhen the working directory changes
FileChangedWhen a watched file changes

Configuring hooks

Hooks are defined in a hooks key in any Claude Code settings file:
  • User settings: ~/.claude/settings.json
  • Project settings: .claude/settings.json
  • Local settings: .claude/settings.local.json
  • Managed settings: /etc/claude-code/managed-settings.json (Linux) or MDM profile (macOS/Windows)
The hooks object is a map from event name to an array of matcher objects. Each matcher optionally filters by a string pattern (e.g., a tool name) and contains an array of hook commands.
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"About to run Bash\" >> /tmp/audit.log"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/usr/local/bin/notify-team.sh"
          }
        ]
      }
    ]
  }
}
You can also manage hooks interactively with the /hooks command inside a Claude Code session.

Hook types

Claude Code supports four hook types.
Runs a shell command. The hook input is passed as JSON on stdin.
{
  "type": "command",
  "command": "jq '.tool_name' >> /tmp/tools-used.log",
  "timeout": 10
}
FieldDescription
commandShell command string to execute
shell"bash" (default) or "powershell"
timeoutTimeout in seconds (positive number)
ifPermission rule syntax filter (e.g., "Bash(git *)") — hook runs only when the tool call matches
statusMessageCustom spinner message shown while the hook runs
onceRun once and remove after first execution
asyncRun in the background without blocking
asyncRewakeRun in background; exit code 2 wakes the model with a blocking error
Exit codes for command hooks:
  • 0 — success, execution continues
  • 2 — blocks the operation and surfaces stderr to Claude as an error
  • Any other non-zero — shows stderr to the user only (non-blocking)
Evaluates a prompt with an LLM. Use $ARGUMENTS in the prompt string to receive the hook input JSON.
{
  "type": "prompt",
  "prompt": "Does this tool call look safe? $ARGUMENTS\nRespond with {ok: true} or {ok: false, reason: '...'}",
  "timeout": 30
}
FieldDescription
promptPrompt string; use $ARGUMENTS to inject hook input JSON
modelModel to use (e.g., "claude-sonnet-4-6"); defaults to the small fast model
timeoutTimeout in seconds
ifPermission rule syntax filter
statusMessageCustom spinner message
onceRun once and remove
Runs a multi-turn LLM agent to verify a condition. The agent must call a structured output tool with {ok: boolean, reason?: string}.
{
  "type": "agent",
  "prompt": "Verify that all unit tests passed before this commit is allowed.",
  "timeout": 60
}
FieldDescription
promptDescription of what to verify; use $ARGUMENTS for hook input JSON
modelModel to use; defaults to Haiku
timeoutTimeout in seconds (default 60)
ifPermission rule syntax filter
statusMessageCustom spinner message
onceRun once and remove
POSTs the hook input JSON to a URL. Useful for sending events to external systems.
{
  "type": "http",
  "url": "https://hooks.example.com/claude-events",
  "headers": {
    "Authorization": "Bearer $MY_TOKEN"
  },
  "allowedEnvVars": ["MY_TOKEN"],
  "timeout": 10
}
FieldDescription
urlURL to POST the hook input JSON to
headersAdditional headers; values may reference $VAR_NAME env vars
allowedEnvVarsExplicit list of env var names allowed for interpolation in headers
timeoutTimeout in seconds
ifPermission rule syntax filter
statusMessageCustom spinner message
onceRun once and remove
Only env vars explicitly listed in allowedEnvVars are interpolated. All other $VAR references in headers resolve to empty strings.

Matcher patterns

The matcher field on a hook entry filters which invocations trigger the hook. It is matched against values relevant to the event—most often the tool name for PreToolUse / PostToolUse events. The if field inside an individual hook command provides finer-grained filtering using permission rule syntax (e.g., "Bash(git *)" or "Read(*.ts)"). A hook with an if condition only runs when the tool call matches that pattern.
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "./audit.sh",
            "if": "Bash(rm *)"
          }
        ]
      }
    ]
  }
}

Environment variables in hook scripts

Shell command hooks (type: "command") inherit the full process environment plus the following additional variables:
VariableDescription
CLAUDE_PROJECT_DIRAbsolute path to the project root directory
CLAUDE_ENV_FILEPath to a .sh file your hook can write export KEY=VALUE lines into, injected into subsequent Bash tool commands. Set for SessionStart, Setup, CwdChanged, and FileChanged events only.
CLAUDE_PLUGIN_ROOTPlugin or skill base directory (when the hook comes from a plugin or skill)
CLAUDE_PLUGIN_DATAPlugin data directory (when the hook comes from a plugin)
CLAUDE_PLUGIN_OPTION_<KEY>Plugin configuration option values (uppercased)
When running inside GitHub Actions with CLAUDE_CODE_SUBPROCESS_ENV_SCRUB set, sensitive credentials (e.g., ANTHROPIC_API_KEY, AWS_SECRET_ACCESS_KEY) are stripped from the hook subprocess environment to prevent prompt-injection exfiltration.

Example configurations

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$(date -u) PreToolUse\" >> /var/log/claude-audit.log"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$(date -u) PostToolUse\" >> /var/log/claude-audit.log"
          }
        ]
      }
    ]
  }
}

Use cases

  • Audit logging — record every tool call to a file or SIEM system
  • Policy enforcement — block specific commands (exit code 2 surfaces an error to Claude)
  • Notifications — send Slack or webhook messages when sessions complete
  • Environment setup — load .envrc or inject credentials at session start via CLAUDE_ENV_FILE
  • Quality gates — use agent hooks to verify tests pass before allowing commits

Security considerations

Hook commands run with the same OS privileges as Claude Code itself. Only configure hooks from sources you trust.
  • allowManagedHooksOnly — when set to true in managed settings, only hooks from managed settings (MDM/managed-settings.json) run. User, project, and local hooks are silently ignored. This lets IT administrators restrict hook execution to approved scripts.
  • disableAllHooks — setting this to true in any settings file disables all hooks and status line execution.
  • allowedHttpHookUrls — an allowlist of URL patterns HTTP hooks may target. When set, HTTP hooks with non-matching URLs are blocked. Supports * as a wildcard (e.g., "https://hooks.example.com/*").
  • httpHookAllowedEnvVars — restricts which env vars HTTP hooks may interpolate into headers; the effective set is the intersection of this list and each hook’s own allowedEnvVars.
  • Hook scripts should validate their inputs. The JSON input is controlled by Claude Code but may reflect user-supplied content.

Build docs developers (and LLMs) love