> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bronto.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Scripting and agents

> Output formats, JSON Lines when piped, the error envelope, exit codes, jq recipes, and how agents should drive the bronto CLI.

`bronto` is meant to be called from shell scripts, CI jobs, and AI agents. Data goes to
stdout and everything else to stderr, in the formats described on this page.

<Frame caption="Piping output from the bronto CLI into jq and back">
  <video src="https://mintcdn.com/bronto/KAWScdJxL520s-EQ/cli/tapes/scripting.webm?fit=max&auto=format&n=KAWScdJxL520s-EQ&q=85&s=9fed7acdc8ef1d9ac8bfd6e03e5c5ea9" poster="/cli/tapes/scripting.png" aria-label="Piping output from the bronto CLI into jq and back" autoPlay muted loop playsInline controls data-path="cli/tapes/scripting.webm" />
</Frame>

## Output when piped

You usually don't need `-o`. The CLI checks whether stdout is a terminal:

| Command | At a terminal | Piped |
| - | - | - |
| `search`, `tail`, `traces` | table | JSON Lines, one object per line |
| everything else (`list`, `get`, `usage`, `config list`, ...) | table | one pretty-printed JSON document, usually an array |

Parse that JSON document as a whole. For one row per line from a non-streaming command,
pass `-o jsonl`.

```console theme={"dark"}
$ bronto datasets list -o jsonl --fields collection,dataset,log_id
{"collection":"prod","dataset":"checkout-service","log_id":"0b7c6f2e-1d4a-4c8e-9a51-3f2d8e6b7a10"}
{"collection":"prod","dataset":"payments-api","log_id":"5e2a9c41-7b3d-4f6a-8c20-9d1e4b5f6a21"}
{"collection":"prod","dataset":"web-frontend","log_id":"8f1d3b7a-2c5e-4a9b-b6d4-1e7f9a3c2b32"}
{"collection":"staging","dataset":"checkout-service","log_id":"c3a8e5d2-9f4b-4e1a-a7c3-6b2d8f1e4c43"}
$ bronto monitors list | jq length
2
```

`-o` forces a format: `table`, `json`, `jsonl`, `csv`, or `raw`. `raw` prints each event's
original `@raw` line, for piping logs into other tools. The full rules
are in [Output formats](/cli/reference/output-formats).

64-bit numbers such as `metadata.sequence` come through `json`, `jsonl`, and `--jq`
unchanged, without float rounding.

## Pick fields

`--fields a,b,c` keeps only those keys (or table columns). `--fields '?'` prints the
available field names instead of the data:

```console theme={"dark"}
$ bronto datasets list --fields '?'
collection
dataset
log
log_id
metadata
$ bronto datasets list --fields collection,dataset
[
  {
    "collection": "prod",
    "dataset": "checkout-service"
  },
...
]
```

Search events are flattened, and fields parsed from the log line sit under
`message_kvs.`. `--fields` accepts the bare name: `status` finds `message_kvs.status`
when the event has no top-level `status` key, and the output key is the name you typed.
The full name `message_kvs.status` works too, and keeps that key:

```console theme={"dark"}
$ bronto search "status >= 500" -d prod/checkout-service --since 1h -n 3 -o jsonl --fields '@time,host,status,path'
{"@time":"2026-07-19 09:24:49.632 UTC","host":"web-3","path":"/api/checkout","status":502}
{"@time":"2026-07-19 09:24:33.007 UTC","host":"web-3","path":"/api/checkout","status":502}
{"@time":"2026-07-19 09:24:18.321 UTC","host":"web-2","path":"/api/checkout","status":502}
```

`--select` asks the server for just the named fields. It takes the names you'd write in a
query, and the result uses them as keys:

```console theme={"dark"}
$ bronto search "status >= 500" -d prod/checkout-service --since 1h -n 2 --select status --select path
{"path":"/api/checkout","status":502}
{"path":"/api/checkout","status":502}
```

## jq built in

