---
title: "Input/Output Schemas"
description: "Complete JSON schemas for all hook types."
type: skill
canonical_url: https://claudary.paisolsolutions.com/skills/input-output-schemas
source: "Claudary"
difficulty: intermediate
author: "Claude Code Knowledge Pack"
date: 2026-07-10T11:30:12.604Z
license: CC-BY-4.0
attribution: "Input/Output Schemas — Claudary (https://claudary.paisolsolutions.com/skills/input-output-schemas)"
---

# Input/Output Schemas
Complete JSON schemas for all hook types.

## Overview

# Input/Output Schemas

Complete JSON schemas for all hook types.

## Common Input Fields

All hooks receive these fields:

```typescript
{
  session_id: string           // Unique session identifier
  transcript_path: string      // Path to session transcript (.jsonl file)
  cwd: string                  // Current working directory
  permission_mode: string      // "default" | "plan" | "acceptEdits" | "bypassPermissions"
  hook_event_name: string      // Name of the hook event
}
```

---

## PreToolUse

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm install",
    "description": "Install dependencies"
  }
}
```

**Output** (optional, for control):
```json
{
  "decision": "approve" | "block",
  "reason": "Explanation for the decision",
  "permissionDecision": "allow" | "deny" | "ask",
  "permissionDecisionReason": "Why this permission decision",
  "updatedInput": {
    "command": "npm install --save-exact"
  },
  "systemMessage": "Message displayed to user",
  "suppressOutput": false,
  "continue": true
}
```

**Fields**:
- `decision`: Whether to allow the tool call
- `reason`: Explanation (required if blocking)
- `permissionDecision`: Override permission system
- `updatedInput`: Modified tool input (partial update)
- `systemMessage`: Message shown to user
- `suppressOutput`: Hide hook output from user
- `continue`: If false, stop execution

---

## PostToolUse

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.js",
    "content": "const x = 1;"
  },
  "tool_output": "File created successfully at: /path/to/file.js"
}
```

**Output** (optional):
```json
{
  "systemMessage": "Code formatted successfully",
  "suppressOutput": false
}
```

**Fields**:
- `systemMessage`: Additional message to display
- `suppressOutput`: Hide tool output from user

---

## UserPromptSubmit

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate factorial"
}
```

**Output**:
```json
{
  "decision": "approve" | "block",
  "reason": "Prompt is clear and actionable",
  "systemMessage": "Optional message to user"
}
```

**Fields**:
- `decision`: Whether to allow the prompt
- `reason`: Explanation (required if blocking)
- `systemMessage`: Message shown to user

---

## Stop

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}
```

**Output**:
```json
{
  "decision": "block" | undefined,
  "reason": "Tests are still failing - please fix before stopping",
  "continue": true,
  "stopReason": "Cannot stop yet",
  "systemMessage": "Additional context"
}
```

**Fields**:
- `decision`: `"block"` to prevent stopping, `undefined` to allow
- `reason`: Why Claude should continue (required if blocking)
- `continue`: If true and blocking, Claude continues working
- `stopReason`: Message shown when stopping is blocked
- `systemMessage`: Additional context for Claude
- `stop_hook_active`: If true, don't block again (prevents infinite loops)

**Important**: Always check `stop_hook_active` to avoid infinite loops:

```javascript
if (input.stop_hook_active) {
  return { decision: undefined }; // Don't block again
}
```

---

## SubagentStop

**Input**: Same as Stop

**Output**: Same as Stop

**Usage**: Same as Stop, but for subagent completion

---

## SessionStart

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "SessionStart",
  "source": "startup" | "continue" | "checkpoint"
}
```

**Output**:
```json
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Current sprint: Sprint 23\\nFocus: User authentication\\nDeadline: Friday"
  }
}
```

**Fields**:
- `additionalContext`: Text injected into session context
- Multiple SessionStart hooks' contexts are concatenated

---

## SessionEnd

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "SessionEnd",
  "reason": "exit" | "error" | "timeout" | "compact"
}
```

**Output**: None (ignored)

**Usage**: Cleanup tasks only. Cannot prevent session end.

---

