# GHL UTM Enricher — PX3 Lab

Micro-serviço webhook que enriquece contatos do Linkia PX3 com UTM
baseado no `attributionSource` nativo do GHL, pra alimentar o workflow
`[UTM] Aplicar tag de origem` existente no CRM.

## URLs

- Interno: `http://127.0.0.1:8919`
- Público: `https://ghl-utm-enricher-px3.agentesclimb.us`
- Health: `GET /health`
- Webhook: `POST /webhook`
- Manual: `GET|POST /enrich/<contact_id>?force=1`

## Config

- Env: `/opt/mia/config/ghl_utm_enricher_px3.env` (chmod 600)
- Systemd: `ghl-utm-enricher-px3.service`
- Log: `/opt/mia/logs/ghl_utm_enricher_px3.log`
- DB (dedupe): `./dedupe.db`

## Cadastro do webhook no Linkia PX3 (Renato)

1. Entrar no CRM Linkia PX3 (location `W7PGxpfbsFaEEUoQOtUb`).
2. **Settings → Integrations → Webhooks** (ou Workflows → criar webhook step).
3. Criar webhook com:
   - **Evento:** `Contact Created` (idealmente também `Contact Updated` se disponível)
   - **URL:** `https://ghl-utm-enricher-px3.agentesclimb.us/webhook`
   - **Method:** `POST`
   - **Content-Type:** `application/json`
4. (Opcional) Setar `WEBHOOK_SECRET` no env e adicionar header
   `X-Webhook-Secret: <valor>` no cadastro do webhook pra segurança extra.

Se o Linkia não tiver painel de webhook nativo, alternativa: criar um
Workflow disparado por `Contact Created` que faça um step "Webhook" pra
mesma URL, passando o `{{contact.id}}` no body como `{"contact_id": "..."}`.

## Comandos operacionais

```bash
# Status
sudo systemctl status ghl-utm-enricher-px3

# Restart
sudo systemctl restart ghl-utm-enricher-px3

# Log ao vivo
tail -f /opt/mia/logs/ghl_utm_enricher_px3.log

# Testar manualmente enriquecimento de um contato
curl https://ghl-utm-enricher-px3.agentesclimb.us/enrich/<contact_id>

# Testar webhook local
curl -X POST http://127.0.0.1:8919/webhook \
  -H 'Content-Type: application/json' \
  -d '{"type":"ContactCreate","contact_id":"OCOLI3hoMT5fK3LMsFmI"}'
```

## Backfill retroativo

```bash
cd /opt/mia/workspace/clientes/px3lab/ghl_utm_enricher

# 1) SEMPRE começar com analyze (só lê, não toca) pra ver o que vai acontecer:
/opt/mia/venvs/hotmart_capi/bin/python backfill_enrichment.py --days 90 --limit 200 --analyze

# 2) Dry-run: passa por todo o fluxo mas nao escreve
/opt/mia/venvs/hotmart_capi/bin/python backfill_enrichment.py --days 90 --limit 100 --dry-run

# 3) Execução real (limite 100 pra segurança inicial)
/opt/mia/venvs/hotmart_capi/bin/python backfill_enrichment.py --days 90 --limit 100

# 4) Backfill completo dos 90 dias
/opt/mia/venvs/hotmart_capi/bin/python backfill_enrichment.py --days 90 --limit 0
```

## Regras de mapeamento (attribution -> UTM)

Aplicadas na função `_map_attribution_to_utm()` em `app.py`:

| attributionSource                            | utm_source | utm_medium         |
|----------------------------------------------|------------|--------------------|
| tem `utmSource` populado                     | (usa direto — preserva utmSource/Medium/Campaign/Content/Term nativos) |
| `Paid Social` + medium=whatsapp              | meta       | whatsapp_ads       |
| `Paid Social` (outro medium)                 | meta       | paid_social        |
| `Social media` / `Organic Social` + whatsapp | meta       | whatsapp_organic   |
| `Social media` / `Organic Social` (outro)    | meta       | social             |
| `Paid Search` / contém `cpc`                 | google     | cpc                |
| `Organic Search`                             | google     | organic            |
| `Referral`                                   | referral   | referral           |
| `Email`                                      | email      | email              |
| `Direct` / `CRM Workflows` / `CRM UI` / `Manual` / vazio | (nada — deixa workflow classificar como `origem_desconhecida`) |

**Bônus (Paid Social):** quando o GHL captura `adName` e `adId` do anúncio
Meta (fluxo fb.me → WhatsApp), o enricher popula automaticamente
`utm_campaign = adName` e `utm_content = adId`.

O guard `already_has_utm_source` NUNCA sobrescreve valor existente no campo.

## Arquitetura modular

`app.py` expõe `run_handlers(contact_id, event_type)` que hoje só chama
o `enrich_contact()`. Novos handlers (ex: envio de `Purchase` pro Meta
CAPI quando N8N Dinastia processar compra) plugam nessa lista sem tocar
no webhook.
