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

Este módulo depende de

1
  • attributionPlugins criam attribution_campaign via attribution_bridge

Módulos que dependem deste

2
  • youtubePlugin dentro do Hub framework de marketing
  • analyticsPlugin dentro do Hub framework de marketing

Marketing (Hub framework — paid only)

Status: 🟢 Estável (Hub.1 framework + Hub.2 Meta Ads + Hub.4 Google Ads) Code: backend/app/modules/marketing UI: frontend/src/app/(admin)/[slug]/admin/marketing Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: attribution (campaign bridge), tenant_router, billing-crypto (envelope cifragem)


1. Identidade

O que faz (uma frase)

Hub canônico de marketing pago — plugin framework com 4 verbos (Observe / Operate / Predict / Suggest) onde cada provider (Meta Ads, Google Ads — e plugins externos GA4/YouTube/Instagram que vivem aqui) registra implementação isolada, com credenciais cifradas + audit completo de operações sensíveis.

Por que existe (negócio)

Mandir precisa de visão consolidada de gasto pago + orgânico, sem Felipe abrir 4 painéis diferentes. Antes deste módulo:

  • Spend Meta Ads ficava no Ads Manager.
  • Performance Google Ads no Google Ads Console.
  • GA4 no painel próprio.
  • YouTube no Studio.

Sem cruzamento → nenhuma decisão integrada.

Marketing Hub centraliza:

  • Observe (sempre) — persistir L0 dos providers em tabelas <provider>_* no Postgres do tenant.
  • Operate (com policy) — pausar campanha, ajustar bid, etc. (com aprovação humana ou autopilot).
  • Predict / Suggest — previsão de spend e recomendações (Hub.6+ futuro).

Tools pro Conselho (4 GA4 + 6 YouTube + 7 paid total) consultam direto sem o conselheiro saber detalhes da API.

Importante (memória [[council-scope-split-marketing]]): após split em 2026-05-13, o scope marketing cobre apenas paid (Meta Ads + Google Ads). Analytics (GA4) virou namespace analytics; Instagram orgânico virou instagram; YouTube virou youtube.

Por que existe (técnico)

  • Plugin architecture — adicionar provider novo = pasta nova + register, sem mexer core.
  • Persiste L0 (memória [[mandir-hub-arquitetura-persistir]]) — todo plugin grava em tabelas <provider>_* no Postgres do tenant. Conselheiros/UI lêem do DB. Latência 10ms vs 500ms hitting API; sem quota; histórico; cruzamento SQL.
  • Credenciais cifradasmarketing_credential.secret_envelope JSONB (AES-256-GCM, KEK por tenant via HMAC).
  • Operate com policy + auditmarketing_operate_policy define mode (dry_run / confirm_required / autopilot), daily_spend_cap_brl, allowed_actions. Toda operação em marketing_operate_audit (append-only, antes/depois state).

Status atual

  • Hub.1 framework + Hub.2 Meta Ads + Hub.3 GA4 + Hub.4 Google Ads + Hub.5 Instagram + Hub.6 YouTube todos deployados em 2026-05-13. Memórias [[sessao-2026-05-13-hub234]] + [[sessao-2026-05-13-hub56]].
  • 5 plugins ativos: ga4, google_ads, instagram, meta_ads, youtube.

Próxima mudança: Hub.6+ para Operate de Google Ads (pausar campanhas, ajustar bid). Hub.7+ Predict + Suggest.


2. Cases de uso reais

Case 1: Conselheiro CEO consulta paid media summary

Fluxo: CEO chama tool read_paid_media_summary(window_days=30) → cross_module agrega meta_ads_insight_daily + google_ads_insight_daily → retorna {spend_brl, impressions, clicks, conversions, ROI} → CEO inclui na resposta.

Case 2: Sync diário Meta Ads

Beat task marketing.run_observe(provider=meta_ads) → resolve credenciais ativas → para cada uma, decifra → call Meta Marketing API (campaigns + adsets + ads + insights chunked por dia) → upsert em meta_ads_* → registra marketing_sync_run(status=ok, rows_ingested).

Case 3: Felipe pausa anúncio (Operate)

UI /admin/marketing/meta-ads → botão "Pausar" → OperateRequest(action=PAUSE, action_qualified="meta_ads.pause_ad", payload={ad_id, ...}) → consulta marketing_operate_policy → se mode=confirm_required, awaits aprovação → executa via Meta API → registra marketing_operate_audit (state_before/after, executed_at, result).


3. Oportunidades de negócio

  • Cross-channel attribution as a service: consolidar attribution_campaign cross-providers — recurso premium pra agências.
  • Operate-as-a-service: "pausar tudo se ROAS < 1.0" automático com policy autopilot — diferencial sobre Ads Manager.
  • Spend forecasting: Predict via Claude com histórico → projeção semanal de gasto.
  • Plugin marketplace: terceiros desenvolvem plugins (TikTok Ads, LinkedIn Ads, Pinterest, etc.) com revenue share.
  • Compliance LGPD nativa: auditing completo (audit log) + opt-out por contato → produto pra setores regulados.

