3Hooks and the SDK

The other seven hook types

4 min read721 words

PreToolUse and PostToolUse are the two hook types you will reach for most, but they are not the only ones. Claude Code exposes seven additional hook types that fire at other points in the session lifecycle — session start, user prompt submission, stop, compaction, and more. This lesson is a reference for what they are and when to use them, plus a debugging trick you will want every time you work on a new hook type.

The seven additional hook types

| Hook type | When it runs | Typical use | | --- | --- | --- | | Notification | When Claude Code sends a notification — that is, when Claude needs permission to use a tool, or after Claude Code has been idle for 60 seconds | Route notifications to Slack, system tray, pager — anywhere you want to be alerted that Claude is waiting on you | | Stop | When Claude Code has finished responding | Run a final cleanup step, log the session, trigger a follow-up job | | SubagentStop | When a subagent (shown as a "Task" in the UI) has finished | React specifically to subagent completion — for example, aggregating subagent results | | PreCompact | Before a compact operation runs, whether manual or automatic | Save the pre-compact conversation to a log, or block compaction under certain conditions | | UserPromptSubmit | When the user submits a prompt, before Claude processes it | Inspect or rewrite prompts, enforce team conventions, add automatic context | | SessionStart | When starting or resuming a session | Log session start, warm caches, inject session-wide context | | SessionEnd | When a session ends | Flush logs, commit an audit trail, run cleanup |

Each one is configured the same way as PreToolUse and PostToolUse: under the appropriate key in settings.json, with a command that receives JSON on stdin.

The confusing part — stdin shape varies by hook type

Here is what catches people. The JSON payload your hook receives is not the same across hook types.

  • For PreToolUse and PostToolUse, the payload has tool_name and tool_input, and the shape of tool_input depends on which tool was called (Read, Write, TodoWrite, etc.).
  • For Stop, SessionStart, UserPromptSubmit, and friends, the payload has entirely different fields relevant to that event.

There is no shortcut — when you add a new hook type, you need to know what stdin will actually look like before you can parse it correctly. Which brings us to the jq debugging trick.

Debugging trick — log stdin with jq

The cleanest way to figure out the shape of any hook's stdin is to route it through jq and dump it to a file. Add a temporary hook with a wildcard matcher and a jq command:

"PostToolUse": [
  {
    "matcher": "*",
    "hooks": [
      {
        "type": "command",
        "command": "jq . > post-log.json"
      }
    ]
  }
]

What this does:

  • The * matcher fires on every tool use.
  • jq . reads the JSON from stdin and pretty-prints it.
  • Redirecting to post-log.json writes the formatted payload to a file you can open and inspect.

Now run Claude Code, do whatever would trigger the hook you are trying to build, and open post-log.json. You are looking at the exact shape your real hook will need to parse.

The same pattern works for any hook type — just put the jq command in the Stop, SessionStart, UserPromptSubmit, or other section. Once you have inspected a few real payloads, you know exactly which fields to reach for.

Remove the debugging hook when you are done. Do not commit it.

Key Takeaways

  • 1Claude Code supports seven hook types beyond PreToolUse and PostToolUse: Notification, Stop, SubagentStop, PreCompact, UserPromptSubmit, SessionStart, and SessionEnd.
  • 2Each hook type fires at a specific point in the session lifecycle and covers a distinct automation use case, from routing notifications to enforcing prompt conventions.
  • 3The JSON payload delivered on stdin differs by hook type — there is no universal shape, so you need to inspect the real payload before parsing it.
  • 4Use a wildcard matcher with `jq . > post-log.json` as a temporary logging hook to capture and pretty-print the exact stdin structure for any hook type you are working on.