← Back to Blog

Claude Code Headless Mode: Complete Guide (2026)

How Claude Code headless mode (-p flag) works: JSON output, permission scoping, exit codes, and CI/CD automation patterns that don't hang in production.


Every Claude Code tutorial shows you the interactive REPL — type a prompt, watch it think, approve the diff. That workflow is great for building. It's useless for automation.

If you want Claude Code to run inside a GitHub Action, a pre-commit hook, a cron job, or any pipeline where no human is watching the terminal, you need headless mode: the -p flag (short for --print) that turns Claude Code from an interactive agent into a scriptable CLI tool.

Here's how it actually works, the output formats you need for parsing, and the mistakes that make headless runs hang forever in CI.

What Headless Mode Actually Is

Normal Claude Code is a REPL. It opens a persistent session, waits for input, and expects you to review and approve changes interactively. Headless mode strips that out:

claude -p "Summarize the changes in the last commit"

This runs once, prints the result to stdout, and exits. No REPL, no persistent terminal session, no interactive approval loop by default. That's the entire concept — everything else in this guide is about controlling what happens during that single, non-interactive run.

The -p flag is what makes Claude Code usable in contexts where nothing is watching: CI pipelines, cron jobs, git hooks, other programs that shell out to Claude and parse the response.

Basic Usage Patterns

The simplest form takes a prompt as an argument:

claude -p "List all TODO comments in this repo and group them by file"

You can also pipe input through stdin, which is the pattern you'll use most in scripts:

cat error.log | claude -p "Explain what's causing this error and suggest a fix"

git diff | claude -p "Write a commit message for this diff"

Piping is important because it lets Claude Code slot into existing shell pipelines instead of requiring you to restructure your workflow around it. Anything that produces text on stdout can feed Claude Code.

Output Formats: text, json, stream-json

By default, headless mode prints plain text — fine for reading, useless for parsing programmatically. For automation you want structured output:

# Plain text (default) — human-readable, hard to parse
claude -p "Review this file for bugs" --output-format text

# Single JSON object — easiest to parse in scripts
claude -p "Review this file for bugs" --output-format json

# Streaming JSON — one JSON object per line, useful for long-running tasks
claude -p "Refactor this module" --output-format stream-json

--output-format json is what you want for almost every automation use case. It returns a single JSON object containing the result, cost, duration, and session metadata — clean and predictable to parse with jq or any JSON library:

result=$(claude -p "Is this PR safe to merge?" --output-format json)
verdict=$(echo "$result" | jq -r '.result')
cost=$(echo "$result" | jq -r '.total_cost_usd')

stream-json matters when the task is long-running and you want to show progress instead of blocking silently until completion — think a CI log that streams tool calls as they happen instead of going dark for two minutes.

Permissions in Headless Mode

This is where most people get stuck. Interactive Claude Code stops and asks before running risky commands or editing files. Headless mode has no one to ask — so by default, permission-gated actions will hang the process waiting for an approval that will never come.

You have three ways to handle this, in order of how much control you keep:

# Allow specific tools without prompting
claude -p "Fix the failing tests" --allowedTools "Edit,Bash(npm test:*)"

# Set a permission mode explicitly
claude -p "Update the changelog" --permission-mode acceptEdits

# Skip all permission checks (use only in sandboxed/disposable environments)
claude -p "Run the full test suite and fix failures" --dangerously-skip-permissions

--allowedTools is the safest default for CI: you explicitly whitelist what the agent can touch (specific tools, specific Bash command patterns) and everything else stays blocked. Our permission modes guide covers the full mode hierarchy if you need finer control than the headless flags expose.

--dangerously-skip-permissions does exactly what it says. Only use it inside an isolated container or ephemeral CI runner you're comfortable losing — never on a machine with real credentials or production access.

Session Continuity: --resume and --continue

Headless runs are stateless by default — each invocation starts fresh. If you need multi-step automation that builds on prior context, use session flags:

# Continue the most recent session
claude -p "Now add tests for that function" --continue

# Resume a specific session by ID
claude -p "Apply the same fix to the other file" --resume abc123

This is the pattern for pipelines that run Claude Code multiple times against the same problem — say, one step that diagnoses an issue and a second step that fixes it, without re-explaining the context each time.

A Real CI/CD Example

Here's a GitHub Actions step that runs Claude Code headlessly to review a pull request diff and post a summary:

