Chapter 04 · Section I · 12 min read
4.1 Headless mode: --output-format, streams, exit codes
How to invoke Claude Code from a script — one-shot prompts with structured output, streaming events, and exit codes you can branch on.
Headless mode is Claude Code in one-shot form: no interactive REPL, no permission prompts, a single prompt goes in and a single result comes out. It is what turns Claude Code into a component you can call from shell scripts, cron jobs, and CI pipelines.
The basic shape
claude -p "summarise the changes in the current branch" \
--output-format json \
--permission-mode acceptEdits
-p— the prompt. Everything after is one message.--output-format— how the result is printed.--permission-mode— usuallyacceptEditsorbypassPermissionsin headless because there is no human to approve.
Output format: text
The default. Prints Claude’s reply as plain text. Fine for eyeballing.
claude -p "list the top-level directories" > /tmp/dirs.txt
Output format: json
Single JSON object per invocation:
{
"type": "result",
"subtype": "success",
"duration_ms": 1832,
"num_turns": 3,
"result": "The changes on this branch are…",
"total_cost_usd": 0.0142,
"session_id": "abc123..."
}
Parse with jq:
result=$(claude -p "summarise" --output-format json)
cost=$(echo "$result" | jq -r '.total_cost_usd')
reply=$(echo "$result" | jq -r '.result')
Perfect for scripts that want the reply and the metadata.
Output format: stream-json
Newline-delimited JSON — one event per line as they happen:
{"type":"assistant","message":{...}}
{"type":"user","message":{...}} // tool result
{"type":"result","subtype":"success","result":"...","total_cost_usd":0.02}
Useful when you want to react to events as they arrive — a progress meter, an incremental sink, a pipeline that consumes each tool call.
claude -p "audit" --output-format stream-json | while read -r line; do
echo "$line" | jq 'select(.type=="assistant")'
done
Exit codes
Non-zero exits mean something failed:
- 0 — success. The
result.subtypein json output is"success". - 1 — general error (bad flag, unreachable model, IO error).
- 2 — permission denied (a hook blocked, or
bypassPermissionsrefused a denied pattern). - 130 — user interrupt (SIGINT).
Branch on the exit code in scripts. Do not parse stderr for control flow.
Check your understanding
Quick check
—