Documentation Index
Fetch the complete documentation index at: https://docs.runloop.ai/llms.txt
Use this file to discover all available pages before exploring further.
This document specifies the client-side API for interacting with Claude Code via the JSON Lines protocol.
Official documentation: https://platform.claude.com/docs/en/agent-sdk/user-input
Overview
The SDK provides a bidirectional communication channel with Claude Code:
- Input: Messages sent from the SDK to Claude
- Output: Messages received from Claude
All messages are JSON Lines (newline-delimited JSON) over stdin/stdout.
Client Methods
| Method | Description | Request Message |
|---|
query | Send user message, receive response stream | UserMessage |
control_request | Send control request (initialize, interrupt) | ControlRequest |
control_response | Respond to tool permission requests | ControlResponse |
Query
Send a user message and receive a stream of responses until a Result message indicates completion.
UserMessage:
{
"type": "user",
"message": {
"role": "user",
"content": [
{ "type": "text", "text": "What is 2 + 2?" }
]
},
"session_id": "550e8400-e29b-41d4-a716-446655440000"
}
Fields:
| Field | Type | Required | Description |
|---|
type | "user" | yes | Message type discriminator |
message.role | "user" | yes | Role identifier |
message.content | ContentBlock[] | yes | Array of content blocks |
session_id | string (UUID) | no | Session identifier |
ContentBlock variants:
// Text
{ "type": "text", "text": "Hello world" }
// Image (base64)
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "<base64-encoded-data>"
}
}
Control Request
Send a control request to Claude.
Payload (Initialize - enable tool approval protocol):
{
"type": "control_request",
"request_id": "init-uuid",
"request": {
"subtype": "initialize",
"protocolVersion": "1.0",
"features": []
}
}
Payload (Interrupt - stop current response):
{
"type": "control_request",
"request_id": "interrupt-uuid",
"request": {
"subtype": "interrupt"
}
}
Control Response
Respond to a ControlRequest received from Claude. All responses use the subtype: "success" envelope with a nested response object.
Payload (Allow):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid-from-control-request",
"response": {
"behavior": "allow",
"updatedInput": { "command": "ls -la" }
}
}
}
Payload (Allow with permissions):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid-from-control-request",
"response": {
"behavior": "allow",
"updatedInput": { "command": "ls -la" },
"updatedPermissions": [
{
"type": "addRules",
"rules": [{ "toolName": "Bash" }],
"behavior": "allow",
"destination": "session"
}
]
}
}
}
Payload (Deny):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid-from-control-request",
"response": {
"behavior": "deny",
"message": "Reason for denial"
}
}
}
Payload (Deny with interrupt):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid-from-control-request",
"response": {
"behavior": "deny",
"message": "Stopping execution",
"interrupt": true
}
}
}
Response Fields:
| Field | Type | Description |
|---|
behavior | "allow" | "deny" | Whether to allow or deny the tool use |
updatedInput | object | Tool input to use (required for allow) |
updatedPermissions | array | Permission rules to add (optional, allow only) |
message | string | Denial reason (required for deny) |
interrupt | boolean | Stop agent execution (optional, deny only) |
Request Types (Claude → SDK)
When tool approval is enabled, Claude sends ControlRequest messages to the SDK.
Claude requests permission to use a tool.
{
"type": "control_request",
"request_id": "uuid",
"request": {
"subtype": "can_use_tool",
"tool_name": "Bash",
"tool_use_id": "toolu_xxx",
"input": {
"command": "ls -la"
}
}
}
Fields:
| Field | Type | Description |
|---|
request_id | string | Unique ID to reference in response |
request.subtype | "can_use_tool" | Request type discriminator |
request.tool_name | string | Name of tool (Bash, Read, Write, Edit, etc.) |
request.tool_use_id | string | ID of the tool_use content block |
request.input | object | Tool-specific input parameters |
ExitPlanMode (Plan Review)
When Claude runs in plan mode, it sends this request asking the user to approve, revise, or reject the plan before executing.
Request:
{
"type": "control_request",
"request_id": "uuid",
"request": {
"subtype": "can_use_tool",
"tool_name": "ExitPlanMode",
"tool_use_id": "toolu_xxx",
"input": {
"plan": "## Plan\n\n1. First step\n2. Second step\n..."
}
}
}
Response (Execute Plan):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid",
"response": {
"behavior": "allow",
"updatedInput": { "plan": "..." }
}
}
}
Response (Revise):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid",
"response": {
"behavior": "deny",
"message": "Please revise the plan to include error handling"
}
}
}
Response (Reject):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid",
"response": {
"behavior": "deny",
"message": "User rejected the plan"
}
}
}
AskUserQuestion (Structured Questions)
Claude can ask structured multiple-choice questions for user clarification.
Request:
{
"type": "control_request",
"request_id": "uuid",
"request": {
"subtype": "can_use_tool",
"tool_name": "AskUserQuestion",
"tool_use_id": "toolu_xxx",
"input": {
"questions": [
{
"header": "Clarify",
"question": "What do you mean by 'the box'?",
"multiSelect": false,
"options": [
{ "label": "A file/directory", "description": "A file or folder named 'box'" },
{ "label": "A Docker container" }
]
}
]
}
}
}
Response (Answer):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid",
"response": {
"behavior": "allow",
"updatedInput": {
"questions": [
{
"header": "Clarify",
"question": "What do you mean by 'the box'?",
"multiSelect": false,
"options": [
{ "label": "A file/directory", "description": "A file or folder named 'box'" },
{ "label": "A Docker container" }
]
}
],
"answers": {
"What do you mean by 'the box'?": "A Docker container"
}
}
}
}
}
Response (Skip):
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "uuid",
"response": {
"behavior": "deny",
"message": "User skipped the question"
}
}
}
HookCallback
Notification about a hook event.
{
"type": "control_request",
"request_id": "uuid",
"request": {
"subtype": "hook_callback",
"hook_name": "pre-commit",
"hook_data": { }
}
}
McpMessage
MCP (Model Context Protocol) server communication.
{
"type": "control_request",
"request_id": "uuid",
"request": {
"subtype": "mcp_message",
"server_name": "server-name",
"message": { }
}
}
SDKControlInterrupt
Claude acknowledges an interrupt request.
{
"type": "control_request",
"request_id": "uuid",
"request": {
"subtype": "sdk_control_interrupt"
}
}
Output Types (Claude → SDK)
These are the message types received from Claude via receive().
System
Initialization, status, and task messages.
Init (session start):
{
"type": "system",
"subtype": "init",
"session_id": "uuid",
"cwd": "/path/to/project",
"model": "claude-sonnet-4",
"tools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep"],
"mcp_servers": [],
"slash_commands": ["compact", "cost", "review"],
"agents": ["Bash", "Explore", "Plan"],
"plugins": [{ "name": "plugin-name", "path": "/path/to/plugin" }],
"skills": [],
"claude_code_version": "2.1.52",
"apiKeySource": "none",
"output_style": "default",
"permissionMode": "default"
}
Status (e.g., compacting):
{
"type": "system",
"subtype": "status",
"session_id": "uuid",
"status": "compacting",
"uuid": "message-uuid"
}
Compact Boundary:
{
"type": "system",
"subtype": "compact_boundary",
"session_id": "uuid",
"compact_metadata": {
"pre_tokens": 155285,
"trigger": "auto"
},
"uuid": "message-uuid"
}
Task Started:
{
"type": "system",
"subtype": "task_started",
"session_id": "uuid",
"task_id": "task-id",
"task_type": "local_agent",
"tool_use_id": "toolu_xxx",
"description": "Task description",
"uuid": "message-uuid"
}
Task Progress:
{
"type": "system",
"subtype": "task_progress",
"session_id": "uuid",
"task_id": "task-id",
"tool_use_id": "toolu_xxx",
"description": "Current activity",
"last_tool_name": "Read",
"usage": {
"duration_ms": 13996,
"tool_uses": 9,
"total_tokens": 38779
},
"uuid": "message-uuid"
}
Task Notification:
{
"type": "system",
"subtype": "task_notification",
"session_id": "uuid",
"task_id": "task-id",
"status": "completed",
"summary": "Task completed successfully",
"output_file": "/path/to/output",
"tool_use_id": "toolu_xxx",
"usage": {
"duration_ms": 172300,
"tool_uses": 11,
"total_tokens": 42005
},
"uuid": "message-uuid"
}
User
Echo of the user message sent.
{
"type": "user",
"message": {
"role": "user",
"content": [{ "type": "text", "text": "..." }]
},
"session_id": "uuid"
}
Assistant
Claude’s response.
{
"type": "assistant",
"message": {
"id": "msg_xxx",
"role": "assistant",
"model": "claude-sonnet-4-20250514",
"content": [
{ "type": "text", "text": "Here's my response..." },
{
"type": "tool_use",
"id": "toolu_xxx",
"name": "Bash",
"input": { "command": "ls -la" }
},
{
"type": "thinking",
"thinking": "Let me analyze this..."
}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 1500,
"output_tokens": 200,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 1200,
"service_tier": "standard",
"cache_creation": {
"ephemeral_1h_input_tokens": 0,
"ephemeral_5m_input_tokens": 0
}
}
},
"session_id": "uuid",
"uuid": "message-uuid",
"parent_tool_use_id": null
}
Result
Query completion.
Success:
{
"type": "result",
"subtype": "success",
"is_error": false,
"duration_ms": 5000,
"duration_api_ms": 4500,
"num_turns": 3,
"session_id": "uuid",
"total_cost_usd": 0.05,
"uuid": "message-uuid",
"permission_denials": []
}
Error (max turns exceeded):
{
"type": "result",
"subtype": "error_max_turns",
"is_error": true,
"duration_ms": 60000,
"duration_api_ms": 55000,
"num_turns": 100,
"session_id": "uuid",
"total_cost_usd": 1.50,
"errors": ["Maximum turns exceeded"],
"permission_denials": []
}
Error (during execution):
{
"type": "result",
"subtype": "error_during_execution",
"is_error": true,
"duration_ms": 0,
"duration_api_ms": 0,
"num_turns": 0,
"session_id": "uuid",
"total_cost_usd": 0,
"errors": ["No conversation found with session ID: ..."],
"permission_denials": []
}
Result Fields:
| Field | Type | Required | Description |
|---|
subtype | string | yes | "success", "error_max_turns", or "error_during_execution" |
is_error | boolean | yes | Whether the result represents an error |
duration_ms | number | yes | Total duration in milliseconds |
duration_api_ms | number | yes | API call duration in milliseconds |
num_turns | number | yes | Number of agentic turns |
session_id | string | yes | Session identifier |
total_cost_usd | number | yes | Total cost in USD |
errors | string[] | no | Error messages (when is_error is true) |
permission_denials | array | no | Tools blocked due to permission denials |
uuid | string | no | Unique identifier for this message |
Error
Anthropic API error.
{
"type": "error",
"error": {
"type": "overloaded_error",
"message": "The API is temporarily overloaded"
}
}
RateLimitEvent
Rate limit status.
{
"type": "rate_limit_event",
"session_id": "uuid",
"uuid": "message-uuid",
"rate_limit_info": {
"status": "allowed",
"resetsAt": 1771390800,
"rateLimitType": "five_hour",
"utilization": 0.85,
"overageStatus": "rejected",
"overageDisabledReason": "org_level_disabled",
"isUsingOverage": false
}
}
RateLimitInfo Fields:
| Field | Type | Required | Description |
|---|
status | string | yes | "allowed", "allowed_warning", or "rejected" |
resetsAt | number | no | Unix timestamp when the rate limit resets |
rateLimitType | string | no | "five_hour", "hourly", or "seven_day" |
utilization | number | no | Utilization ratio (0.0 to 1.0) |
overageStatus | string | no | "allowed" or "rejected" |
overageDisabledReason | string | no | "org_level_disabled" or "out_of_credits" |
isUsingOverage | boolean | yes | Whether overage billing is active |
ControlRequest
See Request Types section above.
ControlResponse
Acknowledgment for SDK-initiated control requests (e.g., initialize handshake).
{
"type": "control_response",
"response": {
"subtype": "success",
"request_id": "init-uuid"
}
}
Typical Flow
SDK Claude
| |
|-- UserMessage ------------------->|
| |
|<-- System (init) -----------------|
|<-- Assistant (response) ----------|
|<-- ControlRequest (CanUseTool) ---|
| |
|-- ControlResponse (allow) ------->|
| |
|<-- Assistant (tool result) -------|
|<-- Result (completion) -----------|
Wire Protocol
- Transport: stdin/stdout of Claude CLI process
- Format: JSON Lines (one JSON object per line, newline-delimited)
- Encoding: UTF-8
- Buffer size: 10MB recommended for stdout reader
Version Compatibility
The protocol is unstable. Current tested version: 2.1.52