- name: AI code review
  run: |
    diff=$(git diff origin/main...HEAD)
    review=$(echo "$diff" | claude -p "Review this diff for bugs, security issues, and style violations. Be specific about line numbers."       --output-format json       --allowedTools "Read,Grep"       --max-turns 5)
    echo "$review" | jq -r '.result' >> review.md
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Three details matter here. First, --allowedTools "Read,Grep" keeps this read-only — a code reviewer should never have Edit or Bash access. Second, --max-turns 5 caps how many tool calls the agent can make before it's forced to respond, which bounds both cost and runtime. Third, the API key comes from a CI secret, not an interactive login — headless mode authenticates the same way any other CLI tool would in a pipeline.

Exit Codes and Error Handling

Headless mode returns standard exit codes, which is what makes it scriptable in the first place:

  • 0 — completed successfully
  • Non-zero — the run failed (auth error, hit max turns, tool error, network failure)

Always check the exit code before trusting the output in a pipeline:

if ! result=$(claude -p "$prompt" --output-format json); then
  echo "Claude Code run failed" >&2
  exit 1
fi

Don't assume stdout is valid JSON just because you requested --output-format json — a hard failure (auth expired, rate limit) can still print an error to stderr and return non-zero with empty or malformed stdout. Check the exit code first, parse second.

Headless Mode vs. the Claude Agent SDK

People conflate these two, but they solve different problems.

Headless Claude Code (-p) is a CLI wrapper around an existing, opinionated agent — it comes with the full tool set (Read, Edit, Bash, Grep, Glob, etc.), the permission system, and the same behavior as interactive mode, just non-interactive. You're scripting an existing product.

The Claude Agent SDK is a programming library — you write Python or TypeScript, define your own tools, control the loop yourself, and build a custom agent from primitives. You're building your own product.

Use headless mode when you want "Claude Code, but scripted" — CI review steps, git hooks, cron jobs that run a fixed task against your existing codebase. Reach for the Agent SDK when you're building a standalone application with custom tools, a different permission model, or an agent that needs to run outside a filesystem-and-shell context entirely.

Common Mistakes

Forgetting a timeout. A CI job with no timeout and a headless Claude Code call that hits an unexpected permission prompt will hang until your CI provider kills the job — usually at the maximum job timeout, burning minutes for nothing. Always wrap headless calls in an explicit timeout:

timeout 120 claude -p "$prompt" --output-format json

Not scoping --allowedTools. Running headless with full default permissions in an unattended pipeline means any tool call that would normally trigger a confirmation prompt in interactive mode either silently succeeds (with --dangerously-skip-permissions) or hangs (without it). Scope tools explicitly every time.

Treating stdout as guaranteed-clean JSON. Log lines, deprecation warnings, or partial output can leak into stdout alongside your JSON payload depending on shell configuration. Parse defensively — check the exit code, and don't assume jq will succeed on the first try in production without a fallback.

Using headless mode for tasks that need human judgment. Headless mode is for well-scoped, repeatable tasks — running tests and fixing failures, generating a commit message, checking a diff against a lint spec. It's the wrong tool for open-ended architectural decisions where you actually want the plan-review step. For those, use plan mode interactively instead.

Combining Headless Mode With Subagents and Hooks

Headless mode composes with the rest of the Claude Code feature set. A headless invocation can still delegate to subagents for context-isolated subtasks, and hooks still fire on the same lifecycle events (PreToolUse, PostToolUse, SessionStart) whether the session is interactive or headless. This means your existing guardrails — a hook that blocks rm -rf or force-pushes — still apply in CI. Don't rely on --allowedTools alone if you already have hook-based safety checks; layer both.

The Takeaway

Headless mode is what turns Claude Code from a tool you use to a tool your infrastructure uses. The pattern that works in production: structured JSON output for parsing, explicitly scoped --allowedTools instead of skipping permissions, an explicit timeout so failures don't hang your pipeline, and exit-code checks before you trust anything on stdout.

Start small — a single CI step that runs Claude Code against a diff and posts a comment. Once that's reliable, headless mode becomes the backbone for anything repeatable: automated code review, changelog generation, test-failure triage, dependency update checks. The interactive REPL is for building. Headless mode is for running.

---

Want AI automation built into your engineering pipeline? Our consulting team builds Claude Code and Agent SDK integrations for production CI/CD. Or start learning the fundamentals at Like One Academy.


Free: Claude custom instructions template pack

Eight copy-paste templates — developer, writer, analyst, CLAUDE.md starter, and more. Plus new guides in your inbox. No spam, unsubscribe anytime.

Or grab the templates directly — no email needed

Keep learning — for free

50+ AI courses. 590+ lessons. No paywall for starters.

Need help building this?

We build MCP servers, Claude workflows, and AI agents for teams. Strategy calls start at $150/hr.