New to Claude Skills? Learn how to install them →

ant CLI Scripting and Automation

How to version-control Claude API resources as YAML with the ant CLI, chain commands with --transform, inspect errors, and call ant from inside Claude Code.

August 26, 2026
Get Claude Skills
9 min read

Anthropic's ant CLI covers messages, models, files and the Managed Agents beta as one subcommand per resource. What that first guide didn't get to is the scripting layer built on top: treating agents, environments and deployments as version-controlled YAML, chaining commands with shell tooling, and letting Claude Code call ant on your behalf. That's what Anthropic's own CLI scripting and automation page covers, and it's the subject of this one.

Version-controlling API resources as YAML

An agent, environment, or deployment is just a resource with an ID and a version number, and ant treats the file you define it in as the source of truth you keep in sync, not a one-time input. The pattern is the same for every resource type: write a YAML definition, create it, note the ID, then check the file into your repository and re-run an update command whenever it changes.

Define and create an agent

name: Summarizer
model: claude-opus-5
system: |
  You are a helpful assistant that writes concise summaries.
tools:
  - type: agent_toolset_20260401
ant beta:agents create < summarizer.agent.yaml
{
  "id": "agent_011CYm1BLqPXpQRk5khsSXrs",
  "version": 1,
  "name": "Summarizer",
  "model": "claude-opus-5"
}

Check summarizer.agent.yaml into your repository, and keep it synced with the API in CI. The update command needs both the agent's ID and its current version as flags:

ant beta:agents update --agent-id agent_011CYm1BLqPXpQRk5khsSXrs --version 1 < summarizer.agent.yaml

Define and create an environment

A session runs inside an environment, which defines its sandbox. The same create-then-track pattern applies:

name: summarizer-env
config:
  type: cloud
  networking:
    type: unrestricted
ant beta:environments create < summarizer.environment.yaml
ant beta:environments update --environment-id env_01595EKxaaTTGwwY3kyXdtbs < summarizer.environment.yaml

Start a session and send a message

With an agent ID and environment ID in hand, start a session exactly as you would with any other Managed Agents workflow:

ant beta:sessions create \
  --agent agent_011CYm1BLqPXpQRk5khsSXrs \
  --environment-id env_01595EKxaaTTGwwY3kyXdtbs \
  --title "Summarization task"
ant beta:sessions:events send \
  --session-id session_01JZCh78XvmxJjiXVy3oSi7K \
  --event '{type: user.message, content: [{type: text, text: "Summarize the benefits of type safety in one sentence."}]}'

Read the conversation back

--transform runs against every listed event, so this prints the text of each message in order:

ant beta:sessions:events list \
  --session-id session_01JZCh78XvmxJjiXVy3oSi7K \
  --transform 'content.0.text' --format auto --raw-output

--format auto overrides the interactive explorer a list command opens by default in a terminal, which matters the moment you run this from a script rather than a shell you're watching. To watch a session live instead of listing after the fact, ant beta:sessions:events stream --session-id session_01JZCh78XvmxJjiXVy3oSi7K writes events to stdout as they arrive.

Scripting patterns

The CLI is deliberately built to compose with ordinary shell tooling rather than requiring its own pipeline language.

Chain a list into a follow-up command

--transform id --raw-output on a list endpoint emits one bare ID per line, plain text with nothing else, so head, xargs and command substitution work directly against it:

FIRST_AGENT=$(ant beta:agents list --transform id --raw-output | head -1)

ant beta:agents:versions list \
  --agent-id "$FIRST_AGENT" \
  --transform "{version,created_at}" --format jsonl

The second command's --transform "{version,created_at}" shows the transform syntax handling an object shape rather than a single scalar field, and --format jsonl emits one JSON object per line, a natural fit for piping into jq or a log aggregator downstream.

Inspect errors, not just successes

--transform-error and --format-error apply the same filtering to a failed request that --transform and --format apply to a successful one. --raw-output specifically doesn't apply to errors, so use --format-error yaml when you want an unquoted scalar out of an error body:

ant beta:agents retrieve --agent-id bogus \
  --transform-error error.message --format-error yaml 2>&1
GET "https://api.anthropic.com/v1/agents/bogus?beta=true": 404 Not Found
Agent not found.

This is the pattern to reach for in a CI step that needs to fail loudly with a readable reason rather than a raw JSON blob, or in a script that checks a resource exists before proceeding.

GJSON transforms and output formats, in more depth

Everything above uses --transform and --format somewhat in passing. Since both are load-bearing for scripting, they're worth their own section, drawn from Anthropic's Using the CLI reference.

--transform takes a GJSON path and reshapes the response before printing. On a list endpoint, the expression runs against each item individually rather than the envelope around them:

ant beta:agents list \
  --transform "{id,name,model}" \
  --format jsonl
{"id": "agent_011CYm1BLqPXpQRk5khsSXrs", "name": "Docs CLI Test Agent", "model": "claude-opus-5"}
{"id": "agent_011CYkVwfaEtn8j2mQpXqZrs", "name": "Coffee Making Assistant", "model": "claude-opus-5"}

