All postsSystems & Scale

Claude Code Hooks for Client Work: The Pre-Delivery Checklist That Runs Itself

David IyaDavid Iya August 25, 2026 9 min read
A matte black engineering relay panel with several toggle switches and one glowing amber indicator light, close-up macro photography
Original image, Claude Code Profit Room
TL;DR
  • Hooks are commands wired into Claude Code's settings that fire automatically at defined points in a session, so they enforce a rule every time instead of only when you remember to check.
  • For client work, run two: a PreToolUse hook that blocks dangerous commands before they execute, and a Stop hook that runs lint, format, and tests before a session is allowed to call itself finished.
  • Hooks are configuration, not code review. They catch the mechanical mistakes reliably so your own attention goes to the judgment calls a script cannot make.

What a Claude Code Hook Is and Why Paid Work Needs One

A hook is a shell command registered against a specific event in a Claude Code session: before a tool runs, after it runs, or when the session ends. You configure it once, in a settings file, and it fires every time that event happens, without you asking for it and without Claude choosing whether to comply.

That last part is the whole point for client work. A written instruction in a [CLAUDE.md](/blog/claude-md-file-for-client-projects) is a strong request, and on most sessions it gets followed. A hook is not a request. It is a gate the session has to pass through, so the rule holds on the session where you were distracted and the one where a different subagent was doing the work, not just the ones where you were watching closely.

The desktop app makes this invisible in the best way. Open a client project and the hooks configured for it are already active; there is nothing to launch and nothing to remember to turn on. The terminal is where hooks get written the first time, not where they need to run.

The Two-Hook Delivery Gate

Run two hooks on every paid repo: a PreToolUse hook that blocks dangerous operations before they execute, and a Stop hook that runs your verification commands before a session is allowed to end. Together they are the Two-Hook Delivery Gate, and each one guards a different failure mode.

HookFires whenWhat it prevents
PreToolUse gateBefore Claude runs a command or writes a fileA destructive or out-of-scope action reaching a live client system
Stop gateWhen Claude is about to end the sessionA session that reports itself finished without lint, tests, or a build actually passing

The Two-Hook Delivery Gate

Neither hook needs to be complicated. The PreToolUse gate is usually a short pattern match against a denylist of commands and paths. The Stop gate is usually the same test command you would have run by hand, just triggered automatically instead of on your memory.

The PreToolUse Gate: Block Before It Runs

A PreToolUse hook intercepts a command before Claude Code executes it and can reject it outright. On client work, this is where the do-not-touch list from your CLAUDE.md stops being a written rule and becomes an enforced one.

  • Block destructive shell commands outright: recursive deletes, force pushes to a shared branch, and anything that drops or truncates a database table.
  • Block writes to files and paths you named as off-limits: production environment files, vendor directories, and anything owned by another team on the client's side.
  • Block commands that touch a live database directly. Migrations get proposed and reviewed by you, never run automatically inside a task.
  • Log every blocked attempt somewhere you will actually see it, so a repeated block tells you the instructions need to be clearer, not just that the hook is doing its job.
Test a new PreToolUse hook against a scratch repo before you turn it loose on a live client project. A hook that is too aggressive blocks legitimate work and trains you to work around your own safety net, which defeats the reason you wrote it.

The Stop Gate: Nothing Finishes Without Proof

A Stop hook fires when Claude is about to end a session and can run your real verification commands, the ones you already wrote down in the commands block of your CLAUDE.md, before the session is allowed to report itself done.

  1. Run the linter and the formatter, and fail the stop if either reports an error rather than just a warning.
  2. Run the test suite, using the exact command that is the real gate on that project, not whichever script happens to be fastest.
  3. Run a build if the project has one, matching what the deploy platform actually runs, so a passing stop means something.
  4. Only after all three pass does the session get to end cleanly. A failing check should hold the session open, not just print a warning that scrolls past.

This closes a specific gap. Without it, a session can describe work as finished when the tests were never actually run in that session, because nothing forced them to run. The Stop gate turns verification into a checkpoint the work must clear rather than a step that depends on you asking for it.

What Not to Put in a Hook

A hook is a shell command that runs unattended, which means it inherits every risk of any unattended shell command. Keep three things out of it.

  • No credentials or secrets written directly into a hook script. Reference an environment variable and keep the value where it already lives, the same rule as a CLAUDE.md.
  • No hook that silently modifies client data. A hook that reformats code is safe to run unattended. A hook that touches records is not, no matter how convenient the automation would be.
  • No hook complex enough that you cannot explain what it does in one sentence. A hook you cannot describe simply is a hook you will eventually misdiagnose during an actual incident, at the worst possible time.

