cdp-edge 1.23.1 → 1.23.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.md +45 -21
  2. package/bin/cdp-edge.js +1 -1
  3. package/contracts/agent-versions.json +67 -66
  4. package/dist/commands/install.js +1 -1
  5. package/dist/commands/server.js +4 -4
  6. package/extracted-skill/tracking-events-generator/agents/database-agent.md +5 -4
  7. package/extracted-skill/tracking-events-generator/agents/fraud-detection-agent.md +0 -1
  8. package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +1 -1
  9. package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +4 -4
  10. package/extracted-skill/tracking-events-generator/agents/ml-clustering-agent.md +81 -70
  11. package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +6 -2
  12. package/extracted-skill/tracking-events-generator/cdpTrack.js +7 -0
  13. package/extracted-skill/tracking-events-generator/models/lancamento-imobiliario.md +344 -0
  14. package/extracted-skill/tracking-events-generator/route-intent-capture.js +222 -0
  15. package/package.json +3 -2
  16. package/server-edge-tracker/INSTALAR.md +5 -5
  17. package/server-edge-tracker/index.js +109 -7
  18. package/server-edge-tracker/modules/db.js +71 -0
  19. package/server-edge-tracker/modules/dispatch/meta.js +12 -0
  20. package/server-edge-tracker/modules/ml/fraud.js +1 -16
  21. package/server-edge-tracker/modules/ml/ltv.js +62 -11
  22. package/server-edge-tracker/modules/ml/segmentation.js +157 -127
  23. package/server-edge-tracker/modules/utils.js +78 -0
  24. package/server-edge-tracker/schema-ltv-feedback.sql +11 -0
  25. package/server-edge-tracker/wrangler.toml +26 -8
  26. package/templates/lancamento-imobiliario.md +344 -0
  27. package/server-edge-tracker/worker.js +0 -4596
@@ -128,74 +128,74 @@ is_business_hours = 1 if 9 <= hour_of_day <= 18 else 0
128
128
 
129
129
  ---
130
130
 
131
- ## Fase 2 — K-Means Clustering (Workers AI)
131
+ ## Fase 2 — K-Means Vetorial Real (embeddinggemma-300m + K-means em JS)
132
132
 
133
- ### 2.1 Prompt para Workers AI
133
+ > **Arquitetura atual:** O clustering não usa LLM para fazer os cálculos matemáticos.
134
+ > Em vez disso, usa **embeddings semânticos reais** + **K-means implementado em JavaScript**,
135
+ > com o Granite usado **apenas para nomear** os clusters resultantes.
134
136
 
