[Admin][API] Usage & Spending — aggregate ledger, platform-paid vs BYOK split, cost + AI kill switch
Why
The core operator concern. The platform currently pays for internal AI (PLATFORM_AI_ENABLED="true": embeddings + summaries + translation fallback) and that spend is invisible in-app. We need cross-user usage + a clear platform-paid vs BYOK cost split, and an operator control to cap/kill platform spend.
Scope
GET /v1/admin/usage (behind requireAdmin): aggregate the ai_usage ledger across ALL users — group by provider, model, kind, user, agent/instance, and day. Return token totals and cost_micros totals with a time window (?days=).
- Platform-paid vs BYOK split: classify each
ai_usage row as platform-paid (internal kinds run on env.AI when PLATFORM_AI_ENABLED — embeddings/summary/translation) vs BYOK (user's Anthropic/CF key). Since CF platform calls currently ledger at cost_micros≈0, add estimated cost via a neuron/token price table so platform spend is a real number, not zero. Document the estimate.
GET /v1/admin/spending — top-line: total platform-paid $ (30d), total BYOK tokens (informational), top spenders, top models, trend series.
- AI kill switch:
GET/PUT /v1/admin/settings/platform-ai to read and toggle the effective PLATFORM_AI_ENABLED behavior at runtime (e.g. a platform_settings row overriding the env default), so the operator can turn platform-paid AI off without a redeploy. Off → embeddings/summaries/translation no-op (BYOK-only); note the known side effect that RAG goes dark until a BYOK-embedding path exists (track separately).
Acceptance criteria
Relevant code
- Ledger:
workers/api/src/lib/usage.ts (recordUsage → ai_usage, has cost_micros), migration 0048_ai_usage.sql
- Platform-AI gate:
agent-do.ts:72, routes/instances-translation.ts, wrangler.toml PLATFORM_AI_ENABLED
- Existing per-user view to reuse shapes:
routes/usage.ts
References
- PAS platform analytics endpoint pattern (
/v1/analytics/admin/platform). PAS has no usage/cost admin — this closes that gap for PAGS.
Part of the PAGS Admin Portal epic. Depends on Foundation. Directly addresses the "platform should not pay for AI" concern.
Best-practice hardening (2026-08-01)
- Authoritative spend = Cloudflare billing actuals, not only a token estimate. Platform-paid Workers AI is billed per neuron; our
ai_usage.cost_micros is ~0 for CF and the token→price table is a derived guess. Pull the real platform AI cost from Cloudflare's GraphQL Analytics / billing API (Workers AI usage) as the authoritative number for the "platform spend" headline. Keep the token estimate only for BYOK (where we genuinely can't see the provider bill) and label estimates as estimates in the API + UI so the two are never conflated.
- Cache the CF billing pull (it's rate-limited + slow); expose
source: "cf-actuals" | "estimate" per figure.
[Admin][API] Usage & Spending — aggregate ledger, platform-paid vs BYOK split, cost + AI kill switch
Why
The core operator concern. The platform currently pays for internal AI (
PLATFORM_AI_ENABLED="true": embeddings + summaries + translation fallback) and that spend is invisible in-app. We need cross-user usage + a clear platform-paid vs BYOK cost split, and an operator control to cap/kill platform spend.Scope
GET /v1/admin/usage(behindrequireAdmin): aggregate theai_usageledger across ALL users — group by provider, model,kind, user, agent/instance, and day. Return token totals andcost_microstotals with a time window (?days=).ai_usagerow as platform-paid (internalkinds run onenv.AIwhenPLATFORM_AI_ENABLED— embeddings/summary/translation) vs BYOK (user's Anthropic/CF key). Since CF platform calls currently ledger atcost_micros≈0, add estimated cost via a neuron/token price table so platform spend is a real number, not zero. Document the estimate.GET /v1/admin/spending— top-line: total platform-paid $ (30d), total BYOK tokens (informational), top spenders, top models, trend series.GET/PUT /v1/admin/settings/platform-aito read and toggle the effectivePLATFORM_AI_ENABLEDbehavior at runtime (e.g. aplatform_settingsrow overriding the env default), so the operator can turn platform-paid AI off without a redeploy. Off → embeddings/summaries/translation no-op (BYOK-only); note the known side effect that RAG goes dark until a BYOK-embedding path exists (track separately).Acceptance criteria
/v1/admin/usageaggregates across all users with provider/model/kind/user/day breakdowns and correct totals./v1/admin/spendingreturns 30-day platform spend, top spenders/models, trend.Relevant code
workers/api/src/lib/usage.ts(recordUsage→ai_usage, hascost_micros), migration0048_ai_usage.sqlagent-do.ts:72,routes/instances-translation.ts,wrangler.tomlPLATFORM_AI_ENABLEDroutes/usage.tsReferences
/v1/analytics/admin/platform). PAS has no usage/cost admin — this closes that gap for PAGS.Part of the PAGS Admin Portal epic. Depends on Foundation. Directly addresses the "platform should not pay for AI" concern.
Best-practice hardening (2026-08-01)
ai_usage.cost_microsis ~0 for CF and the token→price table is a derived guess. Pull the real platform AI cost from Cloudflare's GraphQL Analytics / billing API (Workers AI usage) as the authoritative number for the "platform spend" headline. Keep the token estimate only for BYOK (where we genuinely can't see the provider bill) and label estimates as estimates in the API + UI so the two are never conflated.source: "cf-actuals" | "estimate"per figure.