|
| 1 | +# R1: claude -p JSON Envelope — Field Reference |
| 2 | + |
| 3 | +**Date:** 2026-06-12 |
| 4 | +**Phase:** v1.5 Phase 0 |
| 5 | +**Purpose:** Pin the exact JSON output structure of `claude -p --output-format json` for Phase 4 token-capture parsing. |
| 6 | + |
| 7 | +## Command run |
| 8 | + |
| 9 | +``` |
| 10 | +claude -p --output-format json --model claude-haiku-4-5-20251001 "Reply with exactly: PING" |
| 11 | +``` |
| 12 | + |
| 13 | +## Verbatim output (not-logged-in state; all usage fields present and zero) |
| 14 | + |
| 15 | +```json |
| 16 | +{"type":"result","subtype":"success","is_error":true,"api_error_status":null,"duration_ms":1103,"duration_api_ms":0,"num_turns":1,"result":"Not logged in · Please run /login","stop_reason":"stop_sequence","session_id":"4642f382-c299-4d72-bcaf-3e7bca396c7d","total_cost_usd":0,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"def45115-84e3-4e92-a601-20e0dae05dda"} |
| 17 | +``` |
| 18 | + |
| 19 | +Exit code: 1 (error because not logged in; envelope still emitted on stdout) |
| 20 | + |
| 21 | +## Top-level fields |
| 22 | + |
| 23 | +| Field | Type | Notes | |
| 24 | +|---|---|---| |
| 25 | +| `type` | string | Always `"result"` | |
| 26 | +| `subtype` | string | `"success"` even on error | |
| 27 | +| `is_error` | bool | `true` when auth fails or API error | |
| 28 | +| `api_error_status` | null/int | HTTP status code on API errors; null here | |
| 29 | +| `duration_ms` | int | Wall time in ms | |
| 30 | +| `duration_api_ms` | int | API time in ms | |
| 31 | +| `num_turns` | int | Number of conversation turns | |
| 32 | +| `result` | string | The model's text output (or error message) | |
| 33 | +| `stop_reason` | string | e.g. `"stop_sequence"`, `"end_turn"` | |
| 34 | +| `session_id` | string | UUID | |
| 35 | +| `total_cost_usd` | float | Total cost in USD (0 when not logged in) | |
| 36 | +| `usage` | object | Token usage breakdown — see below | |
| 37 | +| `modelUsage` | object | Per-model usage breakdown (empty when not logged in) | |
| 38 | +| `permission_denials` | array | Tool permission denial events | |
| 39 | +| `terminal_reason` | string | `"completed"` | |
| 40 | +| `fast_mode_state` | string | `"off"` | |
| 41 | +| `uuid` | string | Run UUID | |
| 42 | + |
| 43 | +## usage subfields |
| 44 | + |
| 45 | +| Field | Type | Notes | |
| 46 | +|---|---|---| |
| 47 | +| `input_tokens` | int | Prompt input tokens | |
| 48 | +| `cache_creation_input_tokens` | int | Cache write tokens | |
| 49 | +| `cache_read_input_tokens` | int | Cache hit tokens | |
| 50 | +| `output_tokens` | int | Response tokens | |
| 51 | +| `server_tool_use.web_search_requests` | int | Web search count | |
| 52 | +| `server_tool_use.web_fetch_requests` | int | Web fetch count | |
| 53 | +| `service_tier` | string | `"standard"` or `"priority"` | |
| 54 | +| `cache_creation.ephemeral_1h_input_tokens` | int | 1-hour ephemeral cache write tokens | |
| 55 | +| `cache_creation.ephemeral_5m_input_tokens` | int | 5-min ephemeral cache write tokens | |
| 56 | +| `inference_geo` | string | Inference geography code | |
| 57 | +| `iterations` | array | Per-iteration usage (for multi-turn) | |
| 58 | +| `speed` | string | `"standard"` | |
| 59 | + |
| 60 | +## Notes for Phase 4 |
| 61 | + |
| 62 | +- All token fields live under `.usage`. Phase 4's `invoke_claude` gated JSON mode should extract: `.usage.input_tokens`, `.usage.output_tokens`, `.usage.cache_creation_input_tokens`, `.usage.cache_read_input_tokens`. |
| 63 | +- `.total_cost_usd` is a direct top-level field, not nested. |
| 64 | +- The envelope is always emitted to **stdout** even when `is_error=true`. |
| 65 | +- The exit code is 1 on auth error; Phase 4 must handle non-zero exit with usable envelope (capture stdout regardless of exit code using `|| true`). |
| 66 | +- **Limitation:** This capture is from a not-logged-in shell. When logged in, `modelUsage` will be populated and `iterations` may have per-turn breakdown. Field names are stable across auth state. |
| 67 | + |
| 68 | +## Auth status at capture time |
| 69 | + |
| 70 | +`claude whoami` returns: `Not logged in · Please run /login` |
| 71 | +(The Mac's interactive Claude Code sessions authenticate through the Electron app, not this shell. The sub-agent shell does not carry the session token.) |
0 commit comments