135
- ```python
136
- # Enviar para: env.AI.run('@cf/meta/llama-3.1-8b-instruct', ...)
137
+ ### 2.1 Pipeline de Clustering
137
138
 
138
- PROMPT_CLUSTERING = f"""
139
- You are a Machine Learning expert specializing in customer segmentation.
139
+ ```
140
+ 100 leads (sample) perfil textual embeddinggemma-300m vetores 768d
141
+
142
+ K-means++ (cosine distance, JS puro)
143
+
144
+ silhouette score real calculado em JS
145
+
146
+ Granite 4.0 Micro nomeia cada cluster (1 call de LLM)
147
+ ```
140
148
 
141
- You will receive {n_leads} customers with {features} each.
142
- Your task: Perform K-means clustering to group customers into {n_clusters} segments.
149
+ ### 2.2 Modelos Workers AI utilizados
143
150
 
144
- INPUTS:
145
- - leads: JSON array of customer objects
146
- - features: list of feature names (ltv, behavior_score, engagement_score, etc.)
147
- - n_clusters: number of segments to create (3-10)
151
+ | Modelo | ID | Uso |
152
+ |---|---|---|
153
+ | **Granite 4.0 Micro** | `@cf/ibm-granite/granite-4.0-h-micro` | LTV Prediction + Naming de clusters |
154
+ | **EmbeddingGemma 300M** | `@cf/baai/bge-m3` | Embeddings semânticos para K-means |
148
155
 
149
- TASK:
150
- 1. Normalize all features to 0-1 range (min-max normalization)
151
- 2. Initialize K-means centroids randomly
152
- 3. Assign each lead to nearest centroid (Euclidean distance)
153
- 4. Recalculate centroids as mean of assigned points
154
- 5. Iterate until convergence (max 100 iterations)
155
- 6. Calculate Silhouette Score for each cluster (cohesion vs separation)
156
+ ### 2.3 Perfil textual por lead (input para embedding)
156
157
 
157
- OUTPUT (JSON only):
158
- {{
159
- "clusters": [
160
- {{
161
- "cluster_id": 0,
162
- "name": "Segmento 0 - [AUTO-GENERATED DESCRIPTIVE NAME]",
163
- "size": 123,
164
- "percentage": 0.25,
165
- "characteristics": {{
166
- "avg_ltv": 497.50,
167
- "avg_behavior_score": 75.3,
168
- "avg_engagement_score": 82.1,
169
- "dominant_countries": ["BR", "AR"],
170
- "dominant_states": ["SP", "RJ"],
171
- "dominant_utm_source": ["facebook", "google"],
172
- "top_features": ["ltv", "behavior_score", "engagement_score"]
173
- }},
174
- "centroid": {{
175
- "ltv": 0.75,
176
- "behavior_score": 0.80,
177
- "engagement_score": 0.85
178
- }},
179
- "sample_leads": [lead_id_1, lead_id_2, lead_id_3]
180
- }},
181
- ...
182
- ],
183
- "silhouette_scores": {{
184
- "overall": 0.62,
185
- "by_cluster": [0.71, 0.58, 0.65, ...]
186
- }},
187
- "convergence": {{
188
- "iterations": 47,
189
- "final_inertia": 1523.45
190
- }}
191
- }}
158
+ ```javascript
159
+ function _buildLeadProfile(l) {
160
+ return [
161
+ `LTV: ${l.predicted_ltv_class || 'desconhecido'}`,
162
+ `engajamento: ${Math.round(l.engagement_score || 0)}`,
163
+ `intenção: ${l.intention_level || 'desconhecida'}`,
164
+ `origem: ${l.utm_source || 'direto'}`,
165
+ `canal: ${l.utm_medium || 'desconhecido'}`,
166
+ `país: ${l.country || 'BR'}`,
167
+ `hora: ${l.hour_of_day || 12}h`,
168
+ (l.is_weekend ? 'fim-de-semana' : 'dia-útil'),
169
+ `recência: ${l.days_since_lead || 0} dias`,
170
+ ].filter(Boolean).join(', ');
171
+ }
172
+ ```
192
173
 
193
- IMPORTANT:
194
- - Generate descriptive names for segments based on cluster characteristics
195
- - Example: "Segmento 0 - Alto Valor + Alto Engajamento (SP)"
196
- - Example: "Segmento 1 - Lead Quente + Alta Intenção (RJ)"
197
- - Return ONLY valid JSON, no explanations
198
- """
174
+ ### 2.4 Chamada de embeddings em batch
175
+
176
+ ```javascript
177
+ // Embeds até 100 perfis em uma única chamada
178
+ const embRes = await env.AI.run('@cf/baai/bge-m3', { text: profiles });
179
+ const vectors = embRes.data; // float32[][] — shape [N, 768]
180
+ ```
181
+
182
+ ### 2.5 K-means vetorial (cosine distance)
183
+
184
+ ```javascript
185
+ // Inicialização K-means++ → iterações até convergência → assignments finais
186
+ const { assignments } = _kmeansRun(vectors, nClusters); // implementado em worker.js
187
+ const silhouetteScore = _silhouette(vectors, assignments, nClusters); // score real
188
+ ```
189
+
190
+ ### 2.6 Naming dos clusters via Granite (único uso de LLM)
191
+
192
+ ```javascript
193
+ // Granite recebe apenas as estatísticas agregadas por cluster
194
+ // Retorna nome descritivo + recomendação de campanha em português
195
+ const nameRes = await env.AI.run('@cf/ibm-granite/granite-4.0-h-micro', {
196
+ messages: [{ role: 'user', content: namingPrompt }],
197
+ max_tokens: 800
198
+ });
199
199
  ```
200
200
 
201
201
  ### 2.2 Features para K-Means
@@ -484,20 +484,30 @@ export async function onRequestGet(context: EventContext<Env>) {
484
484
  // Feature Engineering
485
485
  const features = extractFeatures(leads);
486
486
 
487
- // Clustering via Workers AI
488
- const clusters = await context.env.AI.run(
489
- '@cf/meta/llama-3.1-8b-instruct',
490
- { messages: [{ role: 'user', content: getClusteringPrompt(features, nClusters) }] }
487
+ // 1. Embeddings reais via embeddinggemma-300m
488
+ const profiles = sample.map(_buildLeadProfile);
489
+ const embRes = await context.env.AI.run('@cf/baai/bge-m3', { text: profiles });
490
+ const vectors = embRes.data; // vetores 768d
491
+
492
+ // 2. K-means vetorial real (JS puro, cosine distance)
493
+ const { assignments } = _kmeansRun(vectors, nClusters);
494
+ const silhouetteScore = _silhouette(vectors, assignments, nClusters);
495
+
496
+ // 3. Granite apenas para nomear clusters
497
+ const nameRes = await context.env.AI.run('@cf/ibm-granite/granite-4.0-h-micro',
498
+ { messages: [{ role: 'user', content: getNamingPrompt(clusterStats) }], max_tokens: 800 }
491
499
  );
492
-
493
- // Persistir no D1
500
+
501
+ // 4. Persistir no D1
494
502
  await saveClusters(context.env.DB, clusters, algorithm);
495
-
503
+
496
504
  return Response.json({
497
505
  success: true,
498
506
  algorithm,
507
+ engine: 'embeddinggemma-300m + kmeans vetorial',
499
508
  n_clusters: nClusters,
500
- clusters: JSON.parse(clusters.response),
509
+ silhouette_score: silhouetteScore,
510
+ clusters,
501
511
  generated_at: new Date().toISOString()
502
512
  });
503
513
  }
@@ -658,7 +668,7 @@ interface SegmentationAPI {
658
668
 
659
669
  ```
