---
module: analytics.adoons
title: Adoons Insights
tab: Claude
order: 40
audience: dev
---

> **Internal · agent reference.** Grounded in `hiveops-analytics` @ 2026-07-01. Only confirmed facts below.

## Ownership
- **Service:** `hiveops-analytics` owns this feature end-to-end (controller, service, entity, table).
- **Port:** 8089 (prod host 8012). **NGINX:** `/api/analytics/`. External base e.g. `https://api.bcos.cloud/analytics/api/analytics/insights`.
- **No AI generation in-repo.** Insights exist only because someone `POST`ed them. Do not assume an in-service generator/Kafka consumer creates them.

## Endpoints (`InsightController`, base `/api/analytics/insights`)
| Method | Path | Auth |
|--------|------|------|
| GET | `/api/analytics/insights` | authenticated (paged) |
| GET | `/api/analytics/insights/active` | authenticated |
| POST | `/api/analytics/insights` | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` |
| PATCH | `/api/analytics/insights/{id}/acknowledge` | authenticated |
| DELETE | `/api/analytics/insights/active` | authenticated (ack all) |

Frontend calls: `getActiveInsights` → `GET /insights/active`; `acknowledgeInsight(id)` → `PATCH /insights/{id}/acknowledge`.

## Auth
- Header only: `Authorization: Bearer <jwt>`. No cookie fallback.
- Open paths: `/actuator/health`, `/api/public/**`. Everything else authenticated.
- Principal: `AnalyticsPrincipal(userId, email, role, institutionKey)`.

## Data
- **DB:** `hiveiq_analytics` (Postgres prod on 10.10.10.188 via ProxyJump; H2 dev).
- **Table:** `ai_insights` (cols: `id, category, severity, title, body, snapshot_id, published_at, expires_at, acknowledged, acknowledged_at, acknowledged_by_user_id`).
- **FK:** `ai_insights.snapshot_id → analytics_snapshots.id` (nullable).
- **Enums:** `category ∈ {ANOMALY,TREND,RECOMMENDATION,SUMMARY}`; `severity ∈ {INFO,WARNING,CRITICAL}`.

## POST body (`PublishInsightRequest`)
```json
{"category":"TREND","severity":"WARNING","title":"...","body":"...","snapshotId":123,"expiresAt":"2026-08-01T00:00:00Z"}
```
- `category,severity,title,body` = `@NotBlank`. `snapshotId,expiresAt` optional.
- Omit `snapshotId` → links latest snapshot (or null if none).

## Kafka
- Insight path uses **no Kafka**. (Analytics produces `analytics.snapshot-ready`; consumes `hiveops.incidents.created/updated/resolved`, `journal.file-parsed` — snapshots only, NOT insights.)

## Active-list rule
- `/insights/active` = `acknowledged=false AND (expires_at IS NULL OR expires_at > now)`, ORDER BY `published_at DESC`.
- Nightly purge: cron `0 0 3 * * *` deletes acknowledged rows older than 30d.

## Gotchas
- Severity enum is `INFO/WARNING/CRITICAL`, but frontend colour map keys `CRITICAL/HIGH/MEDIUM/LOW/INFO` → `WARNING` renders default blue; `HIGH/MEDIUM/LOW` can never come from this backend.
- Customer `overview.md` lists CRITICAL/HIGH/MEDIUM/LOW/INFO — does NOT match backend enum. Trust the enum.
- Frontend `api.ts` sets NO Authorization header / no axios interceptor, yet backend requires JWT on all `/api/analytics/**`. Token injection is not in frontend code (mechanism unverified — likely NGINX/host shell).
- `AdoonsTab.dismiss()` swallows PATCH errors silently → failed acknowledge = card silently stays.
- `acknowledge` / `acknowledgeAll` are NOT role-gated — any authenticated user can dismiss fleet-global insights.
- POST with bad enum string → 400 (`valueOf` throws). PATCH unknown id → 404.

## Do NOT
- Do NOT expect insights to auto-appear — none generate inside analytics; only `POST` creates them.
- Do NOT use `Authorization` bearer header assumptions for the frontend; it doesn't set one.
- Do NOT invent per-institution scoping — insights are fleet-global, no `institution_key` on the table.
- Do NOT route insight writes through Kafka; it's plain REST+JPA.
- Do NOT publish `HIGH/MEDIUM/LOW` severities — not valid enum values.
- Do NOT put insight reads under `/api/public/**` — they require auth by design.

Cross-links: [technical.analytics], [platform.service-ownership].
