Practical guide for LLM-driven harnesses (Claude Code, Cursor, your own bots).
- Stable JSON contract.
--jsonemits a valid JSON document to stdout and only a JSON document — no progress messages, no spinners. Errors go to stderr as{"error": "...", "detail": "..."}. - Stable exit codes.
0ok /1usage /2auth /3server /4rate limited /5not found. Agents can branch on these without parsing natural-language errors. - No interactive prompts in headless contexts. Pass
--yes(or-y) to destructive commands; pass--api-keyor setOMI_API_KEYto skip interactive login. - Forgiving retry behavior.
429and5xxare retried with backoff before surfacing.
The user gets a dev API key from the Omi web app
(https://app.omi.me → Developer → API Keys) and either:
omi auth login # interactive paste; key not in shell history
# or
export OMI_API_KEY=omi_dev_... # ephemeral, container-friendlyomi memory list --json --limit 50 | jq '.[] | {id, content, category}'omi memory create --json "User prefers dark mode" --category lifestyleomi conversation list --json --limit 5 \
| jq '.[] | {id, title: .structured.title, started_at}'omi action-item list --json --openomi action-item complete --json a1b2c3d4When Omi Desktop exposes its local API, agents can query on-device screen history, recaps, SQL, and tasks without using the cloud dev API:
omi local configure --url http://127.0.0.1:47778 --token ...
# or, for ephemeral sessions:
export OMI_LOCAL_API_URL=http://127.0.0.1:47778
export OMI_LOCAL_TOKEN=...
omi --json local status
omi --json local tools
omi --json local call search_screen_history --args-json '{"query":"pricing page","days":7}'
omi --json local search-screen "pricing page" --days 7 --app Safari
omi --json local screenshot 123 --output /tmp/omi-shot.jpg
omi --json local sql "SELECT COUNT(*) AS screenshots FROM screenshots"
omi --json local task search "taxes" --include-completedOnly complete or delete tasks when the user clearly asks:
omi --json local task complete task_123
omi --json local task delete task_123 --yesomi local screenshot SCREENSHOT_ID --output PATH writes the screenshot to
disk and still prints JSON to stdout for scripts. The screenshot ID usually
comes from local search-screen or SQL over the screenshots table. If Desktop
returns a structured failure such as screenshot_pending, screenshot_file_missing,
or screenshot_chunk_corrupted, JSON mode preserves the reason, hint, and
screenshot_id fields on stderr so agents can retry an older ID or report the
exact blocker. Validate successful outputs with file PATH before passing them
to vision tools.
import json
import subprocess
from typing import Any
def omi(*args: str) -> Any:
"""Invoke the omi CLI in JSON mode, raising on non-success exit codes."""
result = subprocess.run(
["omi", "--json", *args],
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
# The CLI prints structured errors to stderr in JSON mode:
# {"error": "...", "detail": "..."}
try:
err = json.loads(result.stderr)
except json.JSONDecodeError:
err = {"error": result.stderr.strip()}
raise RuntimeError(f"omi exited {result.returncode}: {err}")
return json.loads(result.stdout) if result.stdout.strip() else None
# Read all open action items and mark anything older than 30 days complete.
from datetime import datetime, timedelta, timezone
cutoff = datetime.now(timezone.utc) - timedelta(days=30)
items = omi("action-item", "list", "--open")
for item in items or []:
created = datetime.fromisoformat(item["created_at"].replace("Z", "+00:00"))
if created < cutoff:
omi("action-item", "complete", item["id"])Memories: 120/hr. Conversations: 25/hr. Batch creates: 15/hr.
result = subprocess.run(["omi", "--json", "memory", "create", text], capture_output=True, text=True)
if result.returncode == 4: # rate limited
err = json.loads(result.stderr)
# err["detail"] looks like: "Retry in 12s. ..."
time.sleep(parse_retry_window(err["detail"]) or 60)- Use
--profile <name>if your agent juggles multiple Omi accounts. Each profile has its own credential and API base. - Use
--api-base http://localhost:8080for local backend testing. - Use
OMI_LOCAL_API_URLandOMI_LOCAL_TOKENto override profile-local Desktop API settings for one run. - Use
--verbosefor debugging — it logsMETHOD path → status (Ns)to stderr without affecting stdout, so JSON mode stays valid. - For piping content into a conversation, use
--text -:cat meeting_notes.md | omi conversation create --text - --text-source other_text