Skip to content

Customer Dashboard — CRM ​

The CRM group is the customer's sales workspace: Leads, Contacts, Companies, Opportunities, Tasks, Segments, and Agentic Automation. It covers the full pipeline from a raw lead through qualification, deals, and automated follow-up.

Screenshots ​

LeadsLeads — list viewLeads — pipeline / filtersLeads — detail viewLeads — communication timeline

Contacts, Companies, Opportunities, Segments, Agentic AutomationContactsCompaniesOpportunitiesSegmentsAgentic Automation

Architecture Overview ​

Every CRM route runs the same four-layer gate (confirmed in crmLead.routes.ts header):

  1. verifyJwt — identity.
  2. requireFeatureAccess(leads/segments module) + requireSubSection("crm.*") — the org granted this section.
  3. requirePermission("crm","view") + requireGrant("crm","sub.*") — the team member has the RBAC resource + sub-section grant.
  4. resolveCrmLeadScope → requireLeadPermission(view|create|delete|export) — tenant scope + per-action permission.

The four lead actions (view / create / delete / export) are each granted explicitly — holding crm_leads alone grants no action. Manager-only writes additionally pass requireCrmOrgManager.

How It Works (Step-by-Step) ​

1. Leads (/leads, /my-queue, /leads/:id) ​

  • List view (Leads/1.png): a table with top-bar controls — List / Pipeline toggle, Search leads, Filter (saved filter count), Colors (temperature color rules), Columns (visible/total custom fields, e.g. 8/92), and a fullscreen expand. KPI chips show Total / Today's / Hot / Pipeline value / Unattended / Assigned / Unassigned. Each row has inline Status and Temperature dropdowns, Source, Assigned To (+ Add user), and row actions: Call, WhatsApp, Assign, and a ⋮ overflow menu.
    • Call places a native/AI call (see CRM Call Integration); WhatsApp opens the WhatsApp composer; Assign reassigns the lead (PATCH /api/leads/:id/assign, manager-gated).
  • Lead detail (Leads/4.png, Leads/6.png): header with Edit, Convert to Contact, a status browser, and Prev/Next. Summary cards (Engagement, Interactions, Success Rate, Stage), Activity by channel (Calls / WhatsApp / SMS / Email / Notes counts), Key Facts (status + tags), Details & attribution (status, assigned to, source, created, last contact). Tabs: Overview / Marketing / Communication / Deals & Tasks. The Communication tab shows a Conversation Timeline (Human Call / WhatsApp / Activity / Meeting Note), and the Activity tab offers Set Reminder and Schedule Call.
  • My Queue (/my-queue): the priority-ranked "just call" surface for team members (agent-only; hidden for owners and for members without the sub.my_queue grant).
  • Import (/leads/import/*): upload a CSV → map columns → execute. Gated by create. Writes a CrmLeadImport session, then CrmLead rows.
  • Settings & custom fields: GET/PATCH /api/leads/settings and /api/leads/custom-fields (manager-gated) define the form schema stored in CrmCustomField / CrmOrgSettings.

2. Contacts (/contacts) ​

  • Leads promoted to qualified contacts (the isQualified set). Gated by the crm.contacts sub-section. The History/call-centric contact list is served by contact.routes.ts (GET /contacts, call summary, analysis, bookmark, feedback); the CRM-style contact list reuses the leads controller in "contacts mode".

3. Companies (/companies, /companies/:id) ​

  • B2B accounts. Reads are view; writes are org-manager gated because a company is shared org data that many members' contacts hang off. Hidden for B2C customers (the businessType filter in CustomerLayout).

4. Opportunities (/opportunities) ​

  • Deals; many per contact. List + Pipeline (GET /api/opportunities/pipeline), create/update, and close/reopen. Writes are not manager-gated (per-contact record), only tenant-scoped.

5. Tasks (/tasks) ​

  • The follow-up work queue across contacts. CRUD plus /:id/complete and /:id/reopen. Backed by CrmTask; the bell-popup reminders come from CrmReminder via crmReminder.routes.ts (/api/reminders, /api/reminders/due).

6. Segments (/segments) ​

  • Saved lead filters. Gated by the dedicated crm_segments module (sold independently of Leads). Supports count/preview, members add/remove, share, and per-segment ad analytics. A segment can launch a campaign via Create campaign from segment (requires both voice_campaign and crm_segments, plus the widget.segments.run_campaign grant — see campaign.routes.ts).

7. Agentic Automation (/agentic-automation) ​

  • Org-level auto-callback / follow-up configuration. GET /api/agentic-settings is viewable by anyone who can view leads; PUT is org-manager only because enabling auto-callback places real outbound calls and emails for the whole customer. Stored in CrmAgenticSettings. See Lead Call Agentic Analysis.

File Reference ​

FileRoleKey Functions
routes/crmLead.routes.tsAuthoritative leads endpoint + permission table—
controllers/crmLead.controller.tsLead list/detail/CRUD/assign/bulklistLeads, getLeadById, assignLead, bulkDeleteLeads
controllers/crmCustomField.controller.tsLead form schema + custom fieldsgetLeadFormSchema, createCustomField
controllers/contact.controller.tsCall-centric contact/history viewlistContacts, getContactAnalysis
controllers/crmCompany.controller.tsB2B accountslistCompanies, createCompany
controllers/crmOpportunity.controller.tsDeals + pipelinegetPipeline, closeOpportunity
controllers/crmTask.controller.tsTaskslistTasks, completeTask
controllers/crmReminder.controller.tsLead reminders (bell popup)listDueReminders, snoozeReminder
controllers/crmSegment.controller.tsSaved segmentspreviewSegmentLeads, shareSegment
controllers/crmAgenticSettings.controller.tsAgentic auto-callback configgetAgenticSettings, updateAgenticSettings

Data Model ​

CollectionWhy it existsKey fields
CrmLeadThe core lead record (pipeline)customerUID, status, temperature, assignedToUID, custom fields
CrmLeadIdentity / CrmLeadJourneyDedup identity + cross-channel journeyphone, email, journey events
CrmLeadImportCSV import session + mappingmapping, status, rows
ContactQualified contact / call recordcustomerUID, isQualified, call fields
CrmCompanyB2B accountname, customerUID
CrmOpportunityDeal/stagestage, amount, contactUID
CrmTask / CrmReminderTo-dos + personal nudgesdueAt, userUID, status
CrmSegment / CrmSegmentMemberSaved filter + resolved membershipfilter, leadUID
CrmCustomField / CrmOrgSettingsLead form schemafield defs, businessType
CrmAgenticSettingsAuto-callback/follow-up configtrigger events, enabled flags

API Endpoints ​

Full authoritative table lives in each route file's header. Representative endpoints:

List leads ​

  • Method: GET
  • Path: /api/leads
  • Auth: verifyJwt → requireFeatureAccess(leads) → resolveCrmLeadScope → requireLeadPermission("view")
  • Response: 200 paginated leads for the resolved tenant/visibility.

Create / update / delete a lead ​

  • POST /api/leads (create), PATCH /api/leads/:id (create), DELETE /api/leads/:id (delete).

Assign a lead (manager) ​

  • Method: PATCH
  • Path: /api/leads/:id/assign
  • Auth: lead gate + create + requireCrmOrgManager.

Export leads ​

  • Method: GET
  • Path: /api/leads/export
  • Auth: lead gate + requireLeadPermission("export"). See CSV Export System.

Opportunity pipeline ​

  • Method: GET
  • Path: /api/opportunities/pipeline
  • Auth: crm.opportunities sub-section + view.

Agentic settings ​

  • GET /api/agentic-settings (view), PUT /api/agentic-settings (view + requireCrmOrgManager).

External Integrations ​

How to Test (End-to-End) ​

  1. Grant the customer crm_leads (+ view/create). Open /leads.
  2. Create a lead → it appears in the list; its stage shows under Dashboard → Lead stages.
  3. Open the lead detail; use Set Reminder → confirm it surfaces under Tasks and the bell popup when due.
  4. Convert to Contact → the lead appears in /contacts.
  5. Create an Opportunity on the contact → confirm it shows in the Pipeline.
  6. Save a Segment filter, then Create campaign from segment (needs voice_campaign + crm_segments).
  7. As a team member without the delete permission, confirm DELETE /api/leads/:id returns 403.

Troubleshooting ​

SymptomCauseFix
CRM group hiddenNo CRM feature (crm_leads/crm_segments)Grant a CRM entitlement
A CRM child hidden (e.g. Contacts)Sub-section (crm.contacts) not grantedGrant the sub-section to the org
Companies missingCustomer is B2C (businessType)B2C hides Companies by design
DELETE/export returns 403Action not granted to the team memberGrant crm_leads.delete / crm_leads.export
Assign/settings return 403Member isn't an org managerThose writes are manager-only (requireCrmOrgManager)
Import rows not createdImport gated by create; mapping incompleteGrant create; finish column mapping before execute

Last updated: