feat(api): structured ?format=json for the self-service usage endpoint (#11190)

* feat(api): structured ?format=json for the self-service usage endpoint

GET /api/usage/om-usage already let any key read its own usage — personal
daily/weekly USD limits and the provider quota snapshot — but only as
text/plain, which a UI cannot parse safely. OmniCopilot issue #8 asks exactly
for this surface.

Adds ?format=json, returning the ApiKeyUsageLimitStatus + UsageSnapshot the
text is rendered from. Text and JSON share the same collectors
(collectUsageSnapshots, getApiKeyUsageLimitStatus), so the two can never
disagree about a number. The response is a discriminated union: a key without
allowUsageCommand (403) or an invalid key (401) returns
{ allowed:false, error:{message} }, distinct from allowed:true with empty
sections — the state a panel must render as "nothing learned yet", not a
refusal. Text form unchanged; without ?format the contract is untouched.

The endpoint was previously missing from API_REFERENCE.md; it now has a
section documenting both forms, the allowUsageCommand gate, and the
self-service auth model (caller's own key, not requireManagementAuth).

Regression guards in tests/unit/usage-command-json-format.test.ts (4 tests:
json shape, text default preserved, structured 403, sanitized 401 with no
stack trace). Existing internal-usage-command suite still 12/12.

* chore(changelog): correct the fragment to the real PR number (#11190)

---------

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-22 22:23:17 -03:00
committed by GitHub
parent 62ab93d789
commit eb9fa33ee7
4 changed files with 257 additions and 5 deletions

View File

@@ -636,6 +636,47 @@ completion.
---
## Self-service usage (`/api/usage/om-usage`)
Any API key can read **its own** usage and quotas — no management auth. This is the endpoint a
client (CLI, the OmniCopilot panel) uses to show a key holder their spend.
```bash
# Text form (the historical contract — plain text for a terminal)
curl -H "Authorization: Bearer <your-api-key>" \
http://localhost:20128/api/usage/om-usage
# Structured form — what a UI consumes
curl -H "Authorization: Bearer <your-api-key>" \
"http://localhost:20128/api/usage/om-usage?format=json"
```
The key must have **`allowUsageCommand`** enabled (off by default — the dashboard's API-key
manager toggles it per key). Without it the endpoint answers `403`.
`?format=json` returns a discriminated shape so a caller never reads a data field off a
refusal. On success:
```jsonc
{
"allowed": true,
// present only when the key opted into per-key usage limits (daily/weekly USD):
"personal": { "dailySpentUsd": 1.25, "dailyLimitUsd": 5, "dailyResetAtIso": "…", "weeklySpentUsd": 8, "weeklyLimitUsd": 20, "weeklyResetAtIso": "…" /* */ },
// the selected provider quota snapshot, or null when nothing is cached yet:
"provider": { "connectionId": "…", "provider": "claude", "plan": "…", "quotas": { /* */ } }
}
```
On refusal (`401` bad key / `403` not allowed) the same route returns
`{ "allowed": false, "error": { "message": "…" } }` — a present-but-empty `personal`/`provider`
(key allowed, nothing learned yet) is a different state from a refusal, and only the JSON form
distinguishes them.
**Auth:** the caller's own Bearer API key, validated with `isValidApiKey` — this is *not* the
management surface (`/api/keys/…`), which stays behind `requireManagementAuth`.
---
## Semantic Cache
```bash