## PreCompact

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "PreCompact",
  "trigger": "manual" | "auto",
  "custom_instructions": "Preserve all git commit messages"
}
```

**Output**:
```json
{
  "decision": "approve" | "block",
  "reason": "Safe to compact" | "Wait until task completes"
}
```

**Fields**:
- `trigger`: How compaction was initiated
- `custom_instructions`: User's compaction preferences (if manual)
- `decision`: Whether to proceed with compaction
- `reason`: Explanation

---

## Notification

**Input**:
```json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/Users/username/project",
  "permission_mode": "default",
  "hook_event_name": "Notification"
}
```

**Output**: None (hook just performs notification action)

**Usage**: Trigger external notifications (desktop, sound, status bar)

---

## Common Output Fields

These fields can be returned by any hook:

```json
{
  "continue": true | false,
  "stopReason": "Reason shown when stopping",
  "suppressOutput": true | false,
  "systemMessage": "Additional context or message"
}
```

**Fields**:
- `continue`: If false, stop Claude's execution immediately
- `stopReason`: Message displayed when execution stops
- `suppressOutput`: If true, hide hook's stdout/stderr from user
- `systemMessage`: Context added to Claude's next message

---

## LLM Prompt Hook Response

When using `type: "prompt"`, the LLM must return JSON:

```json
{
  "decision": "approve" | "block",
  "reason": "Detailed explanation",
  "systemMessage": "Optional message",
  "continue": true | false,
  "stopReason": "Optional stop message"
}
```

**Example prompt**:
```
Evaluate this command: $ARGUMENTS

Check if it's safe to execute.

Return JSON:
{
  "decision": "approve" or "block",
  "reason": "your explanation"
}
```

The `$ARGUMENTS` placeholder is replaced with the hook's input JSON.

---

## Tool-Specific Input Fields

Different tools provide different `tool_input` fields:

### Bash
```json
{
  "tool_input": {
    "command": "npm install",
    "description": "Install dependencies",
    "timeout": 120000,
    "run_in_background": false
  }
}
```

### Write
```json
{
  "tool_input": {
    "file_path": "/path/to/file.js",
    "content": "const x = 1;"
  }
}
```

### Edit
```json
{
  "tool_input": {
    "file_path": "/path/to/file.js",
    "old_string": "const x = 1;",
    "new_string": "const x = 2;",
    "replace_all": false
  }
}
```

### Read
```json
{
  "tool_input": {
    "file_path": "/path/to/file.js",
    "offset": 0,
    "limit": 100
  }
}
```

### Grep
```json
{
  "tool_input": {
    "pattern": "function.*",
    "path": "/path/to/search",
    "output_mode": "content"
  }
}
```

### MCP tools
```json
{
  "tool_input": {
    // MCP tool-specific parameters
  }
}
```

Access these in hooks:
```bash
command=$(echo "$input" | jq -r '.tool_input.command')
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
```

---

## Modifying Tool Input

PreToolUse hooks can modify `tool_input` before execution:

**Original input**:
```json
{
  "tool_input": {
    "command": "npm install lodash"
  }
}
```

**Hook output**:
```json
{
  "decision": "approve",
  "reason": "Adding --save-exact flag",
  "updatedInput": {
    "command": "npm install --save-exact lodash"
  }
}
```

**Result**: Tool executes with modified input.

**Partial updates**: Only specify fields you want to change:
```json
{
  "updatedInput": {
    "timeout": 300000  // Only update timeout, keep other fields
  }
}
```

---

## Error Handling

**Command hooks**: Return non-zero exit code to indicate error
```bash
if [ error ]; then
  echo '{"decision": "block", "reason": "Error occurred"}' >&2
  exit 1
fi
```

**Prompt hooks**: LLM should return valid JSON. If malformed, hook fails gracefully.

**Timeout**: Set `timeout` (ms) to prevent hanging:
```json
{
  "type": "command",
  "command": "/path/to/slow-script.sh",
  "timeout": 30000
}
```

Default: 60000ms (60s)

---

Source: [Claudary](https://claudary.paisolsolutions.com/skills/input-output-schemas) · https://claudary.paisolsolutions.com
