API And Metrics

简体中文

The HTTP API is mostly read-only. It is intended for local dashboards, agent wrappers, Prometheus, and operator smoke tests. The only mutating v1 endpoint is the protected manual direct-quota refresh action. Unknown JSON fields may be added in 0.x releases; clients should ignore fields they do not understand.

OpenAPI 3.1 contract: openapi.json. The versioned /api/v1/* paths are the public integration surface. Older unversioned /api/* JSON paths were removed during the 0.x API cleanup.

The API is not an auth boundary. --allowed-host and the built-in Host / Origin checks are request-origin guards, not login or authorization. Keep the monitor on 127.0.0.1 or protect it with an authenticated reverse proxy before exposing it to a LAN or public network.

The browser Privacy toggle is display-only. It masks email addresses and token-like strings in the rendered page, but API and metrics endpoints keep their original payloads for wrappers, agents, Prometheus, and smoke tests.

For the data-flow and secret-redaction model behind these endpoints, see the security self-audit and threat model.

Endpoints

When the process is started with --demo, these same endpoints serve built-in sample data on 127.0.0.1:4516 by default, and /api/v1/status includes "demo": true with source = "demo". Demo mode does not read auth files, management keys, SQLite history, benchmark summaries, or CPA endpoints.

Redacted Examples

/api/v1/recommendations:

{
  "contractVersion": "1.1",
  "sampledAt": "2026-04-20T12:00:00+00:00",
  "source": {"kind": "live", "text": "Live via CLIProxyAPI + direct Codex quota", "gatewayOk": true},
  "routingPolicy": {
    "scoreMax": 100,
    "capacityWeightUnit": "plus",
    "hardBlocks": [
      "routeEligible=false",
      "tone=bad",
      "status disabled/unavailable/invalid/auth invalid/cooldown",
      "exhausted quota windows"
    ]
  },
  "summary": "3 routable accounts · 1 blocked account · top score 86",
  "routableCount": 3,
  "blockedCount": 1,
  "topScore": 86,
  "items": [
    {
      "selector": {
        "authName": "auth-redacted.json",
        "authIndex": "acct-redacted",
        "accountKey": "acct-redacted"
      },
      "authIndex": "acct-redacted",
      "accountKey": "acct-redacted",
      "planKind": "plus",
      "title": "account-redacted@example.invalid",
      "badge": "Plus",
      "summary": "Active · 5h 80% · weekly 80%",
      "routingScore": 86,
      "routingEligible": true,
      "routingBlockedReasons": [],
      "scoreComponents": {"capacity": 36, "health": 25, "plan": 5, "load": 10, "reclaim": 10},
      "capacityWeight": 1.0,
      "quotaWindows": [
        {"id": "5h", "state": "known", "percent": 80, "resetsInSeconds": 3600},
        {"id": "week", "state": "known", "percent": 80, "resetsInSeconds": 86400}
      ],
      "reclaimEligible": true
    }
  ]
}

/api/v1/health:

{
  "title": "Health",
  "state": "warn",
  "summary": "1 item needs review.",
  "pill": "1 action",
  "actions": [
    {
      "kind": "threshold",
      "tone": "bad",
      "title": "5h capacity below threshold",
      "badge": "Threshold",
      "summary": "0.40 Plus < 0.50 Plus"
    }
  ],
  "sources": [],
  "recentChanges": [],
  "actionCount": 1
}

Stable Fields

/api/v1/recommendations and /api/v1/health are the recommended wrapper-facing contracts. Account selection automation should use /api/v1/recommendations and treat deep /api/v1/status.tabs.* payloads as UI implementation detail unless the fields are explicitly documented here or in openapi.json.

Stats Series

/api/v1/stats/series is the stable machine-readable surface behind the Stats workbench. It reads deduplicated timestamped usage details from the monitor SQLite database, so it is empty until the process runs with --state-db and CPA has returned usage detail rows with timestamps.

Supported query parameters:

CSV exports use these columns:

timestamp,series_id,series_label,requests,tokens,failed,request_text,token_text,failure_rate

The browser stores Stats controls such as range, metric, grouping, smoothing, and display mode in localStorage. Those preferences are display state; they do not mutate the API payload or monitor SQLite history.

Client labels are loaded from a JSON file:

{
  "clients": [
    {"apiName": "sk-example-from-cpa", "label": "Alice"},
    {"api_name": "rotated-api-name", "label": "Alice"}
  ]
}

Pass it with --client-labels-file /path/to/client-labels.json, or set CODEX_QUOTA_MONITOR_CLIENT_LABELS_FILE. Keep the file with runtime secrets; do not put real API names in world-readable docs or Nix store text.

Prometheus

Use examples/prometheus/prometheus.yml for scrape config and examples/prometheus/alerts.yml for starter alerts. Useful metrics include:

CLI Helpers

CLI flags override environment variables, and environment variables override TOML config files. Use --config /path/to/config.toml or CODEX_QUOTA_MONITOR_CONFIG; use --config off to ignore config files. Use --allowed-host / CODEX_QUOTA_MONITOR_ALLOWED_HOSTS when a browser must reach the dashboard through a non-loopback hostname or IP; values are comma-separated authorities such as monitor.lan,192.0.2.10. Omit the port to allow that host with any browser-facing port, or include :4515 to require a specific Host/Origin port. This allowlist does not authenticate viewers. For reverse proxies, use the browser-facing hostname such as quota.example, not the loopback upstream. The v1 browser UI expects to live at the root path of that hostname; path-prefix deployments are not supported.