# WhatsApp CTWA — Click to WhatsApp Tracking

## O que é CTWA

Click to WhatsApp (CTWA) é um formato de anúncio no Facebook/Instagram onde o botão
de ação abre diretamente o WhatsApp em vez de direcionar para uma landing page.

O problema clássico: sem landing page, o pixel do browser não dispara, e a Meta
não consegue atribuir a conversão ao anúncio automaticamente.

A solução implementada aqui: o webhook do WhatsApp Business recebe cada mensagem
recebida, extrai o `ctwa_clid` (identificador do clique no anúncio) e envia um
evento `Contact` para a Meta Conversions API com `action_source: "chat"`.
Isso fecha o loop clique → conversa e aparece no Gerenciador de Eventos.

---

## Fluxo Completo

```
[Anúncio Meta — Click to WhatsApp]
          ↓
Usuário clica no botão "Enviar Mensagem"
          ↓
Meta injeta ctwa_clid no deep link do WhatsApp
          ↓
WhatsApp abre → usuário envia mensagem
          ↓
Meta Cloud API → POST /webhook/whatsapp (worker)
          ↓
Worker extrai: phone + ctwa_clid + ad_id + headline
          ↓
D1 armazena em whatsapp_contacts
          ↓
Meta CAPI ← evento "Contact" com:
   • ph        = SHA256(phone)   → Advanced Matching por telefone
   • ctwa_clid = click ID        → atribuição ao anúncio
   • action_source = "chat"      → obrigatório para CTWA
          ↓
[Gerenciador de Eventos mostra a conversão]
```

---

## Dados Capturados

| Campo | Origem | Enviado ao CAPI | Hasheado |
|-------|--------|-----------------|----------|
| Telefone | `message.from` | `ph` | SHA-256 |
| Click ID | `message.referral.ctwa_clid` | `ctwa_clid` | Não |
| ID do anúncio | `message.referral.source_id` | — (armazenado no D1) | — |
| URL do anúncio | `message.referral.source_url` | `event_source_url` | Não |
| Título do anúncio | `message.referral.headline` | — (armazenado no D1) | — |
| IP | Header CF-Connecting-IP | `client_ip_address` | Não |
| User-Agent | Header User-Agent | `client_user_agent` | Não |

---

## Endpoints

### `GET /webhook/whatsapp`
Verificação do webhook pela Meta (feita uma vez ao cadastrar).

Parâmetros recebidos:
- `hub.mode` = `subscribe`
- `hub.verify_token` = valor do secret `WA_WEBHOOK_VERIFY_TOKEN`
- `hub.challenge` = número aleatório que deve ser devolvido

Resposta: devolve o `hub.challenge` como texto puro com status 200.

### `POST /webhook/whatsapp`
Recebe eventos de mensagens da Meta Cloud API.

Processa apenas mensagens do tipo `messages` com `referral.ctwa_clid` (anúncios CTWA).
Mensagens orgânicas (sem referral) são recebidas com 200 mas não geram evento no CAPI.

Resposta: `{ "ok": true, "processed": N, "results": [...] }`

---

## Banco de Dados (D1)

### Tabela `whatsapp_contacts`

```sql
CREATE TABLE whatsapp_contacts (
  id            INTEGER PRIMARY KEY AUTOINCREMENT,
  created_at    TEXT    NOT NULL DEFAULT (datetime('now')),
  phone_hash    TEXT    NOT NULL,  -- SHA256(phone) — enviado ao CAPI como "ph"
  phone_raw     TEXT    NOT NULL,  -- número normalizado (ex: 5511999998888)
  wamid         TEXT    UNIQUE,    -- ID da mensagem (deduplicação)
  ctwa_clid     TEXT,              -- click ID do anúncio (não hasheado)
  ad_id         TEXT,              -- ID do anúncio
  source_url    TEXT,              -- URL do anúncio
  headline      TEXT,              -- título do anúncio
  capi_sent     INTEGER DEFAULT 0, -- 0=pendente | 1=enviado
  capi_event_id TEXT,              -- event_id para deduplicação no CAPI
  message_body  TEXT               -- texto da primeira mensagem
);
```

A coluna `capi_sent` é atualizada para `1` após confirmação de sucesso da CAPI.
A coluna `wamid` tem índice UNIQUE — garante que a mesma mensagem não seja processada duas vezes.

---

## Secrets Necessários

