# 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: , 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 1. Every rate/average field is `number | null` — `null` means no qualifying data existed in the requested range (FR-007). A consumer must never see `NaN` or a silently-substituted `0` for "no data." 2. Every count field is a plain `number`, always present, `0` is a legitimate, meaningful value for a count (distinct from the `null`-for-no-data rule above, which applies only to rates/averages). 3. `from`/`to` in every response echo the *resolved* range actually used (including the default, when omitted) — a caller never has to separately know what "the default" was. 4. 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.