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

# Scripting and CI

> Use the caesar-search CLI in scripts, schedulers, and CI pipelines: JSON-only parsing, exit-code control flow, configuration precedence, and the raw API escape hatch.

The CLI is built to be scripted: `--json` gives a single JSON object on stdout, errors are a JSON envelope on stderr, and exit codes are a stable contract.

## Branch on exit codes, parse only JSON

```bash theme={"system"}
#!/usr/bin/env bash
set -u

caesar-search search "zero-downtime postgres schema migration" \
  --format compact --json -o /tmp/results.json
code=$?

case "$code" in
  0) jq -r '.results[].doc_id' /tmp/results.json ;;
  3) echo "auth error: set CAESAR_API_KEY or run caesar-search auth login" >&2; exit 1 ;;
  4) echo "API error after retries; see the stderr envelope" >&2; exit 1 ;;
  5) echo "timeout: retry or raise --timeout" >&2; exit 1 ;;
  *) echo "bad input (exit $code): fix the command" >&2; exit 1 ;;
esac
```

This example bakes in the three scripting rules:

* **`--json` on every call.** Human output is not a stable interface — its wording and layout can change between releases. Errors arrive on stderr as `{"error": {"code": "...", "message": "...", "hint": "..."}}` (`hint` only when present); all fields are snake\_case.
* **Exit codes are the contract**, not output text: `0` success, `2` bad input, `3` auth, `4` API error, `5` timeout. The full table is on [CLI usage](/clients/cli/usage).
* **`-o` for artifact capture.** It writes the payload to the file and suppresses stdout entirely, so nothing competes with the file and output limits can't truncate JSON mid-parse. Upload the file as a CI artifact, parse it with `jq`.

## Configuration precedence

Resolution order, highest first:

| Setting  | Order                                                                                                                        |
| -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| API key  | `--key` flag → `CAESAR_API_KEY` env → OS keychain (written by browser login) → config file `api_key` → auth error if missing |
| Base URL | `--base-url` flag → `CAESAR_BASE_URL` env → config file `base_url` → default `https://alpha.api.trycaesar.com`               |

The general rule is flag over env over config file over built-in default. In CI, prefer a masked `CAESAR_API_KEY` secret over the config file — nothing to provision on disk, and `caesar-search auth status --json` will confirm `key_source` is `env`.

## Let one layer own retries

By default the CLI retries HTTP 429 and 5xx up to 3 times (4 attempts total) with exponential backoff capped at 8 seconds. Caesar rate limits use `X-RateLimit-Reset`; if a numeric `Retry-After` header is present, the CLI honors it. If your scheduler or workflow engine already handles retries, disable the CLI's so attempts aren't multiplied:

```bash theme={"system"}
caesar-search search "site reliability postmortem templates" --json --no-retry
```

With `--no-retry`, a 429 or 5xx fails immediately with exit 4 and the error envelope on stderr — your outer retry policy takes it from there.

## The `api` escape hatch

`caesar-search api <method> <path>` makes an authenticated raw call against the API — same key resolution, retries, and timeout — for endpoints the CLI doesn't wrap:

```bash theme={"system"}
echo '{"query":"hello","max_results":3}' | caesar-search api POST /v1/search --input -
caesar-search api GET /skills
```

Methods: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. The path must start with `/`. `--input <file>` (or `--input -` for stdin) supplies a JSON body. Output is always JSON — `api` ignores `--json` — and still honors `-o`. The endpoint catalog lives in the [API reference](/api-reference/index).

## Pin versions; check for drift

Auto-updating mid-pipeline makes builds unreproducible. Instead:

* Pin the CLI in CI: `npm install -g caesar-search-cli@0.2.0`, or fetch a specific release archive and verify it against `checksums.txt`.
* Detect drift with the read-only check and surface it — don't act on it automatically:

```bash theme={"system"}
caesar-search update --check --json
# {"current": "0.1.5", "latest": "0.2.0", "update_available": true, "channel": "npm"}
```

* Upgrade deliberately with `caesar-search update`; it picks the right channel (npm, brew, standalone). Never `npm update -g` or `brew upgrade` by hand.

<Note>
  `update --check` works on every channel, including source (`dev`) builds. Only the actual update refuses on `dev` builds (exit 2).
</Note>

## For agents

* Always pass `--json`; parse stdout only. Errors are a JSON envelope on stderr.
* Branch on exit codes (`0`/`2`/`3`/`4`/`5`), never on output text.
* Prefer `-o /tmp/<name>.json` then `cat` the file — `-o` suppresses stdout so harness output limits can't truncate JSON.
* Exit 3 means auth: set `CAESAR_API_KEY` or run `caesar-search auth login` before retrying.
* Pass `--no-retry` only when your own loop handles retries; otherwise let the CLI back off.

Full common-mistakes table: [CLI usage](/clients/cli/usage).
