Tracking
Status: 🟡 Em fluxo (porta simplificada do legacy mandir-events) Code: backend/app/modules/tracking UI: frontend/src/app/(admin)/[slug]/admin/tracking (auditoria) Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: todos os módulos (todos emitem eventos)
1. Identidade
O que faz (uma frase)
Sink puro de eventos do sistema — recebe emit_event(tenant_id, event_name, properties) de qualquer módulo do Suite e persiste em tracking_event (audit log unificado), com idempotência via event_id UNIQUE.
Por que existe (negócio)
Audit log central permite:
- Reconstruir trajetória de qualquer entidade (contato, deal, campanha) cronologicamente.
- Compliance LGPD — provar quando dado foi acessado/modificado.
- Debug pós-fato — "o que aconteceu naquele dia?" sem stack traces.
- Analytics agregada — Intelligence consome tracking_event pra calcular padrões.
Por que existe (técnico)
Substitui o legacy mandir-events (Drishti) que era serviço separado com TimescaleDB. Versão monolítica simplificada:
- Sem TimescaleDB (Postgres normal).
- Sem Redis dedup (UNIQUE constraint cobre).
- Sem API keys internas (mesmo módulo, sem auth interna).
Status atual
- Em prod — recebe eventos de todos os módulos.
Próxima mudança: emit pro mandir-events externo (futuro analytics cross-tenant); hot tier em Redis pra queries últimas 24h.
2. Cases de uso reais
Case 1: Audit de envio de campanha
Email module envia: emit_event("email.campaign.dispatch.done", properties={campaign_id, sent_count}). Tracking persiste. Felipe consulta /admin/tracking?event_name=email.campaign.dispatch.done → linha do tempo.
Case 2: Intelligence consome eventos pra L1
Intelligence query tracking_event WHERE event_name LIKE 'whatsapp.message.%' AND occurred_at > now() - 30d → calcula engagement_score_30d por contato.
3. Oportunidades de negócio
- Compliance dashboard: filtragem LGPD-aware (eventos por contato, exportável).
- Anomaly detection: spike em
payment.failed→ alert. - Custom event subscriptions: webhook outbound pro tenant (notify quando event X).
4. Arquitetura interna
Arquivos
models.py— 1 tabela.routes.py— 3 endpoints.service.py—ingest,list_events.
Tasks
Sem Celery. Ingest síncrono.
Adapter
Sem adapter externo. core.events.emit_event(tenant_id, event_name, properties) chama tracking.service.ingest direto.
5. Tabela (1)
tracking_event
| Coluna chave | Notas |
|---|---|
tenant_id | idx |
event_id | UUID idempotency key (UNIQUE com tenant) |
event_name | ex: whatsapp.message.received, email.campaign.dispatch.done |
event_category | Inferred via event_name.split(".")[0] |
source | suite (default) / evolution / zapier / etc. |
contact_id | Nullable |
session_id | String (web sessão) |
properties | JSONB livre |
occurred_at | Default now |
created_at |
Índices: (tenant, occurred_at), (tenant, event_name, occurred_at), (tenant, contact_id, occurred_at). UNIQUE (tenant_id, event_id).
Relacionamentos
- Lê de: todos os módulos (via
emit_event). - Escreve em: intelligence consome (não FK, query).
6. API / Endpoints (3)
| Método | Rota | Auth | O que faz |
|---|---|---|---|
| POST | /api/tracking/events | Público (rate-limit 600/min) | Ingest event, 202 Accepted, fire-and-forget |
| GET | /api/tracking/events | session | List (filtros: event_name, event_category, source, contact_id, since, until) |
| GET | /api/tracking/events/{id} | session | Single event |
7. Service
ingest(event)— upsert idempotente. Se duplicate (event_id já existe pra tenant), retorna None silenciosamente (HTTP 202 anyway).list_events(filters, pagination)— query com facetas.
8. Configuração
| Var | Propósito |
|---|---|
| Rate limit POST | 600/min via slowapi |
Sem kill switch (eventos sempre persistem).
9. Operações
Sem operações ativas. Sink passivo.
Troubleshooting
Sintoma: Eventos chegando duplicados
Causa: caller não passou event_id → cada POST é novo registro.
Fix: sempre passar event_id (UUID gerado client-side).
10. Limitações e débitos técnicos
| # | Item |
|---|---|
| 1 | Sem Redis hot tier — query > 24h é lenta sem index custom |
| 2 | Sem retention policy — cresce indefinido |
| 3 | event_id opcional — sem dedup se omitido |
| 4 | Sem API keys — qualquer um com X-Tenant-ID pode ingest (relies em rate limit) |
11. Histórico
- Em prod desde Suite v2. Porta simplificada de Drishti (mandir-events legacy).