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 don’t change between minor versions, so branch on them in your harness instead of parsing error text. When Desktop returns the exact execute_sql result SQL Error: The local database could not complete that query., both omi local sql and omi local call execute_sql exit with code 1, including when Desktop wraps that result in an HTTP 200 response. With --json, this error goes to stderr and stdout stays empty. This message match does not classify other SQL result strings as failures. A successful query with no rows still returns exit code 0.

Authentication: API key vs browser OAuth

The CLI supports both. For agents specifically, the API-key path is almost always the right call: API keys are long-lived and scoped, and they don’t need a browser, so they suit 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, so your agent gets exit 4 quickly enough to decide whether to back off itself.
  • Transport errors (DNS failures, dropped TCP) are 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. This is handy when the content is generated by another tool and you don’t want to shell-escape it.