Skip to main content
omi-cli is built so an LLM-driven harness can drive it without a wrapper layer. This page documents the stable contract.

The JSON contract

Pass --json (a global flag, before the subcommand) and the CLI behaves like a strict tool:
  • stdout receives a single JSON document and only a JSON document. No spinners, no progress messages, no decorative output.
  • stderr receives any error as a JSON object: {"error": "...", "detail": "..."}.
  • exit code signals what happened.

Exit codes

These codes are part of the contract — they won’t shift between minor versions. Branch on them in your harness without parsing English errors.

Authentication: API key vs browser OAuth

The CLI supports both — but for agents specifically, the API-key path is almost always the right call: API keys are long-lived, scoped, and don’t need a browser — perfect for agents. The browser OAuth flow exists for humans on a laptop, uses a loopback callback with PKCE, and stores short-lived Firebase ID tokens that auto-refresh between calls.
The on-disk config file is great for humans but a footgun in shared CI runners. The env var injection runs the same prefix validation as omi auth login so a malformed value still fails fast with exit 1.

Retry behavior

Transient failures are retried automatically before the CLI surfaces an error:
  • 5xx — exponential backoff with jitter (initial 0.5s, cap 8s).
  • 429 — honors the server’s Retry-After header when present (capped at 60s so a misconfigured upstream can’t pin your agent forever); falls back to jitter otherwise. The Retry-After cap means a 429 with a one-hour hint becomes a one-minute wait — your agent gets exit 4 quickly enough to decide whether to back off itself.
  • Transport errors (DNS failures, dropped TCP) — retried up to 4 attempts.
After retries are exhausted, the CLI surfaces a structured error and the appropriate exit code. The detail field for rate-limit errors looks like:
The policy name (dev:conversations, dev:memories, dev:memories_batch) matches the backend’s rate-limit policy IDs so you can map them to your own backoff strategies.

Worked example: Python harness

Handling rate limits in your harness

Tips

  • One JSON document per invocation. Don’t try to stream — the CLI doesn’t emit incremental output. Run it again for the next page.
  • Use --profile to isolate environments. A staging profile + a prod profile saves you from accidentally writing to prod with a test script.
  • Use --api-base http://localhost:8080 for local backend testing.
  • --verbose is safe in JSON mode. Debug output goes to stderr, stdout stays valid JSON.
  • Pipe stdin with --text - for omi conversation create — handy when the content is generated by another tool and you don’t want to shell-escape it.