Menuabrir
EstávelAtualizado em 14 de mai. de 2026, 00:06

Este módulo depende de

2
  • attributionResolve UTM via attach_attribution() em create_contact
  • intelligenceRecebe purchase_intent_level de run_relationship_health

Módulos que dependem deste

12
  • businesscontact_id em price_inquiry, objection, appointment; contexto do cliente
  • instagramResolve/cria contato de DM inbound
  • attributionPopula crm_contact.attribution_* em attach_attribution()
  • linksLink type scoring aplicado em CRM scoring rules
  • councilcross_module.search_contacts, read_contact_360 para contexto
  • membersSync automático de lifecycle_stage via _sync_crm_lifecycle()
  • whatsappResolve nome e contexto de contatos; cria contato no inbound
  • emailResolve contatos por email; atualiza status em email_send via contact_id
  • formsCria contato automaticamente quando form é submetido
  • agentsread_contact_360, read_objections, read_contact_attributes para contexto
  • surveysOpcional crm_contact_id vinculado em submissions identificadas
  • intelligenceLê crm_contact, crm_deal, tags para L1 scores e churn risk

CRM

Status: 🟢 Estável (mega-módulo, em produção desde Suite v2) Code: backend/app/modules/crm UI: frontend/src/app/(admin)/[slug]/admin/crm Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: fonte canônica de pessoa do Suite — usado por: whatsapp (lookup), email (recipients), members (sync), attribution (UTM resolution), intelligence (L1 input), council/agents (tools)


1. Identidade

O que faz (uma frase)

Gestão completa de relacionamento com contatos (pessoas), deals (oportunidades por pipeline), activities (tarefas humanas), accounts (CNPJs B2B), tags + scoring + custom fields + webhooks + API keys — fonte única de verdade de "pessoa" no Mandir, consumida por todos os outros módulos.

Por que existe (negócio)

CRM é o coração operacional. Sem ele:

  • Email não tem destinatário consolidado.
  • Agentes IA não sabem quem é quem.
  • Attribution não tem onde guardar UTM.
  • Intelligence não tem crm_contact_id pra agregar.

Com CRM integrado:

  • Contato chega (form/whatsapp/import) → CRM resolve identidade (email/phone/IG) → resto do Suite usa o mesmo crm_contact_id.
  • Deals em pipeline customizável (drag-and-drop kanban) → forecast.
  • Tags + scoring rules calculam crm_contact.score em tempo real (50+ event_types seeded).
  • Custom fields permitem personalização sem migration.
  • Accounts (B2B) hierárquicos (matriz → filiais).

Por que existe (técnico)

  • Multi-tenant com soft-delete (deleted_at).
  • Sem FK cross-módulo — relacionamento via service layer (UUIDs soltos).
  • Scoring async — eventos (crm_contact_event) atualizam score em background.
  • Identity resolution via fuzzy matching (memória [[brain-architecture]]).
  • Webhooks outbound (per-tenant) pra integrações externas.

Status atual

Em prod desde Suite v2. Funcionalidades maduras. Próxima mudança: integração mais profunda com intelligence.contact_attribute (L1) + identity resolution via embeddings.


2. Cases de uso reais

Case 1: Lead via Instagram DM

Hari (agente) recebe DM IG → instagram.service.resolve_or_create_contact_from_ig(ig_user_id, username, name) → CRM cria/resolve contato → vincula instagram_thread.contact_id → próximas DMs já com identidade.

Case 2: Felipe move deal no kanban

UI /admin/crm/deals → arrasta card "Mentoria Premium R$ 2997" de "Proposta" pra "Negociação" → PATCH /deals/{id}/advancecrm.deal.stage_changed event → intelligence atualiza padrão de conversão.

Case 3: Custom field "professor responsável" por tenant

Felipe cria crm_custom_field_definition(entity=contact, name="Professor", field_type=select, options=["Felipe", "Vivian"]). Adiciona valor pra cada contato. read_contact_360 retorna o valor pro Conselho.