--format itself accepts six values: auto, json, jsonl, yaml, pretty, raw and explore. auto is the default for create and update commands, pretty-printed JSON. List and retrieve commands default to an interactive fold-and-search TUI when connected to a terminal (arrow keys expand and collapse nodes, / searches, q exits) and to pretty-printed JSON when piped, which is exactly the behaviour that makes --format auto worth setting explicitly in a script, as in the session-events example earlier: it guarantees plain output regardless of whether the script happens to run attached to a terminal.

--raw-output (or -r) and --format raw are easy to conflate but do different things. --raw-output strips the surrounding JSON quotes from a string result, the same as jq -r, and is what makes --transform id --raw-output usable directly in a shell variable assignment. --format raw instead prints the response body's raw JSON bytes without auto-pagination; on a list endpoint that means --transform applies to the pagination envelope itself rather than to each item, a distinction worth knowing before a --format raw transform returns something unexpected.

Passing request bodies: flags, stdin, and @file references

The CLI picks up request data from three places, and the right one depends on the shape of what you're sending. Flags suit scalar fields and short structured values; structured flag values accept a relaxed YAML-like syntax with unquoted keys, or strict JSON. Repeating a flag like --tool builds an array, one element per repetition. Stdin suits nested or multiline bodies; a piped JSON or YAML document merges with any flags on the same command, with flags taking precedence when both set the same field. @file references inline a file's contents into any string or binary field, --system @./prompts/researcher.txt reads a system prompt from disk, and the CLI detects file type and base64-encodes binary content automatically; force plain text with @file:// or forced base64 with @data:// when the automatic detection doesn't do what you want, and escape a literal leading @ with a backslash.

Debugging a request

--debug prints the exact HTTP request and response, headers included, to stderr, with API keys redacted:

ant --debug beta:agents list

This is the fastest way to confirm exactly what a script is sending when a flag's structured-value syntax isn't parsing the way you expect, rather than guessing from the CLI's own error message alone.

Using ant from inside Claude Code

Claude Code works with ant out of the box: with the CLI installed and authenticated on the machine Claude Code is running on, you can ask it to operate on your API resources directly, and it shells out to ant, parses the structured output, and reasons over the results without any custom integration code. Anthropic's own examples give a sense of the range:

  • "List my recent agent sessions and summarize which ones errored."
  • "Pull the events for session session_01... and tell me where the agent got stuck."
  • "Upload every PDF in ./reports to the Files API and print the resulting IDs."

This is a genuinely different way to use the CLI than running it yourself: instead of remembering exact subcommands and transform expressions, you describe the outcome and let Claude Code compose the ant calls. It's a good fit for one-off investigation work, debugging a session that behaved unexpectedly, or auditing a batch of resources, where writing a proper script would be overkill for something you'll only run once.

Authenticating plain curl requests with CLI credentials

A script that calls the API directly with curl or another HTTP client, rather than through ant itself, can still use the credentials ant auth login already stored, instead of managing a separate static API key. The OAuth access token goes in the Authorization header as a Bearer token; x-api-key is only for static API keys and isn't the right header for a CLI-issued token.

ant auth print-credentials --access-token prints the active profile's access token, refreshing it first if it's expired or close to expiring:

curl https://api.anthropic.com/v1/messages \
  -H "Authorization: Bearer $(ant auth print-credentials --access-token)" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 256,
    "messages": [{"role": "user", "content": "hi"}]
  }'

Keep ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN unset while doing this. Either variable takes precedence over a CLI login for ant commands, and can silently route a request to a different organization or workspace than the one you're logged into. Run ant auth status to confirm which organization and workspace you're actually authenticated against; it warns explicitly when an environment variable is overriding your login.

Where this fits with the rest of the platform

Every resource shown here, agents, environments, sessions, deployments, is the same Managed Agents surface covered in Claude Managed Agents explained, multiagent orchestration, scheduled deployments and session budgets. The scripting patterns on this page are what turn those API resources from something you configure once through the Claude Console into something a CI pipeline can create, update and tear down the same way it manages any other piece of infrastructure defined as code.

Troubleshooting

An update command fails even though the agent ID is correct. Check the --version flag matches the resource's current version, not the version you originally created it at. ant beta:agents retrieve --agent-id <id> returns the current version if you've lost track of it.

--transform id --raw-output prints nothing. Confirm the preceding list command actually returned results; an empty list produces empty transform output rather than an error, which can look identical to a broken pipe further down a script.

A script using CLI credentials picks the wrong organization. This is almost always ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN still set in the shell's environment, silently overriding the ant auth login session. Run ant auth status to confirm, and unset both variables if you intend to run purely off the CLI login.

Where to go next

Anthropic's own CLI scripting and automation page is the full reference this article draws from. For installing and authenticating ant itself, start with Anthropic's ant CLI. For the resources these scripts operate on, see Claude Managed Agents explained and how budgets work in Claude Managed Agents. Browse the wider catalogue at getclaudeskills.com/skills or by category.

Verified 26 August 2026 directly against Anthropic's CLI scripting and automation documentation at platform.claude.com/docs/en/cli-sdks-libraries/cli/scripting, read in full.

Frequently asked questions