Skip to content

Hook Stdout Schema Reference

Status: Draft Created: 20260306, updated 20260310 Source: Claude Code hooks reference, retrieved 20260306. Exit code behavior verified empirically 20260308–20260310.

Complete reference for the JSON output a hook can return to Claude Code via stdout. Used by hookwatch to define TypeScript interfaces and Zod validation schemas for context injection (Epic 4).

Exit Code Behavior

Exit code is the primary dispatch mechanism — Claude Code reads it before deciding whether to parse stdout at all.

Exit CodeJSON Parsed?Behavior
0Yesstdout parsed for JSON output fields; normal execution continues
2No*JSON ignored; stderr only; may block depending on event (see per-event table)
Other non-zeroNonon-blocking; stderr shown in verbose mode only

* Upstream bug #30586: JSON is accidentally parsed at exit 2 in some Claude Code versions — a regression that will be fixed. Do not rely on this behavior.

Signal exit codes

When a hook process is killed by a signal, the exit code follows the Unix 128+N convention.

Exit CodeSignalDescription
129SIGHUPlikely terminal hangup
130SIGINTlikely interrupted (Ctrl-C)
131SIGQUITlikely quit
134SIGABRTlikely aborted
137SIGKILLlikely forced termination
139SIGSEGVlikely segmentation fault
141SIGPIPElikely broken pipe
143SIGTERMlikely terminated

Note: "likely" because the 128+N convention is widely followed but not mandated. macOS and Linux differ on some signal numbers. Claude Code empirically treats all signal exits as non-blocking (same behavior as other non-zero codes).

Per-event blocking table (exit 2)

Exit 2 blocks or adds feedback depending on the event type.

EventBlocks on exit 2?Effect
PreToolUseYesblocks the tool call
PermissionRequestYesdenies the permission
UserPromptSubmitYesblocks the prompt
StopYesprevents Claude from stopping
PostToolUseNostderr fed back to Claude as tool result error
PostToolUseFailureNostderr shown in verbose mode
SessionStartNostderr shown to user only
SessionEndNostderr shown in verbose mode
SubagentStartNostderr shown in verbose mode
SubagentStopNostderr shown in verbose mode
NotificationNostderr shown in verbose mode
PreCompactNostderr shown in verbose mode
TeammateIdleYesexit 2 sends stderr as feedback, keeps teammate working
TaskCompletedYesexit 2 prevents completion, sends stderr as feedback
ConfigChangeNostderr shown in verbose mode
WorktreeCreateNostderr shown in verbose mode
WorktreeRemoveNostderr shown in verbose mode
InstructionsLoadedNostderr shown in verbose mode

TeammateIdle and TaskCompleted blocking behavior added in Claude Code 2.1.33.

Standard Output (All Hooks)

These fields are parsed only at exit 0. Every hook can return:

FieldTypeDefaultDescription
continuebooleantruefalse halts processing entirely; true proceeds
suppressOutputbooleanfalsetrue hides hook output from transcript
systemMessagestringMessage sent to Claude for context or explanation

Decision Control

Alternatives to exit 2 for blocking behavior, available via JSON at exit 0.

Top-level decision field

For PostToolUse and Stop — return a decision field in JSON:

json
{
  "decision": "block",
  "reason": "Explanation of why the action is blocked"
}

hookSpecificOutput with permissionDecision

For PreToolUse — return inside hookSpecificOutput:

json
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Reason shown to user"
  }
}

continue: false

Stops Claude entirely — Claude will not continue processing after the hook runs. Available in JSON at exit 0. More forceful than decision: block.

json
{
  "continue": false,
  "systemMessage": "Explanation of why processing stopped"
}

Event-Specific Output

PreToolUse

json
{
  "hookSpecificOutput": {
    "permissionDecision": "allow|deny|ask",
    "updatedInput": {"field": "modified_value"},
    "additionalContext": "Text injected into context for the model"
  },
  "systemMessage": "Explanation for Claude"
}
FieldSinceRequiredDescription
hookSpecificOutput.permissionDecision1.0.59yesallow
hookSpecificOutput.permissionDecisionReason1.0.59noReason shown alongside the decision
hookSpecificOutput.updatedInput2.0.30noModified tool input fields — merged into original input before execution (works with ask decision since 2.1.0)
hookSpecificOutput.additionalContext2.1.9noText injected into context for the model
systemMessagenoExplanation or feedback for Claude

Note: top-level decision and reason fields are deprecated. Use hookSpecificOutput.permissionDecision and hookSpecificOutput.permissionDecisionReason instead.

PostToolUse

No event-specific output fields. Uses standard output only (continue, suppressOutput, systemMessage).

Exit code behavior:

Exit CodeBehavior
0stdout parsed for JSON; normal continuation
2stderr fed back to Claude as a tool result error (non-blocking)
Other non-zerostderr shown in verbose mode only

UserPromptSubmit

json
{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "..."
  }
}

Since 1.0.59. The additionalContext field injects text alongside the user's prompt. No other event-specific fields are documented.

Exit 2 blocks the prompt submission entirely.

Stop

json
{
  "decision": "approve|block",
  "reason": "Explanation",
  "systemMessage": "Additional context"
}
FieldTypeRequiredDescription
decisionstringyesapprove (allow stop)
reasonstringnoExplanation for the decision
systemMessagestringnoAdditional context for Claude

Exit 2 prevents Claude from stopping (same effect as decision: block).

Other Events

SessionStart, SessionEnd, SubagentStart, SubagentStop, Notification, PreCompact, TeammateIdle, TaskCompleted, ConfigChange, WorktreeCreate, WorktreeRemove, InstructionsLoaded — no event-specific output fields documented. Use standard output only.

Notes

  • JSON is only parsed at exit 0 (except for the upstream bug #30586)
  • Only PreToolUse and Stop have event-specific output schemas with decision fields
  • UserPromptSubmit has hookSpecificOutput.additionalContext for context injection
  • All other events use the 3 standard fields only
  • The SDK source may add more event-specific outputs in future versions
  • This document should be re-verified against the official docs periodically

Released under the MIT License.