Summary
Every account command authenticates against pay.quicksilverpro.io, and that host accepts only a dashboard session, not an API key. So the CLI's whole account surface fails for the one credential a user actually has.
Measured against production with a live, funded key:
pay/v1/me -> 401 {"detail":"Invalid or expired session."}
pay/v1/keys -> 401 {"detail":"Invalid or expired session."}
pay/v1/usage -> 401 {"detail":"Invalid or expired session."}
api/v1/models -> 200
$ QSP_API_KEY=sk-... qsp balance --json
Invalid or expired session.
exit=1
What this breaks
| command |
state |
qsp balance |
broken — /v1/me |
qsp whoami |
broken — /v1/me |
qsp init --key sk-... |
broken — _save_key() verifies via /v1/me before storing, so it rejects a perfectly good key |
qsp models |
works (hits api.*) |
qsp chat |
works (hits api.*) |
qsp init --key is the worst of the three: it is the documented way to get set up non-interactively, and it refuses valid keys.
Why it matters more than the command count suggests
The CLI's stated reason to exist is being agent-friendly — --json, small surface, reliable exit codes. The single most obvious agent use of it is "check the balance before spending", and that is exactly what does not work. It also blocks the CI cost-guard pattern entirely, since there is no key-authenticated way to read a balance.
Exit codes themselves are correct and worth keeping: 0 success, 1 remote/operational, 2 usage/auth — verified all three.
Suggested fix
Give pay.* an API-key-authenticated read path for the caller's own account, or expose balance/usage through api.* where the key already authenticates. LiteLLM's /key/info already returns spend and budget for the presenting key, which may be the shortest route.
Whichever path, _save_key() should verify against an endpoint the key can actually authenticate against.
Acceptance
Summary
Every account command authenticates against
pay.quicksilverpro.io, and that host accepts only a dashboard session, not an API key. So the CLI's whole account surface fails for the one credential a user actually has.Measured against production with a live, funded key:
What this breaks
qsp balance/v1/meqsp whoami/v1/meqsp init --key sk-..._save_key()verifies via/v1/mebefore storing, so it rejects a perfectly good keyqsp modelsapi.*)qsp chatapi.*)qsp init --keyis the worst of the three: it is the documented way to get set up non-interactively, and it refuses valid keys.Why it matters more than the command count suggests
The CLI's stated reason to exist is being agent-friendly —
--json, small surface, reliable exit codes. The single most obvious agent use of it is "check the balance before spending", and that is exactly what does not work. It also blocks the CI cost-guard pattern entirely, since there is no key-authenticated way to read a balance.Exit codes themselves are correct and worth keeping:
0success,1remote/operational,2usage/auth — verified all three.Suggested fix
Give
pay.*an API-key-authenticated read path for the caller's own account, or expose balance/usage throughapi.*where the key already authenticates. LiteLLM's/key/infoalready returns spend and budget for the presenting key, which may be the shortest route.Whichever path,
_save_key()should verify against an endpoint the key can actually authenticate against.Acceptance
QSP_API_KEY=sk-... qsp balance --jsonreturns balance and exits 0qsp init --key sk-...accepts a valid keyqsp whoami --jsonworks with a key