Skip to content

Reports & QA

Describe the reporting endpoints, scheduled report delivery, CSV export, and the QA (quality assurance) scorecard/evaluation system.

The control plane exposes five read-only reports under /api/v1/reports, all requiring supervisor or above, all accepting an optional ?from/?to RFC3339 window and ?format=csv, and all honoring a platform_admin’s ?tenant_id override:

Report Returns
campaigns Per-campaign funnel: attempts, leads dialed, human/machine, sales, abandoned, plus contact/conversion/abandonment rates
agents Per-agent calls, talk time, time-in-state, occupancy, and QA averages
call-volume CDR volume + telephony quality (ASR, ACD, daily trend, top hangup causes)
compliance DNC totals/additions, abandonment-vs-cap breaches, calling-hours coverage
dispositions Tenant-wide disposition breakdown by code

Key metric formulas (all percentages rounded to 2 decimals, 0 when the denominator is 0):

contact_rate = human / attempts * 100
conversion_rate = sales / human * 100 # denominator is live humans
abandonment_rate = abandoned / attempts * 100 # breach when > campaign cap
ASR = answered / total_calls * 100
occupancy = (busy_secs + wrap_secs) / (available_secs + busy_secs + wrap_secs) * 100

Time-in-state and occupancy come from agent_state_intervals (completed intervals only, windowed on left_at), so an agent’s current ongoing state is excluded until it ends.

Scheduled reports (/api/v1/report-schedules, admin+): a schedule emails a report digest weekly or monthly. Fields: report, cadence, day_of_week (0–6, weekly) or day_of_month (1–28, monthly), hour_utc, recipients[], enabled. A 1-minute worker claims due schedules with FOR UPDATE SKIP LOCKED (at-most-once across replicas) and emails four headline metrics plus a link to /reports — no CSV is attached.

CSV export: append ?format=csv to any report, or use GET /api/v1/cdrs/export for raw CDRs. Cells are guarded against formula-injection (a leading = + - @ is prefixed with ').

QA scoring: a qa_scorecards row has questions (JSONB: scale_1_5 or yes_no, each weighted). An evaluator scores a recorded call via POST /api/v1/qa/evaluations; the score is computed server-side (clients can’t inject one):

score = earned / total_weight * 100 # 0–100, unanswered questions score 0

One evaluation per (call_id, evaluator) — re-submitting updates in place. GET /api/v1/qa/agent-scores rolls evaluations up per agent.

  • QA endpoints scope to the caller’s own tenant and do not accept a ?tenant_id override (unlike the reports).
  • The agent report’s QA columns count only submitted evaluations, while /qa/agent-scores includes drafts — the two QA aggregates can differ.
  • The agent report’s state column is the current live state, not windowed.