The official Opper CLI — authenticate, route AI agent inference through Opper, and manage the Opper platform from your terminal.
# Global (recommended)
npm i -g @opperai/cli
opper --help
# Or one-shot via npx
npx @opperai/cli loginThe package installs an opper binary on PATH. Once installed, every command is just opper <…>.
Heads up: if you previously installed the legacy Go CLI (
brew install opper-ai/oppercli/opper), uninstall it first — both ship theopperbinary and PATH order decides which one runs:brew uninstall opperThe interactive menu also surfaces a warning when both are detected.
opper login # OAuth device flow
opper whoami # confirm the active slot
opper agents list # see which agents you can launch
opper launch claude # route Claude Code through OpperRun opper with no arguments for an interactive menu (Account · Ask · Agents · Skills · Opper). The menu also offers quick-launch shortcuts for any agent that's already installed and configured.
Auth state lives in ~/.opper/config.json as a list of "slots", each holding an API key, a base URL, and the user metadata returned by the device flow. Use --key <slot> on any command to pick which slot to read from (defaults to default).
| Command | Description |
|---|---|
opper login [--force] [--base-url <url>] |
OAuth device flow; stores into the active slot. |
opper logout [--all] [--yes] |
Clear credentials for the active slot, or every slot. |
opper whoami |
Show the authenticated user for the active slot. |
opper config add <name> <api-key> [--base-url <url>] |
Manually store an API key in a slot. |
opper config list |
List configured slots. |
opper config get <name> |
Print the raw API key (for scripting). |
opper config remove <name> |
Delete a stored slot. |
Key resolution at request time: OPPER_API_KEY env var > the slot named by --key (or default).
opper launch <agent> starts a supported AI agent with its model traffic transparently routed through Opper. Pass-through args after the agent name go straight to the agent's CLI. Each launch — except Claude Desktop (see the table below) — runs inside a fresh Opper session so every call the agent makes is grouped together for tracing and cost — see Routing through a session without the CLI to wire that up by hand. After the session, the CLI prints a summary with duration, model, and a traces link.
opper agents list # NAME / DISPLAY / KIND / STATE / CONFIG / COMMAND
opper launch claude # Anthropic Messages shape → /v3/session/<id>/v1/messages
opper launch claude-desktop # rewire Claude Desktop (GUI) → /v3/compat (persistent GUI profile)
opper launch opencode # OpenAI Chat Completions shape → /v3/session/<id>/chat/completions
opper launch codex # OpenAI Responses shape → /v3/session/<id>/responses
opper launch hermes # OpenAI Chat Completions shape → /v3/session/<id>/chat/completions
opper launch openclaw # OpenAI Chat Completions shape → /v3/session/<id>/chat/completions (background gateway)
opper launch pi # OpenAI Chat Completions shape → /v3/session/<id>/chat/completions
# Anything after the agent name is forwarded to its CLI — handy for
# scripting / cron with non-interactive flags.
opper launch pi -p "summarise this PR"
opper launch claude --resume| Agent | Slug | How Opper plugs in |
|---|---|---|
| Claude Code | claude |
ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN env vars |
| Claude Desktop | claude-desktop |
writes a third-party-inference (deploymentMode: "3p") profile into ~/Library/Application Support/Claude-3p/ (macOS) / %LOCALAPPDATA%\Claude-3p\ (Windows); quits and reopens the GUI app to apply |
| OpenCode | opencode |
provider block in ~/.config/opencode/opencode.json |
| Codex | codex |
sentinel-managed [model_providers.opper] + [profiles.opper-opus] block in ~/.codex/config.toml |
| Hermes | hermes |
isolated HERMES_HOME=~/.opper/hermes-home/ so your real ~/.hermes/ is never touched; OPENAI_API_KEY env var |
| OpenClaw | openclaw |
opper provider entry in ~/.openclaw/agents/main/agent/models.json; opper launch openclaw defaults to gateway start (background daemon) |
| Pi | pi |
opper provider entry in ~/.pi/agent/models.json (added/removed idempotently next to your other providers) |
opper launch <agent> --install runs the upstream agent's installer if it's missing (where supported). Claude Desktop is GUI-only on macOS/Windows and has no scripted installer — install it from https://claude.ai/download first.
The CLI also offers a per-agent submenu (opper → Agents → agent → Launch with model…) that lets you pick a specific Opper model from the catalog instead of the default.
To remove an agent's Opper integration without uninstalling the agent itself:
opper agents remove claude-desktop # works for any registered adapterThis is the non-interactive equivalent of the menu's "Remove Opper integration" action. It clears Opper-owned config (e.g., flips Claude Desktop's deploymentMode back to "1p", removes the opper provider block from OpenCode / Pi / OpenClaw, etc.) without touching anything you put there yourself.
For every launchable agent except Claude Desktop, opper launch is a thin convenience wrapper. Under the hood it does one thing: it mints a session id and points the agent's inference base URL at a session-scoped Opper endpoint, then lets the agent's own SDK speak its native protocol on top. You can wire this up by hand with any OpenAI-, Responses-, or Anthropic-shaped client — no CLI required. (Claude Desktop is the exception — it rewires a persistent /v3/compat GUI profile instead, so it doesn't get a per-launch session.)
https://api.opper.ai/v3/session/<session-id>[/<tag>:<value>…]/<native-path>
<session-id>— a stable id of the formsess_<uuid>(e.g.sess_3c0a79fd-e8e1-49c5-bc19-62ddd85f00c7). Reuse the same id for every call in one logical run to group them into a single session. The server validates the format — an id that doesn't start withsess_is rejected with400 {"error":"invalid session id"}.<tag>:<value>— optional attribution tags, added as extra path segments (e.g./team:growth/env:prod). URL-encode values; keep keys to[A-Za-z][A-Za-z0-9_.-]*and avoid the reservedopper.prefix.<native-path>— whatever path your client's SDK already appends. It also selects the compatibility shape:
| Client speaks | SDK appends | Full session URL |
|---|---|---|
| OpenAI Chat Completions | /chat/completions |
…/v3/session/<id>/chat/completions |
| OpenAI Responses | /responses |
…/v3/session/<id>/responses |
| Anthropic Messages | /v1/messages |
…/v3/session/<id>/v1/messages |
It's the same behaviour as the /v3/compat/... endpoints, just scoped to a session: the /v3/session/<id> prefix takes the place of /v3/compat, and the native tail still picks the wire shape.
Standard Opper bearer token, identical on every shape:
Authorization: Bearer $OPPER_API_KEY
(Anthropic SDKs default to x-api-key — set Authorization explicitly, or use ANTHROPIC_AUTH_TOKEN.)
SID="sess_$(uuidgen | tr '[:upper:]' '[:lower:]')"
curl -s "https://api.opper.ai/v3/session/$SID/chat/completions" \
-H "Authorization: Bearer $OPPER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [{"role": "user", "content": "Hello"}]
}'The response is an ordinary chat completion, with cost, usage, and a meta.trace_uuid for the call. Every request you send to the same $SID — any model, any wire shape — lands in that one session.
Set the client's base URL to the session endpoint (without the native path — the SDK adds that) and use your Opper key. How you set it depends on the client:
Env-var clients — any OpenAI-compatible SDK, and env-var-driven agents like Claude Code, read the base URL from the environment:
SID="sess_$(uuidgen | tr '[:upper:]' '[:lower:]')"
# OpenAI-compatible SDKs / clients
export OPENAI_BASE_URL="https://api.opper.ai/v3/session/$SID"
export OPENAI_API_KEY="$OPPER_API_KEY"
# Claude Code (Anthropic-shaped)
export ANTHROPIC_BASE_URL="https://api.opper.ai/v3/session/$SID"
export ANTHROPIC_AUTH_TOKEN="$OPPER_API_KEY"Config-file agents — OpenCode, Hermes, Pi, and OpenClaw don't read those env vars; they take the base URL from their own provider config (opencode.json, Hermes' base_url, ~/.pi/agent/models.json, …). Point that provider's base URL at https://api.opper.ai/v3/session/<id> and use your Opper key.
Either way, that is exactly what opper launch does for you — plus a fresh id per run and an end-of-session cost summary.
opper ask "<question>" runs an Opper agent grounded on the locally-installed Opper skills (see below). Useful for "how do I…" questions about the platform, SDKs, or the CLI itself.
opper ask "how do I create an index?"
opper ask --model claude-opus-4-7 "compare the v2 and v3 APIs"The answer streams in, then prints a token / request count. Requires Opper skills to be installed first (opper skills install).
Opper skills are markdown documentation packs the CLI uses for grounding opper ask and that you can install for any compatible code agent. The CLI delegates to the upstream skills tool, which fetches from opper-ai/opper-skills and symlinks into your agents' skill paths (~/.claude/skills/, etc.).
opper skills install # `npx skills add opper-ai/opper-skills` — interactive picker
opper skills update # refetch the latest from the source repo
opper skills list # per-target install matrix
opper skills uninstall # remove + clean up legacy bundled-copy installsopper editors list
opper editors opencode [--global|--local] [--overwrite] # also exposed as `opper launch opencode`Direct access to the platform endpoints:
| Command | Description |
|---|---|
opper call <name> <instructions> [input] [--model <id>] [--stream] |
Run an Opper function. Reads input from stdin when the positional arg is omitted. |
opper functions list [filter] / get <name> / delete <name> |
Manage functions. |
opper models list [filter] |
List available models (built-in + custom). |
opper models create <name> <identifier> <apiKey> [--extra <json>] |
Register a custom model. |
opper models get <name> / delete <name> |
Inspect / remove a custom model. |
opper indexes list [--limit] [--offset] |
List knowledge-base indexes. |
opper indexes get <name> / create <name> [--embedding-model <id>] / delete <name> |
Manage indexes. |
opper indexes add <name> <content> [--key <id>] [--metadata <json>] |
Add a document (<content> accepts - for stdin). |
opper indexes query <name> <query> [--top-k <n>] [--filters <json>] |
Semantic search. |
opper traces list [--limit] [--offset] [--name <substring>] |
List traces. |
opper traces get <id> / delete <id> |
Inspect / remove a trace. |
opper usage list [--from-date] [--to-date] [--granularity] [--fields] [--group-by] [--out csv] |
Token / cost analytics. |
opper image generate <prompt> [-o <file>] [--base64] [-m <model>] |
Generate an image. |
# Inline arguments
opper call myfunction "respond in kind" "what is 2+2?"
# Stream the response token-by-token
opper call --stream myfunction "respond in kind" "what is 2+2?"
# Pipe input from stdin (any text)
echo "what is 2+2?" | opper call myfunction "respond in kind"
# Pipe structured JSON in
echo '{"name":"Johnny","age":41}' | opper call myfunction "only print age"
# Override the model for one call
opper call --model claude-sonnet-4-6 myfunction "summarise" "long text…"Bring your own model deployment under any provider Opper supports — Azure, AWS, GCP, custom OpenAI-compatible endpoints, etc. Pass any provider-specific config through --extra as a JSON object.
# Azure OpenAI deployment
opper models create my-gpt4 azure/my-gpt4-deployment my-api-key \
--extra '{"api_base": "https://my-gpt4-endpoint.openai.azure.com/", "api_version": "2024-06-01"}'
# Inspect / delete
opper models get my-gpt4
opper models delete my-gpt4# Create an index
opper indexes create support-docs
# Add documents (inline or from stdin)
opper indexes add support-docs "How to reset your password: …" --key reset-password
cat refunds.md | opper indexes add support-docs - --key refunds --metadata '{"category":"billing"}'
# Search
opper indexes query support-docs "how do I get a refund?" --top-k 5
opper indexes query support-docs "billing question" --filters '{"category":"billing"}'If your application tags calls with arbitrary metadata (e.g. customer_id), opper usage list can group cost / count / tokens by that tag. Tagging happens at call time via the SDK:
# Python SDK
result, _ = await opper.call(
name="respond",
input="What is the capital of Sweden?",
tags={"customer_id": "acme"},
)# Then attribute spend per tag
opper usage list --from-date=2026-04-01 --to-date=2026-04-30 \
--fields=total_tokens,cost --group-by=customer_id
# Pipe to CSV for billing systems
opper usage list --from-date=2026-04-01 --group-by=customer_id --out=csv > april.csv# Save to image_<ts>.png in cwd
opper image generate "a serene mountain lake at dawn"
# Specific output, specific model
opper image generate "logo concept" -o ./out/logo.png \
-m vertexai/imagen-4.0-fast-generate-001-eu
# Print raw base64 (for piping)
opper image generate "icon" --base64 | base64 -d > icon.png# Pi in non-interactive mode for cron / CI
opper launch pi -p "summarise the latest PR title and body"
# Claude Code with a specific model and resumed session
opper launch claude --model claude-opus-4-7 --resume
# Codex with Sonnet for a single ask
opper launch codex --model claude-sonnet-4-6 -- "implement this feature"| Flag | Description |
|---|---|
--key <slot> |
API key slot to use (default: default). |
--debug |
Verbose diagnostic output. |
--no-telemetry |
Disable anonymous telemetry. |
--no-color |
Disable ANSI colors. |
-v, --version |
Print CLI version. |
-h, --help |
Show help (grouped by domain). |
- Node.js ≥20.12 (for
util.styleText, used by interactive prompts). - macOS, Linux, or WSL. Native Windows shells aren't tested.