Proprietary data · CRM · Activities

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.

Table crm_activitiesScope org + ownerAttach company · contact · dealDrives tasks · reminders
Tasks & timelineactivities APIcrm_activities
00

System design

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.

Tasks & timelineVue · activities storeactivities subdomainFastAPI · bulk-createcrm_activitiesPostgres · interaction logCelery + Redisreminders / beatEnvoy sequencessource_app=envoyDiscovery agentsagent_discovery rows
01

Data flow — birth of a row

How 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.

new activitycanonical payloadlinked rowcrm links setowner setready to writerow writtenid mintedtimestamps freshaccount currentservedloop closes1Createthree entry pathsmanual · envoy · agent2Attachpolymorphic linkscompany · contact · deal3Assignowner vs authorassigned_to · created_by4Insert + RLSowner-scoped writeRLS · org write5Bump last-activitycompany + persontrigger6Surfacetasks + remindersdue_date · status
02

Schema

The 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.

7 columns
  • primary key

    Primary key.

  • foreign key

    Tenant scope. Enforced by RLS on every row.

    → organizations
  • 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.

keyprimary keyforeign keyenumtext
03

Relationships

Activities 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_* · org-scopedtenant

crm_activities is polymorphic — any combination of company, contact and deal, plus an Envoy sequence and owner.

04

RLS, triggers & reminders

The 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.

Ownership RLS

Insert, update and delete are scoped to the row creator OR the assigned user — narrower than the plain org-membership read. Superadmins bypass.

Polymorphic attach

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.

Last-activity trigger

update_crm_last_activity_dates() keeps the linked company and person last-interaction timestamp fresh. handle_updated_at() maintains updated_at.

Reminders

due_date + status drive the task queue. A Celery task sends reminders off the request path so notification work never blocks a write.

Denormalized sequence

sequence_name and step_id are hydrated from outreach_sequences for fast display, so the timeline renders without a join (ENG-597).

RLSStorage only

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.

AgencyCore · CRM · activitiesac-frontend · ac-python-api · ac-backend