Analytics (GA4)
Status: 🟢 Estável (Hub.3 deployado) Code: backend/app/modules/marketing/plugins/ga4 UI: frontend/src/app/(admin)/[slug]/admin/marketing (seção GA4) Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: marketing (Hub framework), attribution (campaign bridge), tenant_router
1. Identidade
O que faz (uma frase)
Plugin GA4 do Mandir Hub — observa propriedade Google Analytics 4 do tenant, persiste agregado diário, breakdown por source/medium/campaign e eventos em tabelas L0, e expõe 4 tools pro Conselho consumir sem hitar a API.
Por que existe (negócio)
GA4 é a fonte canônica de tráfego do site. Sem este módulo:
- Felipe abre painel GA4 toda vez que quer entender quanto tráfego vem do YouTube.
- Conselho não consegue cruzar tráfego com revenue.
Hub.3 traz o histórico no DB + tools no Conselho. CEO pergunta "quanto tráfego do Instagram converteu?" → Conselheiro chama read_ga4_top_sources + read_ga4_conversion_funnel → resposta com números.
Status atual
- Hub.3 deployado em 2026-05-13 (commit dentro da sessão
hub234, alembic 0103). - Service Account ativo:
mais-consciente-analyticsno projeto GCPmais-consciente-mandir.
2. Cases de uso reais
Case 1: CEO pergunta "como está GA4 últimos 30 dias?"
Fluxo: CEO chama read_ga4_traffic_summary(window_days=30) + read_ga4_top_sources + read_ga4_event_breakdown em paralelo → cross_module devolve dados de ga4_* → CEO sintetiza: "840 sessões, 757 users, 3.2% conversion, top source = direct (35%)".
Case 2: Detecção de queda
Beat task marketing.run_observe(provider=ga4) roda diariamente → se sessions_today < 0.5 × avg_7d → emit anomaly event → intelligence sub-agent anomaly cria intelligence_insight(severity=warning).
3. Oportunidades de negócio
- Multi-property dashboard: clientes que têm 5 sites separados precisam consolidar GA4 — produto pra agências.
- GA4 → CRM attribution: match de session_id com CRM contact (depois que GA4 implementar Identity Mapping). Cross-channel attribution premium.
- Anomaly alerts standalone: queda inesperada → notifica WhatsApp → produto reativo.
Riscos: GA4 Data API tem 24h delay (inevitável). Quota 1MM requests/dia/property — alto pra single-tenant; pode ser problema multi-tenant 100+.
4. Arquitetura interna
Arquivos
| Arquivo | Propósito |
|---|---|
__init__.py | Imports + register_plugin(GA4Plugin()) |
plugin.py | Classe GA4Plugin. Implementa observe() (resto no-op) |
client.py | Wrapper BetaAnalyticsDataClient (Service Account auth) |
models.py | 4 tabelas L0 |
sync.py | sync_property_snapshot, sync_traffic_summary, sync_top_sources, sync_events |
attribution_bridge.py | sync_attribution_campaigns cria attribution_campaign(kind=ad_paid, provider=ga4) |
Tasks
Sem Celery tasks dedicadas — observe acionado por marketing.run_observe(provider=ga4).
5. Tabelas (4)
ga4_property
Snapshot da propriedade.
| Coluna chave | Notas |
|---|---|
tenant_id | |
property_id | GA4 numeric ID |
measurement_id | G-XXXXX (web stream) |
label | Humano-readable |
ga4_metric_daily
Agregado diário.
| Coluna chave | Notas |
|---|---|
property_id / date | Granularity por dia |
sessions / active_users / new_users / page_views / conversions | INT |
total_revenue | DECIMAL |
engagement_rate / avg_session_duration_s | FLOAT |
ga4_source_daily
Breakdown source/medium/campaign × date.
| Coluna chave | Notas |
|---|---|
property_id / date | |
source / medium / campaign / channel_group | Dimensions |
sessions / active_users / conversions / revenue | Métricas |
ga4_event_daily
Eventos por dia × property.
| Coluna chave | Notas |
|---|---|
event_name / event_count / event_value |
Relacionamentos cross-módulo
| Direção | Outro módulo | Como |
|---|---|---|
| ↘ Escreve | attribution | sync_attribution_campaigns cria attribution_campaign por novo campaign GA4 |
| ↘ Tools | council | 4 tools: read_ga4_traffic_summary, read_ga4_top_sources, read_ga4_conversion_funnel, read_ga4_event_breakdown |
6. API / Endpoints
GA4 não tem endpoints REST direto — observa via marketing.run_observe. UI consome via:
| Método | Rota | O que faz |
|---|---|---|
| GET | /api/marketing/ga4/summary | Sessions, users, pageviews, revenue, engagement_rate (window_days) |
| GET | /api/marketing/ga4/sources | Breakdown source/medium/campaign |
| GET | /api/marketing/ga4/events | Event names com counts |
7. Configuração
Credencial
| Campo | Valor |
|---|---|
provider | ga4 |
account_external_id | property_id (ex: 290167437) |
auth_kind | service_account |
secret_envelope | {service_account: {...JSON parseado...}} |
metadata | {measurement_id: "G-XXXXX", service_account_email: "..."} |
scope | ["https://www.googleapis.com/auth/analytics.readonly"] |
Service Account JSON setup
- GCP Console → IAM → Service Accounts → criar
mais-consciente-analytics. - Download JSON key.
- GA4 Console → Admin → Property Access Management → adicionar email da SA com role Viewer.
- UI Mandir
/admin/marketing/credentials→ paste JSON → backend validaservice_accountfield e cifra envelope.
Importante: Service Account JSON ≠ OAuth Client JSON. Memória [[oauth-client-vs-service-account-json]]:
- SA tem
"type":"service_account"no topo. - OAuth Client tem
{"installed":{...}}. - Validar no paste antes de mandar pro backend.
8. Operações
Como conectar property nova
- Criar SA no GCP (uma vez por projeto).
- Adicionar SA email como Viewer no GA4.
- UI Mandir → "Conectar GA4" → paste JSON + property_id + measurement_id.
- Trigger sync inicial.
Troubleshooting
Sintoma: Sync retorna permission_denied
Causa: SA email não foi adicionada na property. Fix: GA4 Console → Property Access Management → adicionar.
Sintoma: (not set) em dimensões
Causa: GA4 não conseguiu inferir source/medium (tráfego direto sem UTM).
Tratamento: (not set) → NULL no DB.
9. Métricas + observabilidade
| Logger key | Quando emite |
|---|---|
marketing.ga4.sync.start | Observe iniciou |
marketing.ga4.sync.done | OK + rows_ingested |
marketing.ga4.sync.failed | Erro |
marketing.ga4.attribution_bridge.created | Novo campaign auto-criado |
10. Limitações e débitos técnicos
| # | Item |
|---|---|
| 1 | 24h delay nativo — GA4 Data API não dá real-time |
| 2 | Sem identity mapping cross-CRM — GA4 user_id ≠ crm_contact.id |
| 3 | Quota 1MM/dia/property — multi-tenant 100+ pode estourar |
| 4 | (not set) virou NULL — perde info "direto sem UTM" vs "erro" |
| 5 | partial=True se qualquer step falha |
| 6 | Sem refresh incremental delta — sempre re-fetch window completo |
11. Histórico
- 2026-05-13 — Hub.3 deployado. Sessão [[sessao-2026-05-13-hub234]].