Overview

The headless mode allows you to run Claude Code programmatically from command line scripts and automation tools without any interactive UI.

Basic usage

The primary command-line interface to Claude Code is the claude command. Use the --print (or -p) flag to run in non-interactive mode and print the final result:

claude -p "Stage my changes and write a set of commits for them" \
  --allowedTools "Bash,Read" \
  --permission-mode acceptEdits \
  --cwd /path/to/project

Configuration Options

The SDK leverages all the CLI options available in Claude Code. Here are the key ones for SDK usage:

FlagDescriptionExample
--print, -pRun in non-interactive modeclaude -p "query"
--output-formatSpecify output format (text, json, stream-json)claude -p --output-format json
--resume, -rResume a conversation by session IDclaude --resume abc123
--continue, -cContinue the most recent conversationclaude --continue
--verboseEnable verbose loggingclaude --verbose
--append-system-promptAppend to system prompt (only with --print)claude --append-system-prompt "Custom instruction"
--allowedToolsSpace-separated list of allowed tools, or

string of comma-separated list of allowed tools
claude --allowedTools mcp__slack mcp__filesystem

claude --allowedTools "Bash(npm install),mcp__filesystem"
--disallowedToolsSpace-separated list of denied tools, or

string of comma-separated list of denied tools
claude --disallowedTools mcp__splunk mcp__github

claude --disallowedTools "Bash(git commit),mcp__github"
--mcp-configLoad MCP servers from a JSON fileclaude --mcp-config servers.json
--permission-prompt-toolMCP tool for handling permission prompts (only with --print)claude --permission-prompt-tool mcp__auth__prompt

For a complete list of CLI options and features, see the CLI reference documentation.

Multi-turn conversations

For multi-turn conversations, you can resume conversations or continue from the most recent session:

# Continue the most recent conversation
claude --continue "Now refactor this for better performance"

# Resume a specific conversation by session ID
claude --resume 550e8400-e29b-41d4-a716-446655440000 "Update the tests"

# Resume in non-interactive mode
claude --resume 550e8400-e29b-41d4-a716-446655440000 "Fix all linting issues" --no-interactive

Output Formats

Text Output (Default)

claude -p "Explain file src/components/Header.tsx"
# Output: This is a React component showing...

JSON Output

Returns structured data including metadata:

claude -p "How does the data layer work?" --output-format json

Response format:

{
  "type": "result",
  "subtype": "success",
  "total_cost_usd": 0.003,
  "is_error": false,
  "duration_ms": 1234,
  "duration_api_ms": 800,
  "num_turns": 6,
  "result": "The response text here...",
  "session_id": "abc123"
}

Streaming JSON Output

Streams each message as it is received:

claude -p "Build an application" --output-format stream-json

Each conversation begins with an initial init system message, followed by a list of user and assistant messages, followed by a final result system message with stats. Each message is emitted as a separate JSON object.

Input Formats

Text Input (Default)

# Direct argument
claude -p "Explain this code"

# From stdin
echo "Explain this code" | claude -p

Streaming JSON Input

A stream of messages provided via stdin where each message represents a user turn. This allows multiple turns of a conversation without re-launching the claude binary and allows providing guidance to the model while it is processing a request.

Each message is a JSON ‘User message’ object, following the same format as the output message schema. Messages are formatted using the jsonl format where each line of input is a complete JSON object. Streaming JSON input requires -p and --output-format stream-json.

echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Explain this code"}]}}' | claude -p --output-format=stream-json --input-format=stream-json --verbose

Agent Integration Examples

SRE Incident Response Bot

#!/bin/bash

# Automated incident response agent
investigate_incident() {
    local incident_description="$1"
    local severity="${2:-medium}"

    claude -p "Incident: $incident_description (Severity: $severity)" \
      --append-system-prompt "You are an SRE expert. Diagnose the issue, assess impact, and provide immediate action items." \
      --output-format json \
      --allowedTools "Bash,Read,WebSearch,mcp__datadog" \
      --mcp-config monitoring-tools.json
}

# Usage
investigate_incident "Payment API returning 500 errors" "high"

Automated Security Review

# Security audit agent for pull requests
audit_pr() {
    local pr_number="$1"

    gh pr diff "$pr_number" | claude -p \
      --append-system-prompt "You are a security engineer. Review this PR for vulnerabilities, insecure patterns, and compliance issues." \
      --output-format json \
      --allowedTools "Read,Grep,WebSearch"
}

# Usage and save to file
audit_pr 123 > security-report.json
# Legal document review with session persistence
session_id=$(claude -p "Start legal review session" --output-format json | jq -r '.session_id')

# Review contract in multiple steps
claude -p --resume "$session_id" "Review contract.pdf for liability clauses"
claude -p --resume "$session_id" "Check compliance with GDPR requirements"
claude -p --resume "$session_id" "Generate executive summary of risks"

Best Practices

  • Use JSON output format for programmatic parsing of responses:

    # Parse JSON response with jq
    result=$(claude -p "Generate code" --output-format json)
    code=$(echo "$result" | jq -r '.result')
    cost=$(echo "$result" | jq -r '.cost_usd')
    
  • Handle errors gracefully - check exit codes and stderr:

    if ! claude -p "$prompt" 2>error.log; then
        echo "Error occurred:" >&2
        cat error.log >&2
        exit 1
    fi
    
  • Use session management for maintaining context in multi-turn conversations

  • Consider timeouts for long-running operations:

    timeout 300 claude -p "$complex_prompt" || echo "Timed out after 5 minutes"
    
  • Respect rate limits when making multiple requests by adding delays between calls