> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Activities

> After this ships, "call this customer back on Thursday" is something the system remembers instead of a note on somebody's desk.

## Context

An activity is the smallest useful unit of follow-up: something to do, about a customer, by a date,
by someone.

`crm.activity_types` is one of **only two tenant-extensible catalogs in the platform** (DEC-B4), and
the reason is exactly the rule the data standard states: the values are the tenant's own business
vocabulary. A gym logs "class no-show", a clinic logs "appointment reminder", a distributor logs
"quarterly visit". Those are not ours to enumerate. What stays ours is the state machine — an
activity is pending, completed or cancelled, and a tenant cannot add a fourth state, because the
state drives logic.

## Scope *(normative)*

* `crm.activities` on a contact: type, description, due date, assignee, state.
* `crm.activity_types`, parametric and **tenant-extensible**, with `is_system` seeds.
* Assignment to a user in the same organization.
* Completion with an outcome note.
* Overdue detection and listing.

## Non-scope *(normative)*

* Reminders and notifications — F1b, through `messaging` and the `activity.created` event.
* Recurring activities. A recurrence engine is a scheduler, and we have one for campaigns; reusing
  it here needs its own design.
* SLAs, queues and escalation. That is ticketing, and it is a non-goal of the module.
* Calendar sync — not planned.

## Behaviour *(normative)*

1. An activity belongs to one contact, has one type, and is `pending`, `completed` or `cancelled`.
   The state set is **not tenant-extensible**: it drives overdue logic and reporting.
2. `activity_types` **is** tenant-extensible. Tenant rows are `is_system = false`, scoped to that
   tenant, and a tenant can never modify or deactivate a system row.
3. The assignee must be an **active member of the same organization**. Assigning to someone who has
   left is rejected, which is also a small nudge to keep memberships current.
4. A due date is optional. An activity without one is a reminder without urgency, which is a real
   thing people write down.
5. Completion records **who and when**, plus an optional outcome. Reopening a completed activity is
   not allowed — the correct action is a new one, so the history stays honest.
6. Overdue is **derived**, never stored. A stored flag needs a job to maintain and will be wrong
   between runs.
7. Deactivating a membership leaves that person's activities assigned to them and **surfaces them for
   reassignment**, rather than silently reassigning or orphaning them.
8. Every mutation writes to `core.audit_log`.

## Data *(normative)*

| Table                | Key invariants                                                                                                                                                                                                                                               |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `crm.activities`     | `tenant_id`, FK `contact_id`; `type_code` FK; `description`; `due_at` nullable; `assignee_user_id` nullable; `state` pending\|completed\|cancelled; `completed_by`, `completed_at`, `outcome`; index on (`tenant_id`, `assignee_user_id`, `state`, `due_at`) |
| `crm.activity_types` | parametric; **tenant-extensible** (DEC-B4); `is_system` seeds `call`, `email`, `meeting`, `task`, `visit`, `follow_up`                                                                                                                                       |

## API *(normative)*

| Endpoint                                | Class      | Permission                          | Budget    |
| --------------------------------------- | ---------- | ----------------------------------- | --------- |
| `GET /v1/crm/activities`                | Management | `crm.activities.read`               | p95 \<1 s |
| `POST /v1/crm/contacts/{id}/activities` | Management | `crm.activities.create`             | p95 \<1 s |
| `PATCH /v1/crm/activities/{id}`         | Management | `crm.activities.update`             | p95 \<1 s |
| `POST /v1/crm/activities/{id}/complete` | Management | `crm.activities.complete`           | p95 \<1 s |
| `GET/POST /v1/crm/activity-types`       | Management | `crm.activity_types.{read\|create}` | p95 \<1 s |

## Events *(normative)*

`crm.activity.created` and `crm.activity.completed`. `created` is what F1b's reminder feature will
consume — the event exists now so adding reminders needs no change here.

## Acceptance criteria *(normative)*

1. A tenant-created activity type is visible only to that tenant.
2. A tenant cannot modify or deactivate an `is_system` type.
3. Assigning to a user outside the organization, or to a deactivated membership, is rejected.
4. Overdue is derived at read time and is correct across timezones.
5. A completed activity cannot be reopened; the API says so with a typed error.
6. Deactivating a membership surfaces that person's open activities for reassignment.
7. Completion records who, when and the outcome.
8. **Negative:** no endpoint adds a fourth activity state.

## Execution

Single slice, synchronous command. Nothing asynchronous — reminders in F1b will consume the event.

## Open questions

| # | Question                                                                                      | Decides | By              |
| - | --------------------------------------------------------------------------------------------- | ------- | --------------- |
| 1 | Can an activity be assigned to any user, or only to the account owner? (PRD open question 2)  | Daniel  | before approval |
| 2 | Should an activity be attachable to a loyalty event, so "follow up on this redemption" links? | Daniel  | before approval |

## Changelog

| Version | Date       | Change        | Why | Author                 |
| ------- | ---------- | ------------- | --- | ---------------------- |
| 0.1.0   | 2026-08-17 | Initial draft | —   | daniel + claude-opus-5 |

## Delivery record

*Not implemented yet.*