660
670
  [ ] Feature Engineering Pipeline implementada
661
- [ ] K-means Clustering via Workers AI
671
+ [ ] K-means Clustering vetorial (embeddinggemma-300m + JS)
662
672
  [ ] DBSCAN Clustering para anomalias
663
673
  [ ] Hierarchical Clustering (drill-down)
664
674
  [ ] Auto-Interpretação de segmentos
@@ -679,7 +689,8 @@ interface SegmentationAPI {
679
689
  | `Clusters vazios` | Menos de `min_data_points` no D1 | Aumentar `max_data_age_months` ou aguardar mais dados |
680
690
  | `Silhouette Score < 0.3` | Clusters não são separáveis | Aumentar `n_clusters` ou usar features melhores |
681
691
  | `Outliers excessivos` | Epsilon/MinPts muito agressivos no DBSCAN | Ajustar parâmetros de detecção de anomalias |
682
- | `Workers AI timeout` | Prompt muito longo ou muitos dados | Dividir em batches de 100-200 leads por request |
692
+ | `embeddinggemma timeout` | Batch maior que 100 perfis | Limitar sample a 100 leads (padrão atual) |
693
+ | `vectors insuficientes` | embeddinggemma retornou menos vetores que nClusters | Reduzir nClusters ou verificar resposta da API |
683
694
 
684
695
  ---
685
696
 
@@ -26,6 +26,9 @@ Toda a sua análise deve ser baseada na infraestrutura nativa da Cloudflare.
26
26
  | Página de obrigado | `Purchase_Success` | 🔴 crítico |
27
27
  | **Preço Formatado (R$, $, €)** | `value_extraction` (Atrelado ao CTA) | 🔴 crítico |
28
28
  | **Campo de Formulário Oculto (CSS hide/opacity 0)** | `honeypot_field` | 🔴 crítico |
29
+ | **[IMÓVEIS] Mapa / botão "Ver localização" / iframe Google Maps** | `FindLocation` | 🔴 crítico imobiliário |
30
+ | **[IMÓVEIS] Simulador de financiamento / parcelas / FGTS / Caixa** | `CustomizeProduct` | 🔴 crítico imobiliário |
31
+ | **[IMÓVEIS] Botão "Favoritar" / ícone de coração / "Salvar imóvel"** | `AddToWishlist` | 🟡 essencial imobiliário |
29
32
  | **Aparecimento de Botão Atrasado (Timer)** | `pitch_seen` | 🟡 essencial |
30
33
  | **Classe de Erro CSS (.error, .invalid)** | `form_error_detected` | 🟡 essencial |
31
34
  | **Micro-Evento: Rage Click** | `rage_click` | 🟡 essencial |
@@ -39,8 +42,9 @@ Toda a sua análise deve ser baseada na infraestrutura nativa da Cloudflare.
39
42
  Antes de mapear elementos, identifique o **modelo de negócio** da página para ativar protocolos específicos:
40
43
 
41
44
  1. **Lançamento Imobiliário**:
42
- * Sinais: Galeria de fotos de imóveis, botões WhatsApp, formulário curto, mapas.
43
- * Protocolo: Ativar `geolocation` e `lead_lock_whatsapp`.
45
+ * Sinais: Galeria de fotos de imóveis, botões WhatsApp, formulário curto, mapas, simulador de financiamento, botão favoritar, calendário de agendamento, tour 360°.
46
+ * Protocolo: Ativar `geolocation`, `lead_lock_whatsapp`, `real_estate_events`.
47
+ * Eventos obrigatórios extras: `FindLocation` (mapa/localização), `CustomizeProduct` (simulação financiamento), `AddToWishlist` (favoritar), `Schedule` (agendamento de visita), `Contact` (WhatsApp/telefone), `video_25/50/75/complete` (tour virtual/VSL).
44
48
  2. **Página de Vendas (Infoproduto)**:
45
49
  * Sinais: Botões Hotmart/Kiwify/Ticto, Vídeo (VSL), Depoimentos, Preço.
46
50
  * Protocolo: Ativar `checkout_passthrough` e `vsl_retention`.
@@ -25,6 +25,7 @@ import {
25
25
  initAntiBlocking,
26
26
  ANTI_BLOCKING_CONFIG
27
27
  } from './anti-blocking.js';
28
+ import { initRouteIntentCapture } from './route-intent-capture.js';
28
29
 
29
30
  // ── Guards — segurança em SSR e SDK não carregado ──
30
31
  const isBrowser = typeof window !== 'undefined';
@@ -631,6 +632,12 @@ export async function init() {
631
632
  passCheckoutParams({ platforms: CONFIG.platforms });
632
633
  }
633
634
 
635
+ // 7. Route Intent Capture — widget pós-clique de rota para imóveis
636
+ // Ativa quando CONFIG.routeIntent.whatsappNumber estiver configurado.
637
+ if (CONFIG.routeIntent?.whatsappNumber) {
638
+ initRouteIntentCapture(CONFIG.routeIntent);
639
+ }
640
+
634
641
  if (CONFIG.debug) console.log('✅ cdpTrack SDK inicializado (Quantum Tier)');
635
642
  }
