Hook Explorer
A hook is a command Claude Code or Codex runs at a set point in a session. It gets a JSON payload on stdin and can answer with JSON on stdout. Pick an event to see both shapes, then break a payload and watch the check catch it.
PreToolUse
- When it fires
- Fires before a tool runs.
- What its matcher matches
- The tool name.
- Whether it can block
- Can block or allow:
permissionDecisiontakesallow,deny,ask, ordefer, andupdatedInputrewrites the tool’s input.
Codex has a PreToolUse event too.
- Only in Claude Code: stdin
scratchpad_dir,prompt_id,effort,mcp_server;stdoutstopReason,terminalSequence. - Only in Codex: stdin
model,turn_id.
What it receives on stdin
hook_event_namestring requiredAlways
PreToolUsetool_namestring requiredtool_inputany JSON requiredIts shape depends on the tool.
tool_use_idstring requiredmcp_serverobject optionalnamestring requiredsourcestring required
Fields every event gets (9)
session_idstring requiredtranscript_pathstring requiredcwdstring requiredscratchpad_dirstring optionalprompt_idstring optionalpermission_modestring optionalagent_idstring optionalagent_typestring optionaleffortobject optionallevelstring required
Sample stdin
{
"session_id": "<session_id>",
"transcript_path": "<transcript_path>",
"cwd": "<cwd>",
"hook_event_name": "PreToolUse",
"tool_name": "<tool_name>",
"tool_input": {},
"tool_use_id": "<tool_use_id>"
}Strings in angle brackets are placeholders, and tool_input is {} here. A real one depends on the tool.
What it can print on stdout
Fields every event can print (7)
continueboolean optionalsuppressOutputboolean optionalstopReasonstring optionaldecisionstring optionalOne of
approve,blockreasonstring optionalsystemMessagestring optionalterminalSequencestring optional
hookSpecificOutputobject optionalhookEventNamestring requiredAlways
PreToolUsepermissionDecisionstring optionalOne of
allow,deny,ask,deferpermissionDecisionReasonstring optionalupdatedInputobject optionaladditionalContextstring optional
hookSpecificOutput must include hookEventName, set to PreToolUse.
Sample stdout
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse"
}
}Check a payload
It starts as the sample. Delete a required field, change a value, or add a key, then
check it against PreToolUse in Claude Code.
Exit codes
Claude Code
0is success. stdout is parsed as JSON when it starts with{and ends with}. Plain text on stdout is added as context onUserPromptSubmit,UserPromptExpansion,SessionStart, andPostModelSwitch; otherwise it goes to the debug log.2is a blocking error on events that can block. It blocks whatever the JSON says, with the message from the JSONreasonif there is one and from stderr if not.- Any other code is a non-blocking error for most events, and JSON on stdout is still read. On
WorktreeCreateandWorktreeRemove, any non-zero exit is a failure.
Codex
0with valid JSON is success.2is a blocking decision, with the reason taken from stderr.- An unknown key anywhere in the JSON on stdout makes the hook run fail, so the hook fails closed.
- A block needs a non-empty
reason, and aPreToolUseallowneedsupdatedInput. The field lists here can’t show either rule.