Where the Hooks Live: Project Versus Personal

Configure the Two-Hook Delivery Gate at the project level, committed with the repo, the same place your CLAUDE.md lives. A hook that only exists in your personal configuration protects you on your machine and nobody else, which matters the moment a client hires a second builder or you bring in help on a bigger engagement.

Personal-level hooks still have a place: your own habits, notification preferences, and conveniences that have nothing to do with a specific client's rules. Keep that layer separate from the project layer the same way you already keep personal preferences out of a client's CLAUDE.md, so the two configurations do not quietly fight each other.

Turning the Gate Into a Billable Line

The Two-Hook Delivery Gate is worth naming to the client, not just running silently. Automated pre-delivery QA is a real deliverable: fewer post-launch bugs, a consistent lint and test pass on every handoff, and a documented safety layer around production data. That belongs in a proposal or a [case study](/blog/write-a-case-study-that-wins-clients), not buried as an implementation detail nobody asked about.

It also compounds with everything else in your delivery system. Hooks catch the mechanical failures automatically; [subagents](/blog/claude-code-subagents-for-client-work) split the work across roles; the CLAUDE.md sets the standing rules both of them operate inside. None of the three replaces the others, and together they are most of what a repeatable [delivery pipeline](/blog/systematize-your-delivery-pipeline) actually is.

Inside the Claude Code Profit Room we share the actual hook configs builders run on paid work, including the denylist patterns that caught something real before it reached a client's production system. Join us at https://www.skool.com/claudecodeprofitroom/about and bring the client repo you have not gotten around to gating yet.

Common Hook Mistakes on Client Projects

Most bad hook setups fail in one of four ways, and all four are easy to catch before they cost you anything.

  1. A Stop hook so slow it gets bypassed in practice. If verification takes minutes on every single session, you will eventually skip it under deadline pressure, which defeats the point. Keep it fast or scope it to what changed.
  2. A PreToolUse gate copied from a different project without checking it still matches the current do-not-touch list. Hooks drift out of date the same way documentation does, silently.
  3. No log of what the gate has blocked. A hook that fires without a trace teaches you nothing about whether the instructions upstream of it need to change.
  4. Treating the hooks as a substitute for actually reading the diff. A hook catches the mechanical failures. It has no opinion on whether the feature is the right one.
Free builder-to-paid drops, straight to your inbox

Short, practical drops on offers, outreach, pricing, and closing clients with Claude Code. No spam, unsubscribe anytime.

Frequently asked

What are Claude Code hooks?

Shell commands registered against specific events in a Claude Code session, such as before a tool runs, after a tool runs, or when a session ends. They fire automatically every time that event happens, which makes them an enforcement layer rather than a written instruction Claude has to choose to follow.

Do hooks work in the Claude Code desktop app?

Yes. Hooks are configured once in a settings file and then run automatically whenever their event fires, regardless of whether you are working through the desktop app or the terminal. The desktop app does not require a separate setup step; opening a project with hooks already configured activates them.

What is the difference between a CLAUDE.md and a hook?

A CLAUDE.md is a written instruction Claude reads and generally follows. A hook is an enforced gate that runs regardless of what Claude decides, because it executes outside the model's control at a defined point in the session. Use the CLAUDE.md for context and conventions, and hooks for anything that must never be skipped, like blocking a destructive command or requiring tests to pass before a session ends.

Are Claude Code hooks safe to run on a live client project?

Yes, if you test them against a scratch repo first and keep them narrow: a PreToolUse denylist for destructive commands and off-limits paths, and a Stop hook that runs lint, tests, and a build. Avoid hooks that write to client data or that you cannot explain in one sentence, and never put credentials directly in a hook script.

Should I charge clients for setting up hooks?

Frame it as part of your delivery process rather than a separate line item on a small project, and name it explicitly as automated pre-delivery QA on a proposal or case study for a larger engagement. The value is real: fewer post-launch bugs and a documented safety layer around production systems, and clients pay more readily for something they can see named and explained than for an implementation detail they never hear about.

Last reviewed August 25, 2026.

David Iya
Co-founder, builder-operator

Co-founder of the Claude Code Profit Room. Went from shipping software to closing paying clients, and now teaches builders the selling half of the equation.

More from David Iya →

Ready to sell what you build?

Take the Profit Quiz and find your fastest path to your next client.