CRM

CRM in sidanclaw is people, companies, and deals: the durable graph of who the team talks to, where the relationship stands, and what it's worth. First-party so the brain reads and writes contacts the same way it reads memories: same database, no translation layer. Attio / HubSpot are sync targets, not the primary surface.

01

Three tables, one schema

Contacts (people), companies (organizations), deals (opportunities). Frozen v1: eight columns max per table plus timestamps. No custom fields, no priority, no description. Cross-workspace links are blocked by triggers, not just FKs. Tags are TEXT[] with a GIN index; external_ref JSONB is a free-form passthrough for synced rows.

02

Deal stages

Six values, locked: lead, qualified, proposal, negotiation, won, lost. Adding a stage requires a migration plus a tool-description update plus an analytics taxonomy update, by design. No custom pipelines in v1.

03

Amounts and dates

amount is NUMERIC(15,2) decimal dollars (USD-equivalent), not cents. Users type "50000", not "5000000". close_date is a calendar DATE; "Q3 close" isn't a wall-clock instant. Currency is implicit USD; a currency_code column ships when multi-currency does.

04

Chat tools

Every assistant with the crm capability gets the full CRUD surface plus advanceDealStage as the canonical stage-transition verb (separate from updateDeal, which won't accept stage). No delete tools in v1: soft-delete is advanceDealStage(id, 'lost') and field-nulling.

saveContact / getContact / listContacts / updateContact  ·  saveCompany / getCompany / listCompanies / updateCompany  ·  saveDeal / getDeal / listDeals / updateDeal / advanceDealStage
05

Relationships across the brain

Every CRM row is an entity in the underlying graph. Save a memory about a contact, link a task to a deal, or open an explicit edge via the universal links param: the entity rollup (getEntity) returns the contact, every memory anchored to it, and the deals it's attached to in one call.