Entities, relationships, custom columns, widgets, and webhooks, in one page.
TL;DR — Customermates tracks five entity types, lets each one carry user-defined fields, links them to each other through typed relationships, and fires webhooks whenever any of that changes.
| Entity | What it represents | Typical fields |
|---|---|---|
| Contact | A person | firstName, lastName, notes |
| Organization | A company | name, notes |
| Deal | A sales opportunity | name, totalValue, totalQuantity |
| Service | A product or offering attached to a deal | name, amount |
| Task | A todo item | name, assignees |
Each entity has id, createdAt, updatedAt, and supports markdown notes. Everything else is either a relationship to another entity or a custom column value.
Relationships are typed and many-to-many. A contact belongs to zero or more organizations and zero or more deals. A deal belongs to zero or more contacts, organizations, and services (with quantities) plus assignees.
| From → To | Example |
|---|---|
| Contact ↔ Organization | "Max works at Initech" |
| Contact ↔ Deal | "Max is a stakeholder on the Q2 contract" |
| Organization ↔ Deal | "The Q2 contract is with Initech" |
| Deal ↔ Service (with quantity) | "5 hours consulting + 1 setup fee" |
| Deal / Service / Task ↔ User | "Assigned to Julia" |
Tasks only link to users. That is intentional: tasks are lightweight.
Default schema covers the basics. For anything else, add a custom column.
Eight types:
| Type | Use for |
|---|---|
| Plain | Free-text values |
| Date | Calendar dates (renewal, next touch) |
| DateTime | Timestamps |
| Currency | Money amounts, stored with an ISO currency code |
| Single-select | Dropdown from a fixed list (stage, priority) |
| Link | One or more URLs |
| One or more email addresses | |
| Phone | One or more phone numbers |
Custom columns are first-class in filters, widgets, and the MCP surface.
Widgets are dashboard charts driven by live data. You pick an entity, a group-by axis (plain field or custom column), an aggregation (count, deal value, deal quantity), and a display type (bar / doughnut / radar). Filters narrow the data set.
A widget re-renders whenever underlying records change. No refresh button.
Every write emits a domain event: contact.created, deal.updated, task.deleted, and so on. You subscribe by URL and event list. Each delivery includes the changed record plus a changes map showing what moved from what to what, so subscribers can act on diffs without polling.
See Webhook events for the full payload catalog.
Notes are per-record markdown. They render through a Tiptap editor in the UI but are plain markdown everywhere else. You can replace them (update_entity_notes) or append to them without losing existing content (append_entity_notes).
Users are your teammates. Roles control what they can read and write. The company is the tenant — every record you see lives inside one company, and cross-tenant access is not possible.