The activities table
crm_activities is the interaction + task log of the CRM. One row per call, meeting, task, note or stage change, polymorphically attachable to a company, contact and/or deal. This page traces it top-down: where it is served from, how a row is born and read, then its full schema, relationships and rules.
System design
interactive Start from the top. The tasks and timeline views never touch storage directly — they talk to the activities subdomain, the only thing that reads or writes crm_activities, and hands reminders to Celery. Click a node to trace what it connects to.
Data flow — birth of a row
interactiveHow an activity lands and is read back, as a closed loop. Three producers converge on one service, which attaches it polymorphically, assigns an owner, writes under ownership RLS, bumps the linked last-activity, then serves the task list — and the read closes back to the start. Click a stage to follow it.
Schema
interactiveThe row groups into the activity core, its timing, the polymorphic links, and the extras. Pick a group; colour marks each column's kind, and clicking a column expands its category and any table it references. Everything is org-scoped; the attach columns are all optional.
What the activity is, the tenant scope, and its status + priority.
- primary key
Primary key.
- enum
call · meeting · email [DEPRECATED] · task · note · stage_change · agent_discovery.
- text
Short label for the activity.
- text
Freeform body / notes.
- text
pending · completed · cancelled.
- text
low · medium · high · urgent.
Relationships
interactiveActivities are the polymorphic child — the row points outward at every other table. Hover or click a table to trace its link and see its key columns.
crm_activities is polymorphic — any combination of company, contact and deal, plus an Envoy sequence and owner.
RLS, triggers & reminders
modelThe database is storage only — no business logic. What it does enforce: ownership isolation, the polymorphic attach, the triggers that keep derived state honest, and the queue that drives reminders.
Insert, update and delete are scoped to the row creator OR the assigned user — narrower than the plain org-membership read. Superadmins bypass.
company_id, contact_id and deal_id are each optional. An activity can hang off all three CRM links, one of them, or none — there is no exclusivity constraint.
update_crm_last_activity_dates() keeps the linked company and person last-interaction timestamp fresh. handle_updated_at() maintains updated_at.
due_date + status drive the task queue. A Celery task sends reminders off the request path so notification work never blocks a write.
sequence_name and step_id are hydrated from outreach_sequences for fast display, so the timeline renders without a join (ENG-597).
crm_activities holds no rules of its own — tables, indexes, RLS and triggers only. The interaction log is append-mostly; derived state like the company and person last-activity is kept honest by triggers, not by application writes, so the timeline can never drift from the log.