Skip to main content

Context

The smallest feature in the module and the one people use most. A note is where the knowledge that does not fit any field goes: “prefers to be called after 6pm”, “complained about the March delivery”, “owns the franchise on Avenida Providencia”. It is also the least predictable personal data in the platform, because it is free text a human wrote about an identified person. That drives two rules: notes are audit-logged like any state change, and they are deleted on erasure with no anonymized remnant — unlike a ledger row, a note has no accounting value that outlives the person it describes.

Scope (normative)

  • crm.notes on a contact, with a typed author.
  • Rich text, bounded in length.
  • Pinning a note to the top of a contact.
  • Editing with a preserved history of edits.
  • Full-text search across a tenant’s notes.

Non-scope (normative)

  • Attachments — F1b. Files bring virus scanning, storage quotas and their own erasure path.
  • Mentions and internal notifications — F1b.
  • Notes on anything other than a contact. If an activity needs a note, it has a description field.
  • Templates or canned responses. That is a ticketing feature.

Behaviour (normative)

  1. A note belongs to exactly one contact and records a typed author — user, api_key or system. A note without an author is a defect: the value is knowing who observed it.
  2. Content is rich text, sanitized on write. Stored HTML is escaped on render; a note is not a script injection vector into a support agent’s browser.
  3. Length is bounded at a documented maximum. A note is not a document store.
  4. Editing preserves the previous versions, visible to anyone who can read the note. A note quietly rewritten after an incident is the failure this prevents.
  5. Deletion is a soft delete and audit-logged. Erasure of the contact is a hard delete of the note and its history (DEC-J3).
  6. Pinning is per contact with a bounded number of pins, so pinning stays meaningful.
  7. Search is scoped to the tenant and to the reader’s permissions.
  8. Every mutation writes to core.audit_log.

Data (normative)

API (normative)

Events (normative)

crm.note.created. Not offered as an outgoing webhook: note content is the least predictable personal data we hold, and pushing it to an arbitrary endpoint by default is not a decision to make on a tenant’s behalf.

Acceptance criteria (normative)

  1. A note records a typed author, and creation without one is impossible.
  2. HTML in content is sanitized on write and escaped on render — proven with an injection fixture.
  3. Editing preserves the previous version and both are readable.
  4. Soft delete hides the note and keeps it recoverable; erasure of the contact removes it entirely, including its edit history.
  5. Search returns only the tenant’s own notes, and only those the reader may see.
  6. Pin count is bounded per contact.
  7. Every mutation writes an audit row.
  8. Negative: no note references a contact of another tenant, and none is reachable without crm.notes.read.

Execution

Single slice, synchronous command. Full-text search uses Postgres tsvector scoped by tenant.

Open questions

Changelog

Delivery record

Not implemented yet.