Report
The report module generates AI-powered daily business summaries. Organizations configure which data sources to include (calls, messages, tasks, contacts), customize the AI prompt, test it in real time, and receive scheduled reports delivered as in-app notifications with email.
How It Works
Section titled “How It Works”Hourly cron job runs: → For each org with reports enabled: → Check if local hour matches send_at_hour → Gather yesterday's data (calls, messages, tasks, contacts) → Build structured prompt with real data → Send to LLM via OpenRouter → get markdown summary → Persist as report_instance → Notify recipients (in-app + email)Data Model
Section titled “Data Model”report Table
Section titled “report Table”Stores per-organization report configuration.
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
organization_id | UUID | Owning organization |
enabled | bool | Whether daily reports are active |
send_at_hour | i16 | Hour of day (0–23) in org’s local timezone |
prompt | Option<String> | Custom AI prompt (falls back to default) |
include_calls | bool | Include call data |
include_messages | bool | Include message data |
include_tasks | bool | Include task data |
include_contacts | bool | Include contact data |
report_recipient Table
Section titled “report_recipient Table”Maps reports to users who receive them.
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
report_id | UUID | FK → report.id |
user_id | UUID | FK → user receiving the report |
A B-tree index on report_id speeds up recipient lookups.
report_instance Table
Section titled “report_instance Table”Persists each generated report for viewing in the UI.
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
report_id | UUID | FK → report.id |
organization_id | UUID | Owning organization |
title | String | e.g. “Daily Report - Acme Corp” |
body | String | Markdown content from AI |
period_start | DateTimeUtc | Start of reporting window |
period_end | DateTimeUtc | End of reporting window |
data_sources | JSON | ReportDataSources snapshot with entity counts |
created_at | DateTimeUtc | When generated |
Shared Types
Section titled “Shared Types”ReportDataSources
Section titled “ReportDataSources”Snapshot of which entities were included and their counts:
pub struct ReportDataSources { pub calls: Option<u32>, pub messages: Option<u32>, pub tasks: Option<u32>, pub contacts: Option<u32>,}ReportInstance
Section titled “ReportInstance”Full report for detail views:
pub struct ReportInstance { pub id: String, pub title: String, pub body: String, // Markdown content pub period_start: String, pub period_end: String, pub data_sources: ReportDataSources, pub created_at: String,}ReportInstanceSummary
Section titled “ReportInstanceSummary”List view variant (no body field) used in ReportInstanceListResponse.
AI Report Generation
Section titled “AI Report Generation”The generate_daily_report service (generate_daily_report_service.rs) handles multi-entity data gathering and LLM generation.
Process:
- Compute yesterday’s time window in the org’s local timezone, convert to UTC
- For each enabled data source, query the relevant tables:
- Calls — filters to substantive calls only (has transcription OR duration ≥ 30s), truncates transcriptions to 500 chars
- Messages — groups by contact with inbound/outbound counts per contact
- Tasks — completed or created tasks in the window
- Contacts — new or updated contacts
- Build a structured markdown context with all gathered data
- Send to LLM via OpenRouter using the
GenerateReportAI area (model:gemini-3.1-pro-preview) - Return the markdown body and data source counts, or
Noneif no substantive activity
Key type:
pub struct ReportGenerationConfig { pub organization_id: Uuid, pub custom_prompt: Option<String>, pub tz: chrono_tz::Tz, pub include_calls: bool, pub include_messages: bool, pub include_tasks: bool, pub include_contacts: bool,}Default prompt (when org has no custom prompt):
Summarize yesterday’s business activity. Highlight key themes, important conversations, action items, and any contacts that need follow-up. Cross-reference insights across calls, messages, tasks, and contacts where relevant.
Timezone-Aware Scheduling
Section titled “Timezone-Aware Scheduling”The send_timezone_aware_daily_reports service runs on an hourly cron job.
Flow:
- Fetch all enabled reports
- Batch-load org timezones, recipients, and org names (avoids N+1 queries)
- For each report, convert current UTC time to the org’s local timezone
- If
local_now.hour() == report.send_at_hour, generate and dispatch - Persist the result as a
report_instance - Send notifications to all configured recipients
Test Prompt Feature
Section titled “Test Prompt Feature”Users can test their prompt configuration without saving, using yesterday’s real data.
| Route | Method | Description |
|---|---|---|
POST /api/reports/test | POST | Fire-and-forget test generation |
Input: TestReportPromptInput
pub struct TestReportPromptInput { pub prompt: Option<String>, pub include_calls: bool, pub include_messages: bool, pub include_tasks: bool, pub include_contacts: bool,}The endpoint spawns a tokio::spawn background task and returns immediately. Results arrive via WebSocket.
WebSocket Events
Section titled “WebSocket Events”| Event | Payload | Description |
|---|---|---|
report.test.completed | ReportTestCompletedEvent | Report generated successfully |
report.test.failed | ReportTestFailedEvent | Generation error |
report.test.no_data | null | No activity data for yesterday |
Client Hook
Section titled “Client Hook”use_realtime_report_test() subscribes to WebSocket events and exposes a Signal<ReportTestState>:
pub enum ReportTestState { Idle, Generating, Completed(ReportTestCompletedEvent), Failed(String), NoData,}UI Component
Section titled “UI Component”ReportTestPanel renders inline below the prompt textarea with five states:
- Idle — hidden
- Generating — structured skeleton loader with theme-aware colors
- Completed — markdown preview with period date and data source badges
- Failed — error message with retry button
- NoData — empty state message
The “Test prompt” button sends the current unsaved form state. After the first test, it changes to “Test again”.
Report APIs
Section titled “Report APIs”| Route | Method | Description |
|---|---|---|
GET /api/reports?page&page_size | GET | Paginated list of report instances |
GET /api/reports/{id} | GET | Single report instance detail |
POST /api/reports/test | POST | Trigger test report generation |
List endpoint defaults to page 0, page size 25 (max 100). Results are org-scoped and sorted by created_at descending.
Settings UI
Section titled “Settings UI”The Reporting tab in Settings includes:
- Enable toggle — turns daily reports on/off
- Send time picker — hour-of-day select with 12h labels
- Data source checkboxes — calls, messages, tasks, contacts grid
- Custom prompt textarea — override the default AI prompt
- Test prompt button — generates a preview using yesterday’s data
- Recipient picker — select org members (validated against org membership)
Report Views
Section titled “Report Views”- Report list (
/reports) —ReportCardcomponents showing title, date, data source badges - Report detail (
/reports/{id}) — full markdown body with metadata
Both views are accessible via the Reports sidebar navigation entry.
Notifications
Section titled “Notifications”Reports use NotificationCategory::Report and NotificationEntity::Report(instance_id). Clicking a report notification navigates to the persisted report instance.
Email delivery uses a dedicated report_notification_email_template with branded styling.
Module Structure
Section titled “Module Structure”src/mods/report/├── api/│ ├── get_report_instances_api.rs # Paginated list│ ├── get_report_instance_api.rs # Single detail│ └── test_report_prompt_api.rs # Fire-and-forget test├── components/│ ├── report_card_component.rs # List card│ └── report_test_panel_component.rs # Inline test results├── hooks/│ └── use_realtime_report_test.rs # WebSocket event listener├── services/│ ├── generate_daily_report_service.rs # Multi-entity AI generation│ └── send_timezone_aware_daily_reports_service.rs # Hourly cron dispatch├── types/│ ├── report_data_sources_type.rs # Entity count snapshot│ ├── report_event_type.rs # WebSocket event constants│ ├── report_instance_type.rs # Instance + summary + list types│ └── report_test_type.rs # Test input, events, state machine└── views/ ├── report_list_view.rs # /reports └── report_details_view.rs # /reports/{id}Related Modules
Section titled “Related Modules”- Notification — report delivery and email templates
- Settings — report configuration UI
- Call — call data source for reports
- Architecture: Real-Time WebSocket — test prompt event delivery
- Architecture: AI Services — OpenRouter integration for generation
- Architecture: Timezone Support — org timezone resolution