Case 4: Bulk apply tag a 100 contatos

POST /api/crm/contacts/bulk body {action: "apply_tag", tag_id: "vip", contact_ids: [...]} → Service aplica em batch → emite eventos.


3. Oportunidades de negócio

  • CRM standalone: vender "Mandir CRM" como subset do Suite — competidor direto de HubSpot Free / Pipedrive Essential com diferencial IA + LGPD nativa.
  • Vertical pipelines: templates pré-configurados por vertical (e-commerce, infoprodutor, consultoria).
  • B2B com accounts hierárquicos: matriz/filial é diferencial sobre CRMs SMB.
  • AI brief / next-action: já existem endpoints (/contacts/{id}/ai-brief, /deals/{id}/ai-next-action) — empacotar como "AI Sales Assistant" premium.

4. Arquitetura interna

Arquivos

Tasks Celery

TaskScheduleO que faz
crm.sla_checkBeatNotifica deals com stage.sla_days vencido (cria crm_notification)

5. Tabelas (17)

Contatos / Tags / Eventos (4)

crm_contact

Coluna chaveNotas
tenant_ididx
email / phone / instagram_user_idLookup
full_name / first_name / last_name
lifecycle_stagesubscriber / lead / customer / vendor
source / entry_doorOrigem
utm_* (5 cols)Snapshot original
attribution_* (cadeia completa)Sprint 1.A
score / lead_score / engagement_scoreAsync via eventos
purchase_intent_level / sentiment_trend / conversation_health_scorePreenchido por intelligence.run_relationship_health_task
assigned_toUUID staff
email_opt_in / optout_email / optout_whatsapp / confirmed_atLGPD
is_active / is_blocked / blocked_reason
is_merged_intoSoft-merge target
short_idHumano-readable
metadata_JSONB livre
deleted_atSoft-delete

Constraint: UNIQUE(tenant_id, email). Índice (tenant_id, lifecycle_stage).

crm_tag + crm_contact_tag

m:n com applied_at. UNIQUE (contact_id, tag_id). CASCADE delete.

crm_contact_event

Fato do sistema (audit + scoring trigger).

ColunaNotas
event_typeemail.opened, billing.payment.received, etc.
source_module
score_deltaAplicado em crm_contact.score async
metadata_JSONB
occurred_at

Deals / Pipeline (3)

  • crm_deal_stage: pipeline customizável, category (awareness/consideration/decision/negotiation), is_terminal (won/lost), sla_days.
  • crm_deal: title, status (open/won/lost), value/currency, probability, contact_id, stage_id, account_id, billing_account_id, seller_entity_id, lost_reason, next_action.
  • crm_activity: task/call/meeting/note vinculado a contact + deal.

Scoring + Custom Fields (3)

  • crm_scoring_rule: event_type, delta, label. Seeded com 50+ defaults.
  • crm_custom_field_definition: entity (contact/deal), field_type (text/number/boolean/date/select/multiselect/url), options.
  • crm_custom_field_value: value_text/number/bool/date/json. UNIQUE (field_id, contact_id) ou (field_id, deal_id).

Notifications + Webhooks + API Keys + Templates + Goals (5)

  • crm_notification: in-app per recipient.
  • crm_webhook: outbound (eventos JSONB array).
  • crm_api_key: key_prefix + key_hash UNIQUE, scopes JSONB.
  • crm_email_template: body_text/html/design.
  • crm_goal: OKR per assignee, metric, target_value, period.

Accounts (B2B) (2)

  • crm_account: name, legal_name, cnpj/cpf, account_type (holding/matriz/filial/autonomo), parent_account_id (self-FK), industry. UNIQUE (tenant_id, cnpj).
  • crm_contact_account: m:n contact↔account com role (decisor/comprador/financeiro/tecnico/influenciador), is_primary.

Relacionamentos cross-módulo