636
643
 
@@ -0,0 +1,344 @@
1
+ # Modelo: Lançamento Imobiliário (Cloudflare Native)
2
+
3
+ Template para páginas de lançamento imobiliário com formulário de interesse, galeria, mapa/localização, simulador de financiamento, vídeo de tour, calendário de agendamento e botão WhatsApp.
4
+
5
+ ---
6
+
7
+ ## 🏗️ ARQUITETURA TÉCNICA (Quantum Tier)
8
+
9
+ ```
10
+ Browser (cdpTrack.js)
11
+ ├─ PageView → ao carregar
12
+ ├─ ViewContent → ao entrar na galeria
13
+ ├─ FindLocation → ao interagir com mapa/localização
14
+ ├─ video_25/50/75/complete → ao assistir tour virtual
15
+ ├─ CustomizeProduct → ao usar simulador de financiamento
16
+ ├─ AddToWishlist → ao favoritar imóvel
17
+ ├─ Contact → ao clicar em WhatsApp/telefone
18
+ ├─ Schedule → ao confirmar agendamento de visita
19
+ └─ Lead → ao enviar formulário (PII completo)
20
+
21
+
22
+ Cloudflare Worker (same-domain /track)
23
+ ├─ Fraud Gate
24
+ ├─ LTV Prediction (Granite 4.0 Micro) + score por eventType
25
+ ├─ D1: upsertProfile, identity graph, distanceKm
26
+ └─ CAPI dispatch: Meta v22.0 + GA4 + TikTok v1.3
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 📘 MAPA DE EVENTOS
32
+
33
+ | Evento | Gatilho | Sinal para as plataformas |
34
+ |---|---|---|
35
+ | `PageView` | Carregamento | Topo do funil |
36
+ | `ViewContent` | Entra na galeria de fotos | Interesse no produto |
37
+ | `FindLocation` | Clica em mapa / "Como chegar" / abre localização | Intenção de visita física — +10 LTV |
38
+ | `video_25` / `video_50` / `video_75` / `video_complete` | Tour virtual / VSL em % assistido | Engajamento profundo |
39
+ | `CustomizeProduct` | Usa simulador de financiamento/parcelas/FGTS | Intenção de compra máxima — +15 LTV |
40
+ | `AddToWishlist` | Clica em "Favoritar" / coração | Interesse persistente — +8 LTV |
41
+ | `Contact` | Clica em WhatsApp ou telefone | Alta intenção de contato |
42
+ | `Schedule` | Confirma agendamento de visita | Conversão de visita |
43
+ | `Lead` | Envia formulário (nome/email/telefone) | Conversão principal com PII |
44
+
45
+ ---
46
+
47
+ ## 🛠️ PASSO 1: CONFIGURAÇÃO DO SDK
48
+
49
+ ### 1.1 Header
50
+ ```html
51
+ <script src="/js/cdpTrack.js" async></script>
52
+ <script>
53
+ window.cdpConfig = {
54
+ metaId: 'SEU_PIXEL_ID',
55
+ ttId: 'SEU_TIKTOK_ID',
56
+ trackEndpoint: '/track'
57
+ };
58
+ </script>
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 🛠️ PASSO 2: EVENTOS DE COMPORTAMENTO
64
+
65
+ ### 2.1 PageView (automático via SDK)
66
+ O SDK dispara automaticamente. Não precisa de código adicional.
67
+
68
+ ---
69
+
70
+ ### 2.2 ViewContent — Galeria de fotos
71
+ ```javascript
72
+ // Dispara quando usuário abre/rola a galeria de imagens
73
+ document.querySelector('.galeria-imovel, [data-section="gallery"]')?.addEventListener('click', () => {
74
+ cdpTrack.track('ViewContent', {
75
+ contentName: 'Galeria — [NOME DO EMPREENDIMENTO]',
76
+ contentCategory: 'imovel',
77
+ funnel_stage: 'gallery_view',
78
+ });
79
+ });
80
+ ```
81
+
82
+ ---
83
+
84
+ ### 2.3 FindLocation — Mapa / Localização
85
+ ```javascript
86
+ // Dispara quando usuário clica no mapa, "Como chegar" ou "Ver localização"
87
+ document.querySelectorAll(
88
+ 'a[href*="maps"], a[href*="waze"], [data-section="localizacao"], #mapa, .btn-localizacao, .btn-como-chegar'
89
+ ).forEach(el => {
90
+ el.addEventListener('click', () => {
91
+ cdpTrack.track('FindLocation', {
92
+ contentName: 'Localização — [NOME DO EMPREENDIMENTO]',
93
+ contentCategory: 'imovel',
94
+ funnel_stage: 'map_view',
95
+ // Coordenadas do empreendimento (preencher):
96
+ property_lat: -23.5505, // latitude
97
+ property_lng: -46.6333, // longitude
98
+ });
99
+ });
100
+ });
101
+ ```
102
+
103
+ ---
104
+
105
+ ### 2.4 Vídeo / Tour Virtual
106
+ ```javascript
107
+ // Para vídeo HTML5 nativo:
108
+ const video = document.querySelector('video#tour-virtual, video.tour-360');
109
+ if (video) {
110
+ const fired = new Set();
111
+ video.addEventListener('timeupdate', () => {
112
+ const pct = Math.floor((video.currentTime / video.duration) * 100);
113
+ if (pct >= 25 && !fired.has(25)) {
114
+ fired.add(25);
115
+ cdpTrack.track('video_25', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
116
+ }
117
+ if (pct >= 50 && !fired.has(50)) {
118
+ fired.add(50);
119
+ cdpTrack.track('video_50', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
120
+ }
121
+ if (pct >= 75 && !fired.has(75)) {
122
+ fired.add(75);
123
+ cdpTrack.track('video_75', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
124
+ }
125
+ });
126
+ video.addEventListener('ended', () => {
127
+ cdpTrack.track('video_complete', { contentName: 'Tour Virtual — [NOME DO EMPREENDIMENTO]' });
128
+ });
129
+ }
130
+
131
+ // Para YouTube embed (via YouTube API):
132
+ // Adicionar ?enablejsapi=1 na URL do iframe e usar onStateChange
133
+ // Ver: https://developers.google.com/youtube/iframe_api_reference
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 2.5 CustomizeProduct — Simulador de Financiamento
139
+ ```javascript
140
+ // Dispara quando usuário interage com o simulador de parcelas/FGTS/Caixa
141
+ const simulador = document.querySelector(
142
+ '#simulador, .simulador-financiamento, [data-section="simulacao"], form.simulador'
143
+ );
144
+
145
+ if (simulador) {
146
+ // Dispara ao 1º interact (foco em qualquer campo do simulador)
147
+ let simuladorFired = false;
148
+ simulador.addEventListener('focusin', () => {
149
+ if (simuladorFired) return;
150
+ simuladorFired = true;
151
+ cdpTrack.track('CustomizeProduct', {
152
+ contentName: 'Simulador de Financiamento — [NOME DO EMPREENDIMENTO]',
153
+ contentCategory: 'imovel',
154
+ funnel_stage: 'financing_simulation',
155
+ intentionLevel: 'comprador',
156
+ });
157
+ });
158
+
159
+ // Dispara também ao clicar em "Simular" / "Calcular"
160
+ simulador.querySelector('button[type="submit"], .btn-simular, .btn-calcular')?.addEventListener('click', () => {
161
+ const valorImovel = simulador.querySelector('input[name="valor"], #valor-imovel')?.value;
162
+ cdpTrack.track('CustomizeProduct', {
163
+ contentName: 'Simulação Concluída — [NOME DO EMPREENDIMENTO]',
164
+ contentCategory: 'imovel',
165
+ funnel_stage: 'financing_simulation',
166
+ intentionLevel: 'comprador',
167
+ value: valorImovel ? parseFloat(valorImovel.replace(/\D/g, '')) : undefined,
168
+ currency: 'BRL',
169
+ });
170
+ });
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ### 2.6 AddToWishlist — Favoritar Imóvel
177
+ ```javascript
178
+ // Dispara quando usuário clica em favoritar / ícone de coração
179
+ document.querySelectorAll('.btn-favoritar, .icon-heart, [data-action="favoritar"], .favorito').forEach(el => {
180
+ el.addEventListener('click', () => {
181
+ cdpTrack.track('AddToWishlist', {
182
+ contentName: '[NOME DO EMPREENDIMENTO]',
183
+ contentCategory: 'imovel',
184
+ funnel_stage: 'wishlist',
185
+ });
186
+ });
187
+ });
188
+ ```
189
+
190
+ ---
191
+
192
+ ### 2.7 Contact — WhatsApp / Telefone
193
+ ```javascript
194
+ // WhatsApp
195
+ document.querySelectorAll('a[href*="wa.me"], a[href*="whatsapp.com"], .btn-whatsapp').forEach(el => {
196
+ el.addEventListener('click', () => {
197
+ cdpTrack.track('Contact', {
198
+ contentName: 'WhatsApp — [NOME DO EMPREENDIMENTO]',
199
+ contentCategory: 'imovel_whatsapp',
200
+ funnel_stage: 'whatsapp_click',
201
+ intentionLevel: 'comprador',
202
+ });
203
+ });
204
+ });
205
+
206
+ // Telefone
207
+ document.querySelectorAll('a[href^="tel:"], .btn-ligar, .btn-telefone').forEach(el => {
208
+ el.addEventListener('click', () => {
209
+ cdpTrack.track('Contact', {
210
+ contentName: 'Telefone — [NOME DO EMPREENDIMENTO]',
211
+ contentCategory: 'imovel_telefone',
212
+ funnel_stage: 'phone_click',
213
+ intentionLevel: 'comprador',
214
+ });
215
+ });
216
+ });
217
+ ```
218
+
219
+ ---
220
+
221
+ ### 2.8 Schedule — Calendário de Agendamento de Visita
222
+ ```javascript
223
+ // Dispara quando usuário CONFIRMA o agendamento (não ao abrir o calendário)
224
+ // Adaptar ao provider: Calendly, Google Calendar, formulário próprio
225
+
226
+ // Exemplo com Calendly:
227
+ window.addEventListener('message', (e) => {
228
+ if (e.data?.event === 'calendly.event_scheduled') {
229
+ cdpTrack.track('Schedule', {
230
+ contentName: 'Visita Agendada — [NOME DO EMPREENDIMENTO]',
231
+ contentCategory: 'imovel',
232
+ funnel_stage: 'schedule_confirmed',
233
+ intentionLevel: 'comprador',
234
+ // Dados do lead do Calendly (se disponíveis via payload):
235
+ email: e.data?.payload?.invitee?.email,
236
+ firstName: e.data?.payload?.invitee?.first_name,
237
+ });
238
+ }
239
+ });
240
+
241
+ // Exemplo com formulário próprio de agendamento:
242
+ document.querySelector('#form-agendamento')?.addEventListener('submit', async (e) => {
243
+ e.preventDefault();
244
+ const data = new FormData(e.target);
245
+ await cdpTrack.track('Schedule', {
246
+ contentName: 'Visita Agendada — [NOME DO EMPREENDIMENTO]',
247
+ contentCategory: 'imovel',
248
+ funnel_stage: 'schedule_confirmed',
249
+ intentionLevel: 'comprador',
250
+ email: data.get('email'),
251
+ phone: data.get('phone'),
252
+ firstName: data.get('nome')?.split(' ')[0],
253
+ });
254
+ e.target.submit();
255
+ });
256
+ ```
257
+
258
+ ---
259
+
260
+ ### 2.9 Lead — Formulário Principal de Interesse
261
+ ```javascript
262
+ document.querySelector('#form-interesse, #form-lead, form.form-contato')?.addEventListener('submit', async (e) => {
263
+ e.preventDefault();
264
+
265
+ await cdpTrack.track('Lead', {
266
+ // PII — enviados hasheados pelo Worker
267
+ email: e.target.email?.value?.trim(),
268
+ phone: e.target.phone?.value?.trim() || e.target.telefone?.value?.trim(),
269
+ firstName: e.target.nome?.value?.split(' ')[0]?.trim(),
270
+ lastName: e.target.nome?.value?.split(' ').slice(1).join(' ')?.trim(),
271
+
272
+ // Contexto
273
+ contentName: '[NOME DO EMPREENDIMENTO]',
274
+ contentCategory: 'imovel',
275
+ intentionLevel: 'comprador',
276
+ funnel_stage: 'lead_form',
277
+
278
+ // UTMs capturados pelo SDK automaticamente
279
+ // Coordenadas do imóvel para distância geoespacial no Worker:
280
+ property_lat: -23.5505,
281
+ property_lng: -46.6333,
282
+ });
283
+
284
+ // Redirecionar para obrigado
285
+ window.location.href = '/obrigado';
286
+ });
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 📊 LTV por Evento — O que o Worker calcula
292
+
293
+ | Evento | Bonus LTV Score | Multiplicador Valor |
294
+ |---|---|---|
295
+ | `Lead` (utm_source=facebook, intention=comprador) | ~65–80 pts | 3.5× → **High** |
296
+ | `CustomizeProduct` | +15 pts automático | Score sobe para High |
297
+ | `FindLocation` | +10 pts automático | Puxa Medium → High |
298
+ | `AddToWishlist` | +8 pts automático | Sinal de retargeting |
299
+ | `Schedule` (visita confirmada) | intention=comprador → +20 pts | Máximo High |
300
+ | `Contact` (WhatsApp) | Sem LTV (evento de sinal) | — |
301
+
302
+ ---
303
+
304
+ ## 🔄 FLUXO COMPLETO DO LEAD IMOBILIÁRIO
305
+
306
+ ```
307
+ Usuário chega na landing
308
+
309
+ ├─ PageView → sinal de alcance
310
+ ├─ ViewContent (galeria) → interesse qualificado
311
+ ├─ video_50 (tour) → engajamento profundo
312
+ ├─ FindLocation (mapa) → intenção de visita física (+10 LTV)
313
+ ├─ CustomizeProduct (simulador) → intenção máxima (+15 LTV)
314
+ ├─ Contact (WhatsApp) → ação de contato
315
+ ├─ Schedule (agendamento) → visita confirmada
316
+ └─ Lead (formulário) → conversão principal
317
+
318
+
319
+ Worker: LTV score ~75-90 → High → valor 3.5× injetado
320
+
321
+
322
+ Meta CAPI + GA4 + TikTok recebem Lead com value=R$689
323
+
324
+
325
+ Algoritmo de Meta aprende a buscar mais leads de alto valor
326
+ ```
327
+
328
+ ---
329
+
330
+ ## 📋 CHECKLIST DE IMPLEMENTAÇÃO
331
+
332
+ - [ ] SDK `cdpTrack.js` carregando no `<head>`
333
+ - [ ] `PageView` disparando (automático)
334
+ - [ ] `ViewContent` na galeria
335
+ - [ ] `FindLocation` no mapa e botão "Como chegar"
336
+ - [ ] Vídeo/tour instrumentado (`video_25`, `video_75`, `video_complete`)
337
+ - [ ] `CustomizeProduct` no simulador de financiamento
338
+ - [ ] `AddToWishlist` no botão de favoritar
339
+ - [ ] `Contact` nos botões de WhatsApp e telefone
340
+ - [ ] `Schedule` no calendário (Calendly ou formulário próprio)
341
+ - [ ] `Lead` no formulário principal com PII e `property_lat/lng`
342
+ - [ ] Verificar `/health` retornando `d1: ok, kv: ok, ai: ok`
343
+ - [ ] Testar evento `Lead` no Meta Events Manager → Test Events
344
+ - [ ] Confirmar LTV na resposta JSON do `/track` (`class: "High"`)