# Claude Code headless template

Two files you copy into a repository of your own, plus one JSON sample.

- `claude-headless.yml` is a GitHub Actions workflow. Copy it to `.github/workflows/claude-headless.yml`. It installs `@anthropic-ai/claude-code` with `npm install -g --prefix` and runs the script. It does not deploy anything. `workflow_dispatch` is the only trigger, so a pull request does not start it.
- `claude-headless.sh` is the script. Copy it to `scripts/claude-headless.sh`. It runs `claude -p` with `--output-format json`, `--permission-mode dontAsk`, and `--allowedTools Read`, then prints the `result` field with jq. Pass `--bare` only when `ANTHROPIC_API_KEY` is set, because bare mode does not read `CLAUDE_CODE_OAUTH_TOKEN`.
- `documented-result.json` is not a model response. The field names are the ones the headless docs and the cost-tracking page they link to name. Every number is a zero placeholder so jq has a type to read. The `result` string says so.

Set one secret: `ANTHROPIC_API_KEY` for an API key, or `CLAUDE_CODE_OAUTH_TOKEN` for a subscription token from `claude setup-token`. If both are set, the script takes the API key path. Do not commit either value.

Checked on November 1, 2026: `actionlint` on the workflow, `shellcheck` and `bash -n` on the script, and the script's `--from-json` path against `documented-result.json`. Claude was not called. Nothing was run on GitHub. The install command is the one that produced a `claude` binary in a prefix on this machine. A GitHub-hosted runner was not used.

The write-up is [Claude Code headless mode](https://shipmunks.dev/blog/claude-code-headless-mode/).
