Menuabrir
Em fluxoAtualizado em 14 de mai. de 2026, 00:06

Este módulo depende de

0

Nenhuma dependência outbound.

Módulos que dependem deste

3
  • attributioncontact_touchpoint referencia tracking_event via event_id
  • emailWebhook eventos consumidos e persistidos pra auditoria
  • programsEmite eventos de conclusão (program.lesson.completed, journey.day.unlocked)

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

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 chaveNotas
tenant_ididx
event_idUUID idempotency key (UNIQUE com tenant)
event_nameex: whatsapp.message.received, email.campaign.dispatch.done
event_categoryInferred via event_name.split(".")[0]
sourcesuite (default) / evolution / zapier / etc.
contact_idNullable
session_idString (web sessão)
propertiesJSONB livre
occurred_atDefault 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étodoRotaAuthO que faz
POST/api/tracking/eventsPúblico (rate-limit 600/min)Ingest event, 202 Accepted, fire-and-forget
GET/api/tracking/eventssessionList (filtros: event_name, event_category, source, contact_id, since, until)
GET/api/tracking/events/{id}sessionSingle 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

VarPropósito
Rate limit POST600/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
1Sem Redis hot tier — query > 24h é lenta sem index custom
2Sem retention policy — cresce indefinido
3event_id opcional — sem dedup se omitido
4Sem 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).