Riscos: providers mudam APIs frequentemente (Google Ads v18 → v19 anual; Meta v22 → v23); credenciais podem ser revogadas; rate limits.


4. Arquitetura interna

Arquivos

ArquivoPropósito
hub.pyContrato HubPlugin + registry + helpers
models.py4 tabelas core
service.pyget_effective_policy, evaluate_operate, audit
credentials.pystore_credential, load_credential_secret, mark_used/error/refreshed
tasks.pyrun_observe, run_observe_all, backfill workers
routes.py34 endpoints /api/marketing/*
plugins/bootstrap.pyload_plugins() no startup + worker
plugins/<provider>/Cada plugin: plugin.py, client.py, models.py, sync.py, attribution_bridge.py

Plugin contract

class HubPlugin(Protocol):
    provider: str  # "meta_ads", "google_ads", etc
    async def observe(db, *, tenant_id, credential_id, window) -> ObserveResult
    async def operate(db, *, tenant_id, credential_id, request) -> OperateResult
    async def predict(db, *, tenant_id, scope) -> list[Forecast]
    async def suggest(db, *, tenant_id) -> list[Recommendation]

Registry PLUGIN_REGISTRY: dict[str, HubPlugin]register_plugin(plugin) no __init__.py de cada subpasta.

Tasks Celery

TaskTriggerIdempotência
marketing.run_observeOn-demand (POST /sync/run)marketing_sync_run audit
marketing.run_observe_allBeatItera tenants ativos × plugins × credenciais ativas
marketing.{provider}_backfillOn-demandChunked (Meta 7d, Google 30d)
marketing.instagram_stories_captureBeat 1hStories expiram 24h

5. Tabelas + relacionamentos

Tabelas core (4)

marketing_credential

Coluna chaveNotas
tenant_id (idx)
providermeta_ads / google_ads / ga4 / youtube / instagram
labelHumano-readable
account_external_idAd account ID, customer_id, property_id, channel_id
auth_kindoauth2 / api_key / service_account
secret_envelope (JSONB)Cifrado AES-256-GCM via core.billing_crypto
scopeOAuth scopes
expires_at / refresh_atOAuth tracking
is_activeSoft kill
last_error / last_used_atAudit

Constraint: UNIQUE(tenant_id, provider, account_external_id).

marketing_sync_run

Coluna chaveNotas
provider / credential_id
kindincremental / backfill / manual / health_probe
started_at / finished_at / duration_ms
statusrunning / ok / failed / partial
rows_ingested
error
cursor_afterCheckpoint pra continuação

marketing_operate_policy

Coluna chaveNotas
provider* = default global ou específico
modedry_run / confirm_required (default fail-safe) / autopilot
daily_spend_cap_brl / per_action_spend_cap_brlLimites
allowed_actions / blocked_actionsJSONB list

Resolução: provider específico > * > fail-safe (confirm_required + caps NULL + listas vazias).

marketing_operate_audit

Append-only.

Coluna chaveNotas
provider / credential_id / actionex: meta_ads.pause_ad
actor_kinduser / council / agent / system
actor_id / approved_by_user_id / approved_at
payload_request / state_before / state_after (JSONB diff)
external_responseResposta provider
estimated_spend_impact_brl
resultpending / ok / failed / reverted / denied
error / executed_at

Tabelas L0 por plugin

  • Meta Ads (6): meta_ads_account, meta_ads_campaign, meta_ads_adset, meta_ads_ad, meta_ads_insight_daily, meta_ads_audience.
  • Google Ads (6): google_ads_customer, google_ads_campaign, google_ads_ad_group, google_ads_keyword, google_ads_insight_daily, google_ads_search_term_daily.
  • GA4 (4): ver analytics.
  • YouTube (9): ver youtube.
  • Instagram (6): ver instagram (compartilhado, não tabelas ig_* pra evitar colisão).

Relacionamentos cross-módulo

DireçãoOutro móduloComo
↘ Escreveattributionattribution_bridge.sync_attribution_campaigns cria attribution_campaign por novo Meta/Google ad
↘ Toolscouncil7 tools paid (read_paid_media_summary, read_meta_*, read_google_ads_*)
↘ Emite eventtrackingmarketing.sync.*, marketing.operate.*

6. API / Endpoints (34)

Prefixo /api/marketing.

Hub Infrastructure (6)

MétodoRotaO que faz
GET/pluginsLista plugins registrados
GET/healthPlugins, credenciais por provider, last sync, pending ops
GET/credentialsLista (filtro provider)
POST/credentialsStore (cifra secret)
POST/credentials/{id}/deactivateMark inactive
GET / PUT/operate-policyCRUD policy

Sync Triggers (8)

MétodoRotaO que faz
POST/sync/runGeneric observe (provider, credential_id, window_days)
POST/sync/{provider}/backfillChunked backfill (meta-ads, google-ads, ga4, youtube, instagram)
POST/sync/instagram/storiesCapture stories (cadência 1h)
GET/sync-runsHistórico runs

Views por provider

  • Meta Ads: /meta-ads/campaigns, /meta-ads/summary (spend 30d).
  • Google Ads: /google-ads/summary, /google-ads/campaigns, /google-ads/search-terms.
  • GA4: /ga4/summary, /ga4/sources, /ga4/events (ver analytics).
  • YouTube: /youtube/{summary,videos,traffic-sources,geography,demographics,search-terms} (6 endpoints — ver youtube).
  • Instagram: /instagram/{summary,media,audience,hashtags} (4 endpoints — ver instagram).

Audit (1)

MétodoRotaO que faz
GET/operate-auditHistórico (filtro provider, resultado, actor)

7. Tools no Council (paid only após split)

ToolImplementação
read_paid_media_summaryAgrega meta_ads_insight_daily + google_ads_insight_daily
read_meta_top_adsTop ads por spend/CTR/conversões
read_meta_campaign_insightsDetalhe por campanha
read_google_ads_summarySpend BRL, conversions, ROI 30d
read_google_ads_campaignsList com cost_brl, CPC, conversions
read_google_ads_search_termsTop search terms por conversão
read_google_ads_keywordsKeywords com quality_score, bid

GA4/YouTube/Instagram tools movidos pros respectivos módulos após split.


8. Configuração

Env vars

VarPropósito
MANDIR_CRYPTO_MASTER_KEYObrigatório — KEK para secret_envelope
MANDIR_PLATFORM_DATABASE_URLMulti-tenant
Provider-specific tokensEm secret_envelope por credencial (não env)

Kill switches

  • Policy mode=dry_run → nega todas as operações (teste).
  • credential.is_active=false → skip observe.
  • Pause global: dropar/desativar credencial.

Rate limits

  • Google Ads API: 100k requests/mês padrão.
  • Meta Graph: ~200 calls/min.
  • GA4 Data API: 1MM requests/dia/property.
  • YouTube Analytics: 500M units/dia/projeto.

9. Operações

Como conectar provider novo

  1. UI /admin/marketing → "Conectar {Provider}" → OAuth flow ou paste API key.
  2. Backend: store_credential(tenant_id, provider, label, secret) → cifra envelope.
  3. Trigger inicial sync: POST /sync/run?provider=X&credential_id=Y.
  4. Verifica marketing_sync_run.status=ok + rows_ingested>0.

Troubleshooting

Sintoma: Sync falha com auth_error

Diagnóstico: SELECT last_error FROM marketing_credential WHERE id=.... OAuth refresh expirou ou revogado. Fix: UI reconectar → novo refresh_token → mark_token_refreshed.

Sintoma: Operate negado mesmo em autopilot

Causa: per_action_spend_cap_brl excedido pela operação. Fix: ajustar policy ou aprovar manualmente.


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

#ItemImpacto
1Google Ads API versionadaUpdate anual obrigatório (v18 → v19)
2Service Account vs OAuth Client confusãoMemória [[oauth-client-vs-service-account-json]] documenta
3MIGRATION_DATABASE_URL hardcoded em alguns workflowsMemória [[sessao-2026-05-13-hub234]]
4Meta Cloud API page_size limitationsAdapter chunked
5parse_iso em datetimes inconsistente entre providersHelper unificado em service.py
6Operate sem rollback automáticoManual via audit (state_before)
7Sem circuit breaker cross-providerFase pós Hub.4

11. Histórico relevante

  • 2026-05-13 — Hub.1 + Hub.2 + Hub.3 + Hub.4 + Hub.5 + Hub.6 todos em prod. Memórias [[sessao-2026-05-13-hub234]] + [[sessao-2026-05-13-hub56]].
  • 2026-05-13 — Split scope marketing em 4 namespaces (marketing paid + analytics GA4 + instagram orgânico+DM + youtube). Commit d28b54d. Memória [[council-scope-split-marketing]].
  • 2026-05-13 — Projeto GCP mais-consciente-mandir cravado: OAuth Client + 7 scopes + 5 APIs ativas + SA mais-consciente-analytics.

12. Glossário

  • Hub plugin: implementação de HubPlugin (Observe/Operate/Predict/Suggest).
  • L0: dados crus persistidos em tabelas <provider>_* (não derivados).
  • Observe: persistir dados; sempre seguro.
  • Operate: executar ação no provider (pausar, ajustar); sujeito a policy.
  • Predict: projeção (futuro Hub.6+).
  • Suggest: recomendação (futuro Hub.7+).
  • Policy mode: dry_run (nega tudo) / confirm_required (default — humano aprova) / autopilot (executa direto se dentro de allowed_actions + caps).