Protocol Overview
Unofficial Documentation
This documents Claude Code's stream-json NDJSON protocol as observed in production. This is not an official Anthropic specification — the protocol is undocumented and can change between releases.
What is Stream-JSON?
Claude Code supports a machine-readable I/O mode where:
- stdout emits one JSON object per line (NDJSON) — the output protocol
- stdin accepts NDJSON messages — the input protocol (undocumented by Anthropic)
This is activated with --output-format stream-json and --input-format stream-json.
Invoking Claude Code
claude -p \
--input-format stream-json \
--output-format stream-json \
--verbose \
--permission-mode default \
--session-id <uuid>| Flag | Purpose |
|---|---|
-p | Non-interactive / pipe mode |
--input-format stream-json | Accept NDJSON on stdin |
--output-format stream-json | Emit NDJSON on stdout |
--verbose | Include full message snapshots (not just deltas) |
--permission-mode | default | plan | acceptEdits | bypassPermissions | dontAsk |
--session-id | Resume or tag a session |
Protocol Architecture
┌──────────────┐ stdin (NDJSON) ┌──────────────────┐
│ │ ───────────────────────────────>│ │
│ Your Code │ │ Claude Code │
│ │ <───────────────────────────────│ │
└──────────────┘ stdout (NDJSON) └──────────────────┘
stdin messages: stdout events:
user message system/init
approve tool assistant (cumulative)
deny tool user (tool results)
tool result result (turn complete)Event Lifecycle
A typical interaction follows this sequence:
1. Process starts → system/init (session_id, model)
2. Your code sends message ← {"type":"user","message":{...}}
3. Claude thinks → assistant (thinking block)
4. Claude responds → assistant (thinking + text blocks)
5. Claude calls a tool → assistant (thinking + text + tool_use blocks)
6. You approve the tool ← {"type":"approve","tool_use_id":"..."}
7. Tool executes → user (tool_result block)
8. Claude processes result → assistant (new text + possibly more tools)
9. Turn finishes → result (cost, tokens, duration)Steps 3-8 may repeat multiple times in a single turn (agentic loop). The result event marks the end of a turn.
Key Concepts
Cumulative Snapshots (--verbose)
With --verbose, every assistant event contains all content blocks accumulated so far — not just the new ones. See Deduplication for how to handle this.
Double-Encoded Results
The result field in result and system/result events is a JSON string inside JSON:
{"result": "\"The actual text is inside escaped quotes\""}You must JSON.parse() the result value to get the actual text.
Polymorphic Content
The content field in tool_result blocks can be a string, an array of text blocks, or null. See Gotchas for details.
Multi-Agent
Claude Code can spawn sub-agents, which produce their own interleaved events on the same stdout. See Multi-Agent for how the translator handles this.