DireçãoOutro móduloComo
↗ Lêattributionattach_attribution() em create_contact (lazy import)
↘ EscrevemembersMembers sync _sync_crm_lifecycle() (status → lifecycle_stage)
↘ Escreveintelligencerun_relationship_health popula purchase_intent_level, sentiment_trend, conversation_health_score
↗ Lê eventosemail, whatsapp, billing, forms, diaryVia crm_contact_event + scoring.get_delta()
↘ Emite eventsintelligence, billing (dunning), todos10+ event_types

6. API / Endpoints (85+)

Prefixo /api/crm. Lista compacta:

Contatos (12)

GET /contacts (search + filters), POST /contacts, GET /contacts/stats, POST /contacts/bulk (patch massivo, max 1000), GET /contacts/export.csv, POST /contacts/import (CSV, on_duplicate=skip|update), GET /contacts/{id}, PATCH /contacts/{id}, DELETE /contacts/{id} (soft), GET /contacts/{id}/context (drawer 360), GET /contacts/{id}/timeline (activities + events), GET /contacts/{id}/custom-fields.

Tags (5)

POST/DELETE /contacts/{id}/tags/{tag_id}, GET/POST/PATCH/DELETE /tags.

Deals (14)

GET /deals (filters), POST/PATCH/DELETE, POST /deals/bulk, GET /deals/export.csv, POST /deals/{id}/advance, /set-category, /reopen, /clone.

Stages + Pipelines (5)

GET/POST/PATCH/DELETE /deal-stages, GET /pipelines.

Activities (5)

GET/POST/PATCH/DELETE /activities, GET /activities/{id}.

Custom fields (4)

GET/POST /custom-fields, POST /custom-field-values, GET /{contacts|deals}/{id}/custom-fields.

Outros

  • Email templates (5).
  • Goals (4) — current_value + progress_pct calculados em tempo real.
  • Notifications (3).
  • Webhooks (5).
  • API keys (3).
  • Accounts (5).
  • Contact↔Account links (3).
  • Reports (6) — funnel, revenue forecast, activity summary, won/lost, pipeline velocity, rep performance.
  • Global search (1) — /search?q=&limit=.
  • AI assistants (2) — /contacts/{id}/ai-brief, /deals/{id}/ai-next-action.

7. Eventos emitidos / consumidos

Emite

  • crm.contact.{created,updated,deleted}
  • crm.deal.{created,stage_changed,won,lost,deleted}
  • crm.activity.{created,updated,deleted}

Consome (via crm_contact_event)

50+ event_types seeded — email.opened, email.clicked, whatsapp.message.received, billing.payment.received, forms.submitted, etc.


8. Configuração

Env vars

VarDefaultPropósito
MANDIR_CRM_IMPORT_BATCH_SIZE100CSV import chunk
MANDIR_CRM_GLOBAL_SEARCH_LIMIT50Limite query global

9. Operações + Troubleshooting

Como criar tenant novo

Backend scoring.seed_for_tenant(tenant_id) popula 50+ scoring rules default. UI /admin/crm/settings mostra todas, permite ajustar deltas.

Sintoma: Score não atualizando

Causa: crm_contact_event não está sendo criado por algum módulo. Diagnóstico: SELECT event_type, COUNT(*) FROM crm_contact_event WHERE tenant_id=... GROUP BY event_type; Fix: verificar se módulo emit_event correto.

Sintoma: Bulk operation timeout

Causa: > 1000 registros. Fix: chunk no client.


10. Limitações e débitos técnicos

#Item
1Soft-delete não cascade automático
2Terminal stages bloqueiam PATCH
3Scoring async
4Custom fields duais (contact XOR deal)
5Account hierarchy 1 nível
6Attribution snapshot
7Sem identity resolution via embeddings

11. Histórico

Em prod desde Suite v2 (2026-05-04). Funcionalidades adicionadas iterativamente: accounts B2B, custom fields, scoring rules, AI brief.