Claude Code Hooks: Automate Your Workflow Without Leaving the Terminal
A beginner-friendly guide to Claude Code hooks — the automation system that runs shell commands, HTTP calls, and LLM prompts at key lifecycle points.
#What Are Hooks?
Every time Claude Code does something — starts a session, edits a file, runs a command, finishes a task — it passes through a lifecycle event. Hooks let you attach your own automation to those events.
Think of hooks as "if this, then that" for your coding workflow:
- When Claude edits a file → auto-run your linter
- When Claude finishes a task → send a Slack notification
- When Claude is about to run a dangerous command → block it
- When a session starts → load custom environment variables
Hooks shipped in Claude Code v2.1.51 and have been expanded significantly through March 2026. If you've been doing manual quality checks after every Claude Code edit, hooks can eliminate most of that friction.
#The Four Hook Types
Claude Code supports four different hook handlers. Start with command hooks — they're the simplest and cover most use cases.
#1. Command Hooks (Shell)
Run any shell command. The most common and easiest to set up.
{ "type": "command", "command": "npm run lint -- --fix"}#2. HTTP Hooks
POST JSON to a URL and receive JSON back. Great for external integrations.
{ "type": "http", "url": "https://your-server.com/webhook", "headers": { "Authorization": "Bearer $API_KEY" }}Headers support environment variable interpolation via allowedEnvVars.
#3. Prompt Hooks
Run a single LLM evaluation. Useful for AI-powered quality gates.
{ "type": "prompt", "prompt": "Review this code change for security issues. Return PASS or FAIL with reasoning."}#4. Agent Hooks
Spin up a multi-turn sub-agent with tool access. The most powerful (and most expensive) option.
{ "type": "agent", "prompt": "Review the changes made in this session and write a summary to CHANGELOG.md"}Want step-by-step guides for this and more?
ClawDocx Pro includes 500+ curated prompts, setup guides, SKILL.md files, and templates — everything to make your AI agent unstoppable.
See plans & pricing#Hook Events: When Do They Fire?
Here's the full lifecycle, in order:
| Event | When It Fires | Common Use |
|---|---|---|
SessionStart | Session begins or resumes | Load env vars, initialize tools |
UserPromptSubmit | You submit a prompt | Input validation, logging |
PreToolUse | Before a tool call executes | Block dangerous commands, add safety checks |
PostToolUse | After a tool call succeeds | Auto-lint, auto-format, log changes |
PostToolUseFailure | After a tool call fails | Error reporting, fallback logic |
SubagentStart | A sub-agent is spawned | Track delegation |
SubagentStop | A sub-agent finishes | Collect results |
TaskCompleted | Task is marked complete | Notifications, final checks |
Stop | Claude finishes responding | Summary generation |
Notification | Claude sends a notification | Custom notification routing |
InstructionsLoaded | CLAUDE.md files are loaded | Validate instructions |
ConfigChange | Config file changes | Auto-reload settings |
The two you'll use most: PostToolUse (react to changes) and PreToolUse (prevent bad actions).
#Your First Hook: Auto-Lint After Edits
Let's set up a hook that runs ESLint every time Claude edits a file.
#Step 1: Open Your Settings
Hooks live in your Claude Code settings file. Open it with:
# In a Claude Code session/hooksOr edit directly:
# User-level settings~/.claude/settings.json# Project-level settings.claude/settings.json#Step 2: Add the Hook
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "type": "command", "command": "npx eslint --fix \"$CLAUDE_FILE_PATH\"", "timeout": 10000 } ] }}What this does:
- Fires after every
WriteorEdittool call (thematcherfilters by tool name) - Runs ESLint with
--fixon the changed file - Times out after 10 seconds so it doesn't block your session
#Step 3: Test It
Ask Claude to edit any file in your project. After the edit, you should see the hook fire in the output — ESLint runs automatically and fixes any issues.
#Practical Hook Recipes
#Auto-Run Tests After Changes
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "type": "command", "command": "npm test -- --related --bail", "timeout": 30000 } ] }}#Block Destructive Commands
Prevent Claude from running rm -rf, DROP TABLE, or other dangerous operations:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "type": "command", "command": "echo $CLAUDE_TOOL_INPUT | grep -qiE '(rm -rf|drop table|truncate|format)' && echo '{\"decision\": \"block\", \"reason\": \"Destructive command blocked by hook\"}' || echo '{\"decision\": \"allow\"}'", "timeout": 5000 } ] }}The PreToolUse event supports decision control — your hook can return allow, block, or ask (prompt the user).
#Send a Notification When Done
Get a desktop notification when Claude finishes a long task:
{ "hooks": { "TaskCompleted": [ { "type": "command", "command": "osascript -e 'display notification \"Claude finished the task\" with title \"Claude Code\"'" } ] }}On Linux, swap osascript for notify-send:
{ "command": "notify-send 'Claude Code' 'Task completed'"}#Webhook to Slack/Discord on Completion
{ "hooks": { "TaskCompleted": [ { "type": "http", "url": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL", "headers": { "Content-Type": "application/json" } } ] }}The hook sends the event's JSON context as the POST body — Slack/Discord can parse the task summary from it.
#AI-Powered Code Review Gate
Use a prompt hook to review every file write:
{ "hooks": { "PostToolUse": [ { "matcher": "Write", "type": "prompt", "prompt": "Review the file that was just written. Check for: hardcoded secrets, SQL injection, XSS vulnerabilities, and missing error handling. If any issues found, list them clearly." } ] }}Prompt hooks use your active model and count toward your usage. Use them selectively on high-risk events rather than on every tool call.
#Hook Input and Output
Every hook receives a JSON payload on stdin (for command hooks) or as the POST body (for HTTP hooks). The payload includes:
- Event type and session metadata
- Tool name and input/output (for tool-related events)
- File paths affected
- Session ID and working directory
For PreToolUse hooks, you can return a JSON decision:
{ "decision": "block", "reason": "This command is not allowed in production branches"}Decision options:
"allow"— proceed normally"block"— prevent the tool call, show reason to Claude"ask"— prompt the user for confirmation
#Async Hooks
Some hooks can run asynchronously — they fire but don't block Claude's workflow. This is useful for logging, analytics, and notifications where you don't need the result before continuing.
Events that support async: Notification, ConfigChange, InstructionsLoaded, WorktreeCreate, WorktreeRemove.
#Tips for OpenClaw Users
If you're running Claude Code through OpenClaw's ACP bridge, hooks still work — they execute in Claude Code's process, not OpenClaw's. This means:
- Local hooks (linting, testing) run on the machine where Claude Code is installed
- HTTP hooks can call your OpenClaw gateway or any external endpoint
- Notification hooks can route through OpenClaw's messaging channels
A powerful pattern: use a TaskCompleted HTTP hook to ping your OpenClaw gateway, which then delivers a formatted summary to your Telegram/Discord/Slack.
#What to Try Next
Start simple — one PostToolUse hook for linting or testing. Once you're comfortable with the pattern, layer in:
PreToolUsesafety gates for production reposTaskCompletednotifications for async work- Prompt hooks for automated code review
- Agent hooks for complex post-task workflows
Hooks turn Claude Code from a tool you supervise into a tool that supervises itself. The less manual checking you need to do, the more you can trust the output.
For more Claude Code automation patterns and advanced hook configurations, check out the premium guides on ClawDocx.