| Secret | Descrição | Como configurar |
|--------|-----------|-----------------|
| `WA_WEBHOOK_VERIFY_TOKEN` | Token de verificação do webhook (você define) | `wrangler secret put WA_WEBHOOK_VERIFY_TOKEN` |
| `META_ACCESS_TOKEN` | Token da Meta Conversions API | Já configurado |
| `META_PIXEL_ID` | ID do Pixel Meta | Já em `wrangler.toml` |

---

## Como Configurar no Meta Business Manager

1. Acesse [business.facebook.com](https://business.facebook.com)
2. Menu lateral → **WhatsApp** → **Configuration**
3. Seção **Webhook** → clique em **Edit**
4. Preencha:
   - **Callback URL:** `https://SEU_WORKER.SEU_USUARIO.workers.dev/webhook/whatsapp`
   - **Verify Token:** valor do secret `WA_WEBHOOK_VERIFY_TOKEN`
5. Clique em **Verify and Save**
6. Após verificar, clique em **Manage** → ative o campo **`messages`**

---

## Como Criar um Anúncio CTWA

No Gerenciador de Anúncios (ads.manager.facebook.com):

1. **Objetivo da campanha:** `Mensagens`
2. **Destino:** `WhatsApp`
3. **Número de WhatsApp:** selecione o número conectado ao Business Manager
4. O anúncio vai gerar automaticamente o `ctwa_clid` para cada clique

---

## Eventos no Gerenciador de Eventos

Após o primeiro contato via CTWA, no Gerenciador de Eventos da Meta você verá:

- **Nome do evento:** `Contact`
- **Origem do evento:** `chat`
- **Correspondência:** `Telephone` (via Advanced Matching pelo telefone hasheado)
- **ID do clique:** `ctwa_clid` vinculado ao anúncio

---

## Próximos Eventos (Qualificação no Funil)

Depois do `Contact` inicial, você pode enviar eventos adicionais conforme o lead avança:

| Momento | Endpoint | `eventName` | Dados extras |
|---------|----------|-------------|-------------|
| Lead qualificado no chat | `POST /track` | `Lead` | `phone`, `value` estimado |
| Venda confirmada no chat | `POST /track` | `Purchase` | `phone`, `value`, `currency` |

Exemplo de chamada para registrar uma venda fechada pelo WhatsApp:

```javascript
fetch('https://SEU_WORKER.SEU_USUARIO.workers.dev/track', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    eventName: 'Purchase',
    phone:     '5511999998888',
    value:     297.00,
    currency:  'BRL',
    pageUrl:   'https://wa.me/',
  })
});
```

---

## Troubleshooting

### Webhook não verifica
- Confirme que o `WA_WEBHOOK_VERIFY_TOKEN` foi salvo com `wrangler secret put`
- Confirme que o worker foi deployado após a configuração do secret
- Teste manualmente: `GET /webhook/whatsapp?hub.mode=subscribe&hub.verify_token=SEU_TOKEN&hub.challenge=12345`

### Evento não aparece no Gerenciador de Eventos
- Confirme que `META_ACCESS_TOKEN` está configurado e válido
- Verifique a tabela `whatsapp_contacts` no D1: `capi_sent = 0` indica falha no envio
- Verifique a tabela `api_failures` para detalhes do erro

### Mensagem recebida mas sem ctwa_clid
- O usuário abriu o WhatsApp diretamente (não veio de anúncio CTWA)
- O contato ainda é salvo no D1, mas sem atribuição ao anúncio
- O evento `Contact` é enviado mesmo sem ctwa_clid (com apenas o telefone hasheado)

---

## Arquivos Relacionados

| Arquivo | Descrição |
|---------|-----------|
| `server-edge-tracker/modules/dispatch/whatsapp.ts` | Funções `processWhatsAppWebhook()`, `sendWhatsApp()`, `verifyHmac()` |
| `server-edge-tracker/index.ts` | Rotas `/webhook/whatsapp` (GET verify + POST mensagens) |
| `server-edge-tracker/migrate-v6.sql` | Migration que criou a tabela `whatsapp_contacts` |
| `server-edge-tracker/wrangler.toml` | Secret `WA_WEBHOOK_VERIFY_TOKEN` documentado |
| `server-edge-tracker/schema.sql` | Schema completo do D1 (referência) |

---

*Implementado em: 30 de março de 2026. CDP Edge v1.2 — WhatsApp CTWA Module. Migrado para TypeScript (`whatsapp.ts`) em 12 de abril de 2026 (v2.2.5+).*
