Skip to content

User Insight Dashboard ​

Aggregates every call a phone number has ever made across an organization's campaigns and agents into one unified view, so admins can see the full history of a single caller instead of digging through campaigns one by one.


Table of Contents ​

  1. Architecture Overview
  2. How It Works
  3. File Reference
  4. Data Model
  5. API Endpoints
  6. Feature Gating
  7. How to Test
  8. Troubleshooting

Architecture Overview ​

Clicking a row drills into a per-phone call history (/users/:phone/calls), and a "Generate Report" button aggregates that history into a summary (/users/:phone/report).


How It Works ​

  1. Table view Admin opens /admin/user-insights. The page fetches GET /users, which groups every Contact by originalPhone (the caller's number) within the admin's org and returns one row per unique number with total calls, associated campaigns/agents, and first/last call dates.
  2. Search Typing 3+ characters into the search box re-fetches the list with a case-insensitive substring filter on the phone number.
  3. Detail view Clicking a row fetches GET /users/:phone/calls, returning every call for that number (newest first), each with the recording, transcription, and analysis for that specific call.
  4. Report view Clicking "Generate Report" fetches GET /users/:phone/report, which aggregates all of that number's calls into one summary: totals, inbound/outbound split, per-campaign and per-agent breakdowns, and a combined list of all analysis results and transcriptions.
  5. Every query is scoped to the requesting admin's orgUID and excludes isTestCall: true contacts, so test data and other tenants' data never leak in.

File Reference ​

FileRoleKey Functions
backend/client_backend/src/types/userInsight.tsShared TypeScript interfaces for summary/call/report shapesUserInsightSummary, UserInsightCall, UserInsightReport
backend/client_backend/src/repositories/userInsight.repository.tsMongoDB aggregation pipelines, org + test-call scopingaggregateUniqueUsers(), findUserCalls(), aggregateUserReport()
backend/client_backend/src/controllers/userInsight.controller.tsRequest validation, pagination parsing, response shapinglistUniqueUsers(), getUserCalls(), getUserReport()
backend/client_backend/src/routes/userInsight.routes.tsRoute + middleware chain definitionsmounts 3 GET routes
app/src/types/userInsight.tsFrontend types mirroring backend response envelopesPaginatedUsersResponse, PaginatedCallsResponse, UserReportResponse
app/src/services/userInsight.service.tsAxios client for the 3 endpointslistUniqueUsers(), getUserCalls(), getUserReport()
app/src/components/features/UserInsightTable.tsxPaginated table of unique phone numbers
app/src/components/features/UserInsightDetail.tsxCall history cards (recording, transcription, analysis)
app/src/components/features/UserInsightReport.tsxAggregated report view
app/src/pages/admin/UserInsightsPage.tsxOrchestrates table/detail/report views + feature gate

Data Model ​

No new collections were added. The feature reads from three existing collections:

originalPhone is the caller's number this is the field everything is grouped by, since one phone number can call into multiple campaigns/agents over time.


API Endpoints ​

All three endpoints share this middleware chain:

verifyJwt → requireRole("admin") → requireOrg → requireFeatureAccess(...FEATURE_MAP.tokenAnalytics)

FEATURE_MAP.tokenAnalytics maps to the admin_analytics feature flag the same gate used by Token Consumption Analytics. No new feature flag was created.

List Unique Users ​

  • Method: GET
  • Path: /api/v1/user-insights/users
  • Query: page (default 1), limit (default 20, max 100), search (optional, partial phone match)
  • Response 200:
    json
    {
      "success": true,
      "data": {
        "users": [
          {
            "phone": "+15551234567",
            "totalCalls": 5,
            "campaigns": [{ "campaignUID": "camp_1", "campaignName": "Spring Promo" }],
            "agents": [{ "agentId": "agent_1", "agentName": "Agent Smith" }],
            "firstCallDate": "2024-01-01T00:00:00.000Z",
            "lastCallDate": "2024-03-01T00:00:00.000Z"
          }
        ],
        "pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
      }
    }

Get User Call History ​

  • Method: GET
  • Path: /api/v1/user-insights/users/:phone/calls
  • Query: page (default 1), limit (default 20, max 50)
  • Response 200: { success, data: { phone, calls: UserInsightCall[], pagination } } sorted by callStartedAt descending.

Get User Insight Report ​

  • Method: GET
  • Path: /api/v1/user-insights/users/:phone/report
  • Response 200: { success, data: UserInsightReport } with totalCalls, callDirectionDistribution, campaignBreakdown, agentBreakdown, analysisResults, transcriptionSegments.
  • Response 404: no contacts found for that phone in this org.

Shared Error Responses ​

StatusReason
400Invalid page/limit (non-numeric or < 1)
403No org context, non-admin role, or admin_analytics not enabled
404Report requested for a phone with no contacts
500Unexpected server/DB error

Feature Gating ​

Reuses the existing admin_analytics flag see Analytics.md → Feature Gating System for the full deny-by-default logic. On the frontend, both the sidebar link and the /admin/user-insights route are wrapped with:

tsx
const { hasFeature } = useFeature();
if (!hasFeature(FEATURES.ADMIN_ANALYTICS)) return null;

How to Test (End-to-End) ​

Prerequisites: an org with admin_analytics in its plan features, an admin-role user, and at least one Contact document with isTestCall: false.

  1. Log in as an admin whose org has admin_analytics enabled.
  2. Navigate to Admin → User Insights in the sidebar (hidden if the feature isn't enabled).
  3. Confirm the table loads unique phone numbers with correct call counts, campaign/agent names, and dates.
  4. Type 3+ digits of a known phone number into search confirm the list filters down.
  5. Click a row confirm the detail view shows that number's calls sorted newest-first, with recording/transcription/analysis sections (or placeholders when missing).
  6. Click Generate Report confirm totals, inbound/outbound counts, and campaign/agent breakdowns add up correctly.
  7. In MongoDB, verify: no contact with isTestCall: true appears anywhere, and no data from a different orgUID appears in any view.

Troubleshooting ​

SymptomCauseFix
Sidebar link / page missingadmin_analytics not in org's plan featuresEnable the feature for the org (same toggle used for Token Analytics)
403 on all endpointsUser role isn't admin, or no orgUID on the requestConfirm JWT role and that requireOrg resolved an org
404 on reportNo non-test contacts exist for that phone in this orgExpected behavior not a bug
Row shows raw ID instead of a nameCampaign/Agent was deleted after the call was madeExpected fallback per requirement 4.6 campaignName/agentName is null
Slow list/report loadAggregation exceeded maxTimeMS: 5000Check indexes on Contact.orgUID + Contact.originalPhone; narrow the date range or search filter

Last updated: