Saved CRM reports
Org-scoped definitions, recomputed with the caller’s current crm:read permissions. Create needs crm:create and crm.enabled. Definitions are shared with CRM readers in the same org; saving never grants data access.
This feature ships dark: platform crm.reports.enabled defaults false. Org entitlement crm.reports defaults true and is an independent kill switch. API keys also need crm.api.enabled and the CRM key family. Keys count org-visible activity only: their creator’s private timeline activity stays private. The current MCP transport requires a key with write scope; the report tools themselves write nothing. Read-only REST keys may run reports. Lifecycle runs need crm.v2.lifecycles. No promotion or flag changes are part of this implementation.
| Definition | Meaning |
|---|---|
{kind:"pipeline",pipelineId} | Deals created in the window, grouped by current stage and currency; count, exact decimal-string valueCents, valued count, won/lost facts. Also cumulative funnelCount/funnelValueCents, cumulativeConversionRate and nextStepConversionRate, by currency: later recorded-open deals and all recorded-Won facts contribute to earlier open steps, even after stage flags change; Lost stages have null funnel metrics. Empty stages are included. Won is the terminal step regardless of stage ordering. This is sequential inference from current placement, not historical visits. Unlike HighLevel’s last-status-change window, this report uses the explicitly documented created-in-window cohort. |
{kind:"activity",groupBy:"type"|"person"|"period",period:"day"|"week"|"month"} | Counts by activity kind, owner UUID (null is unassigned), or UTC day/ISO week/month. Only full-detail entries count: org-visible, owned by viewer or authored by viewer. Busy-only blocks from other people reveal no activity details. |
{kind:"lifecycle",lifecycleId} | Instances created in window with live CRM subjects. Distinct reach per pinned version/stage divided by cohort size; terminal flag; visits and average seconds per visit, including open visits observed through to. Re-entry is a new instance; revisits count once in reach and separately in duration. SLA reminder events are ignored. |
{kind:"source",recordKind:"person"|"company"|"deal"} | Live records created in window counted by source enum. No source references. |
POST /v1/orgs/{orgId}/crm/reports takes {name,definition} (maximum 100 definitions/org).
GET .../reports?limit=25&offset=0 lists definitions with nextOffset.
POST .../reports/{reportId}/run takes {from,to,limit?,offset?} and returns {kind,from,to,totalRows,rows,nextOffset}.
GET .../reports/{reportId}/export.csv?from=...&to=...&limit=...&offset=... exports that same aggregate page. x-report-total-rows and optional x-report-next-offset describe pagination; exports are no-store.
MCP read tools: crm_reports_list, crm_report_run. Their outputs are untrusted CRM data.
Windows are timezone-qualified ISO instants, [from,to), positive and at most 366 days. Pages hold 1–100 aggregate rows (default 25), offset 0–10000. Every input query reads at most 10001 rows and refuses more than 10000 (422 crm_report_too_large); every statement has a 3-second database timeout (422 crm_report_timeout). Never silently truncated totals. Sorted aggregate pages come from a consistent read transaction; separate page requests recompute and may reflect subsequent edits.
Exports contain aggregates, stage labels/ids and activity owner ids already readable by the viewer, never contact names, addresses, emails, phones, titles, timeline bodies, source references or history reasons. All cells are quoted with doubled quote escapes. Text carries a leading apostrophe for spreadsheet formula safety, which is part of the exported value; use JSON for exact text. Spreadsheet applications differ; do not strip that prefix before opening the CSV.
Validation: apps/api/test/crm-reports.test.ts; additive migration 10771_crm_saved_reports.sql. No customer data was used.
References: API reference, MCP tools, operations runbook.
- API reference: tag
crmin/v1/openapi.json. - Runbook: CRM design, feature switches.
Web: CRM > Deals > CRM reports (shown only after the authorized report API succeeds). Save pipeline value/progression or source reports, choose a UTC created-in-window cohort, run and page results, or export the displayed page. A reader can run saved reports; creating one requires CRM create permission. Source api includes lead-form records; original source is preserved on later form updates. Pipeline progression is inferred from current positions and outcomes and is not historical stage conversion. Existing platform and org report flags remain in force.