`--jq '<expr>'` runs a jq expression over `json` or `jsonl` output and prints each result
on its own line. It uses [gojq](https://github.com/itchyny/gojq), so you don't need jq
installed.

Results print as JSON, so strings keep their quotes. Add `-r` (`--raw-output`), as with
`jq -r`, to print strings without quotes; other values still print as compact JSON.
`--jq` runs after `--fields`. On its own it sees the whole event, so search fields keep
their `message_kvs.` names. With `--fields host,trace_id` it sees only those output keys,
and you write `.host` instead.

```console theme={"dark"}
$ bronto monitors list -o json --jq '.name'
"High 5xx rate on checkout"
"Payments latency p95"
$ bronto monitors list -o json --jq '.name' -r
High 5xx rate on checkout
Payments latency p95
$ bronto search "status >= 500" -d prod/checkout-service --since 1h -o jsonl --jq 'select(."message_kvs.host" == "web-1") | ."message_kvs.trace_id"' -r
9c244820635f8c0dc8342c0c2058c1c3
8cadcf646ba7d02e25166280c042e3cb
67d30b1a2fddae59f08e7502c43a12e3
$ bronto search "status >= 500" -d prod/checkout-service --since 1h -o jsonl --fields host,trace_id --jq 'select(.host == "web-1") | .trace_id' -r
9c244820635f8c0dc8342c0c2058c1c3
8cadcf646ba7d02e25166280c042e3cb
67d30b1a2fddae59f08e7502c43a12e3
```

Differences from the `jq` CLI:

* A value that makes the expression fail is skipped, and the other rows still print.
* `-r` is the only jq option. It needs `--jq`.
* At a terminal the default format is a table, so add `-o json` or `-o jsonl`. Piped,
  `--jq` works without `-o`.

Some recipes:

```sh theme={"dark"}
# Count 5xx responses per path
bronto search "status >= 500" -d prod/checkout-service --since 1h -o jsonl \
  --jq '."message_kvs.path"' -r | sort | uniq -c | sort -rn
```

Show the waterfall for the slowest failing request's trace:

```console theme={"dark"}
$ bronto traces show "$(bronto search "status >= 500" -d prod/checkout-service --since 1h \
  --order-by 'duration_ms DESC' -n 1 -o jsonl --jq '."message_kvs.trace_id"' -r)"
6 span(s) across 3 service(s), total 30.05s
web-frontend/GET /checkout             ████████████████████████████████████████ 30.05s ERROR
  checkout-service/POST /api/checkout  ███████████████████████████████████████· 30.05s ERROR
    checkout-service/SELECT orders     █······································· 13.00ms
    checkout-service/POST /charge      ███████████████████████████████████████· 30.02s ERROR
      payments-api/POST /charge        ███████████████████████████████████████· 30.01s ERROR
        payments-api/provider.authorize ███████████████████████████████████████· 30.01s ERROR
```

## Errors

Errors go to stderr. When stderr is a terminal you get a readable message and a hint. When
it isn't, you get one line of JSON:

```json theme={"dark"}
{"error":{"code":"usage_missing_dataset","hint":"Pick one with -d \u003cname\u003e: ...","message":"no dataset selected","retryable":false}}
```

* `code` is a stable identifier such as `auth_invalid_key` or `resource_not_found`. Match
  on `code`. `message` can change.
* `retryable` is `true` for rate limits, timeouts, network errors, and server errors.
* `hint` holds a suggested fix, when there is one. Agents should read it.

## Exit codes

| Code | Meaning |
| - | - |
| `0` | success |
| `1` | unexpected or unclassified error |
| `2` | usage or config error (bad flags, bad arguments, missing dataset) |
| `3` | auth error (missing, invalid, or under-privileged key) |
| `4` | not found |
| `5` | rate limited or timed out: retry reads; after a timed-out write, check whether it landed first |

[Exit codes](/cli/reference/exit-codes) maps every error code to its exit status.

## Safe automation

* `--dry-run` stops before any request that changes something. Every mutating command,
  `delete` and action verbs such as `groups add-members` included, prints the request as a
  plan on stdout, `{"body":{...},"dry_run":true,"method":"POST","path":"/monitors"}`.
  Reads still run, so names resolve and the plan's path carries the resolved id. An agent
  can show the plan to a human before running the real command.
* Destructive commands refuse to run without a terminal unless you pass `--yes`
  (`usage_confirmation_required`, exit code 2).
* `--debug` traces each API request and response on stderr: method, URL, status, latency,
  and truncated bodies. The API key header is never printed, and secret fields in bodies,
  such as the keys `api-keys list` returns, show as `REDACTED`. Treat the trace as
  sensitive anyway: bodies can contain log data.
* `--quiet` suppresses notices on stderr, so only data and errors remain.
* `--timeout <seconds>` and `--max-retries <n>` tune the HTTP client. Retries apply to
  idempotent requests that get a 429, 502, 503, or 504.
* `--no-color` (or `NO_COLOR`) turns off ANSI colors.

## Credentials for scripts

On a machine where someone has run `bronto auth login`, a script can reuse that key
without reading the keychain itself:

```sh theme={"dark"}
export BRONTO_API_KEY=$(bronto auth token)
```

`bronto auth token` prints the key the CLI would use, from wherever it resolved.
In CI, set `BRONTO_API_KEY` directly. See [CI/CD](/cli/guides/ci-cd).

## For AI agents

* [`skill.md`](https://github.com/bronto-community/bronto-cli/blob/main/skill.md) in the
  repository is a short guide for agents such as Claude Code. It covers the main commands,
  the output contract, and the exit codes. Point your agent at it, or drop it into
  your agent's skills directory.
* [`llms.txt`](https://github.com/bronto-community/bronto-cli/blob/main/llms.txt) in the
  repository is a short summary of the CLI.
* Every page of these docs is also available as Markdown: add `.md` to its URL, or use
  the page menu next to the title to copy it or open it in an AI assistant. The docs
  site serves `/llms.txt`, an index of all pages, and `/llms-full.txt`, every page in
  one file.
* `bronto --help` and `bronto <command> --help` always match the installed binary.
* `bronto ask` without `--yes` prints a query plan as JSON when piped, so an agent can
  review a generated query before running it. See [Ask](/cli/guides/ask).

Bronto also has [agent skills](https://docs.bronto.io/ai-features/bronto-agent-skills) and a
[hosted MCP server](https://docs.bronto.io/ai-features/hosted-mcp) on the platform side.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.