Documents the exact Prisma query per dashboard figure, the one new durable table this feature needs (ErrorCodeLookup — 014's own equivalent metric is process-lifetime, unusable for a historical report), the "no data -> null, never NaN" convention, and why the AI dashboard's confidence distribution deliberately uses the system-default threshold rather than resolving a per-diagnosis policy (AIDiagnosis has no reliable FK back to which policy applied). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2.3 KiB
Contract: Reporting API
All four routes require a valid staff session with role ADMIN (requireRole('ADMIN')), the
same gate every admin-only surface uses since 010-identity-auth. All return the standard
envelope: { success: true, data: <shape>, meta: null } on success, { success: false, error: {code, message, details} } on failure — no change to this codebase's existing response
convention.
GET /admin/reports/management
Query: from?, to? (ISO dates).
200: ManagementDashboard (data-model.md).
400 VALIDATION_ERROR: from is after to.
401/403: missing/invalid session, or a non-ADMIN role.
GET /admin/reports/product/:externalProductId
Path: externalProductId — the SaaS-facing product identifier (same convention every other
admin product-scoped route already uses, e.g. GET /admin/products/:externalProductId/knowledge
from 004-product-knowledge).
Query: from?, to?.
200: ProductDashboard.
404 NOT_FOUND: no product with that externalProductId (FR-006 — never an empty-but-200
response for an unknown product).
400 VALIDATION_ERROR: from is after to.
GET /admin/reports/support
Query: from?, to? (applies only to the performance figures — workload/SLA-risk/breached
are always current, per data-model.md's SupportDashboard.generatedAt).
200: SupportDashboard.
GET /admin/reports/ai
Query: from?, to?.
200: AiDashboard.
Guarantees
- Every rate/average field is
number | null—nullmeans no qualifying data existed in the requested range (FR-007). A consumer must never seeNaNor a silently-substituted0for "no data." - Every count field is a plain
number, always present,0is a legitimate, meaningful value for a count (distinct from thenull-for-no-data rule above, which applies only to rates/averages). from/toin every response echo the resolved range actually used (including the default, when omitted) — a caller never has to separately know what "the default" was.- No route in this contract mutates any data — a repeated identical request returns the same shape (though not necessarily identical figures, since the underlying data can change between requests) with no side effect.