cdp-edge 1.23.2 → 1.24.0

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 (42) hide show
  1. package/README.md +82 -21
  2. package/bin/cdp-edge.js +10 -1
  3. package/contracts/agent-versions.json +42 -41
  4. package/contracts/types.ts +81 -0
  5. package/dist/commands/install.js +6 -1
  6. package/dist/commands/server.js +4 -4
  7. package/docs/whatsapp-ctwa.md +3 -2
  8. package/extracted-skill/tracking-events-generator/agents/database-agent.md +5 -4
  9. package/extracted-skill/tracking-events-generator/agents/fraud-detection-agent.md +0 -1
  10. package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +1 -1
  11. package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +4 -4
  12. package/extracted-skill/tracking-events-generator/agents/ml-clustering-agent.md +81 -70
  13. package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +6 -2
  14. package/extracted-skill/tracking-events-generator/cdpTrack.js +7 -0
  15. package/extracted-skill/tracking-events-generator/models/lancamento-imobiliario.md +344 -0
  16. package/extracted-skill/tracking-events-generator/route-intent-capture.js +222 -0
  17. package/package.json +9 -5
  18. package/server-edge-tracker/INSTALAR.md +5 -5
  19. package/server-edge-tracker/{index.js → index.ts} +186 -72
  20. package/server-edge-tracker/modules/{db.js → db.ts} +180 -69
  21. package/server-edge-tracker/modules/dispatch/{ga4.js → ga4.ts} +12 -10
  22. package/server-edge-tracker/modules/dispatch/meta.ts +138 -0
  23. package/server-edge-tracker/modules/dispatch/{platforms.js → platforms.ts} +58 -56
  24. package/server-edge-tracker/modules/dispatch/{tiktok.js → tiktok.ts} +22 -20
  25. package/server-edge-tracker/modules/dispatch/{whatsapp.js → whatsapp.ts} +59 -25
  26. package/server-edge-tracker/modules/{intelligence.js → intelligence.ts} +175 -60
  27. package/server-edge-tracker/modules/ml/{bidding.js → bidding.ts} +37 -35
  28. package/server-edge-tracker/modules/ml/{fraud.js → fraud.ts} +49 -56
  29. package/server-edge-tracker/modules/ml/{logistic.js → logistic.ts} +44 -19
  30. package/server-edge-tracker/modules/ml/{ltv.js → ltv.ts} +179 -83
  31. package/server-edge-tracker/modules/ml/{matchquality.js → matchquality.ts} +70 -26
  32. package/server-edge-tracker/modules/ml/segmentation.ts +407 -0
  33. package/server-edge-tracker/modules/utils.ts +186 -0
  34. package/server-edge-tracker/schema-ltv-feedback.sql +11 -0
  35. package/server-edge-tracker/types.ts +251 -0
  36. package/server-edge-tracker/wrangler.toml +24 -6
  37. package/templates/lancamento-imobiliario.md +344 -0
  38. package/docs/PixelBuilder-Documentacao-Completa (2).docx +0 -0
  39. package/server-edge-tracker/modules/dispatch/meta.js +0 -119
  40. package/server-edge-tracker/modules/ml/segmentation.js +0 -316
  41. package/server-edge-tracker/modules/utils.js +0 -89
  42. package/server-edge-tracker/worker.js +0 -4577
@@ -0,0 +1,186 @@
1
+ /**
2
+ * CDP Edge — Utilities
3
+ * Funções puras sem dependências externas.
4
+ * Importadas por todos os outros módulos.
5
+ */
6
+
7
+ // ── Tipos ───────────────────────────────────────────────────────────────────────
8
+ export interface FunnelStageResult {
9
+ depth: string;
10
+ funnelDepth: string;
11
+ }
12
+
13
+ export interface MetaSignalWeights {
14
+ intent: number;
15
+ ltv: number;
16
+ dist: number;
17
+ }
18
+
19
+ export type DistanceBucket = 'very_close' | 'close' | 'nearby' | 'moderate' | 'far';
20
+
21
+ export type FunnelLevel = 'top' | 'mid' | 'bottom' | 'conversion' | 'unknown';
22
+
23
+ export type MetaSignalBucket = 'hot' | 'warm' | 'cold';
24
+
25
+ // ── CORS ──────────────────────────────────────────────────────────────────────
26
+ export function isAllowedOrigin(origin: string | null, siteDomain: string | null): boolean {
27
+ if (!origin || !siteDomain) return false;
28
+ return origin === `https://${siteDomain}`
29
+ || origin.endsWith(`.${siteDomain}`)
30
+ || origin === 'http://localhost:3000'
31
+ || origin === 'http://localhost:5173';
32
+ }
33
+
34
+ export function corsHeaders(origin: string | null, siteDomain: string | null): Record<string, string> {
35
+ const allowed = isAllowedOrigin(origin, siteDomain) ? origin : (siteDomain ? `https://${siteDomain}` : '*');
36
+ return {
37
+ 'Access-Control-Allow-Origin': allowed || '*',
38
+ 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
39
+ 'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With',
40
+ 'Access-Control-Max-Age': '86400',
41
+ };
42
+ }
43
+
44
+ // ── SHA-256 via WebCrypto (obrigatório no Cloudflare Workers) ─────────────────
45
+ export async function sha256(value: string | null | undefined): Promise<string | undefined> {
46
+ if (!value) return undefined;
47
+ const clean = String(value).toLowerCase().trim();
48
+ if (!clean) return undefined;
49
+ const buf = await crypto.subtle.digest(
50
+ 'SHA-256',
51
+ new TextEncoder().encode(clean)
52
+ );
53
+ return Array.from(new Uint8Array(buf))
54
+ .map(b => b.toString(16).padStart(2, '0'))
55
+ .join('');
56
+ }
57
+
58
+ // ── Normalização de telefone → somente dígitos + DDI 55 ──────────────────────
59
+ export function normalizePhone(phone: string | null | undefined): string | undefined {
60
+ if (!phone) return undefined;
61
+ let digits = String(phone).replace(/\D/g, '');
62
+ if (digits.length === 11 && !digits.startsWith('55')) digits = '55' + digits;
63
+ if (digits.length === 10 && !digits.startsWith('55')) digits = '55' + digits;
64
+ return digits.length >= 10 ? digits : undefined;
65
+ }
66
+
67
+ // ── Normalização de cidade → lowercase sem acentos ────────────────────────────
68
+ export function normalizeCity(city: string | null | undefined): string | undefined {
69
+ if (!city) return undefined;
70
+ return String(city)
71
+ .toLowerCase()
72
+ .normalize('NFD')
73
+ .replace(/[\u0300-\u036f]/g, '')
74
+ .replace(/[^a-z0-9]/g, '');
75
+ }
76
+
77
+ // ── Parse seguro de JSON armazenado como TEXT no D1 ───────────────────────────
78
+ export function tryParseJson<T = any>(str: string | null, fallback?: T): T | null {
79
+ if (!str) return fallback !== undefined ? fallback : null;
80
+ try { return JSON.parse(str); } catch { return fallback !== undefined ? fallback : null; }
81
+ }
82
+
83
+ // ── Mapa Meta → GA4 event names ───────────────────────────────────────────────
84
+ export const META_TO_GA4: Record<string, string> = {
85
+ PageView: 'page_view',
86
+ ViewContent: 'view_item',
87
+ Lead: 'generate_lead',
88
+ Contact: 'generate_lead',
89
+ Schedule: 'generate_lead',
90
+ InitiateCheckout: 'begin_checkout',
91
+ AddToCart: 'add_to_cart',
92
+ AddPaymentInfo: 'add_payment_info',
93
+ Purchase: 'purchase',
94
+ CompleteRegistration: 'sign_up',
95
+ Subscribe: 'subscribe',
96
+ StartTrial: 'start_trial',
97
+ Search: 'search',
98
+ AddToWishlist: 'add_to_wishlist',
99
+ };
100
+
101
+ // ── Lista canônica de eventos válidos (19 eventos) ────────────────────────────
102
+ export const VALID_EVENT_NAMES = new Set([
103
+ 'PageView','ViewContent','Lead','Purchase','InitiateCheckout',
104
+ 'AddToCart','CompleteRegistration','Contact','Schedule',
105
+ 'StartTrial','Subscribe','SubmitApplication','Search',
106
+ 'video_start','video_25','video_50','video_75','video_complete',
107
+ // Imóveis — intenção de visita física, financiamento e favoritar
108
+ 'FindLocation','CustomizeProduct','AddToWishlist',
109
+ ]);
110
+
111
+ // ── Taxonomia de funil (funnel_stage → profundidade semântica) ────────────────
112
+ // Fonte de verdade para interpretar funnel_stage em qualquer ponto do sistema.
113
+ export const FUNNEL_TAXONOMY = {
114
+ top: ['scroll_50', 'time_30s', 'page_view', 'gallery_view', 'AddToWishlist'],
115
+ mid: ['map_view', 'gallery_click', 'price_hover', 'time_3min', 'FindLocation'],
116
+ bottom: ['route_click', 'whatsapp_click', 'cta_hover', 'CustomizeProduct'],
117
+ conversion: ['schedule_confirmed', 'lead_form', 'purchase', 'visit_booked'],
118
+ };
119
+
120
+ // Índice invertido: funnel_stage → depth (construído uma vez, zero custo em runtime)
121
+ const _STAGE_TO_DEPTH: Record<string, FunnelLevel> = Object.entries(FUNNEL_TAXONOMY).reduce((acc, [depth, stages]) => {
122
+ stages.forEach(s => { acc[s] = depth as FunnelLevel; });
123
+ return acc;
124
+ }, {} as Record<string, FunnelLevel>);
125
+
126
+ /**
127
+ * Resolve funnel_stage em funnelDepth semântico.
128
+ * bottom_intent = intenção forte (route_click, whatsapp_click)
129
+ * bottom_conversion = ação confirmada (schedule_confirmed, lead_form)
130
+ */
131
+ export function resolveFunnelStage(funnel_stage: string | null | undefined): FunnelStageResult {
132
+ const depth = _STAGE_TO_DEPTH[funnel_stage || ''] || 'unknown';
133
+ const funnelDepth = depth === 'conversion' ? 'bottom_conversion'
134
+ : depth === 'bottom' ? 'bottom_intent'
135
+ : depth;
136
+ return { depth, funnelDepth };
137
+ }
138
+
139
+ // ── Normalização de intent_score → 0.0–1.0 ───────────────────────────────────
140
+ // Aceita: string ('high'/'medium'/'low'), numérico 0-1 ou numérico 0-100
141
+ const _INTENT_STRING_MAP: Record<string, number> = { high: 0.92, medium: 0.65, low: 0.30 };
142
+
143
+ export function resolveIntentScore(value: string | number | null | undefined): number | null {
144
+ if (value === null || value === undefined) return null;
145
+ if (typeof value === 'string') return _INTENT_STRING_MAP[value.toLowerCase()] ?? null;
146
+ const num = parseFloat(String(value));
147
+ if (isNaN(num)) return null;
148
+ const normalized = num > 1 ? num / 100 : num; // escala 0-100 → 0-1
149
+ return Math.min(1, Math.max(0, Math.round(normalized * 100) / 100));
150
+ }
151
+
152
+ /**
153
+ * Distância (distanceBucket) → peso numérico para meta_signal.
154
+ * very_close=1.0 ... far=0.1 ... sem dado=0.3 (neutro)
155
+ */
156
+ export function distanceBucketWeight(bucket: string | null | undefined): number {
157
+ const map: Record<DistanceBucket, number> = { very_close: 1.0, close: 0.75, nearby: 0.5, moderate: 0.25, far: 0.1 };
158
+ return map[bucket as DistanceBucket] ?? 0.3;
159
+ }
160
+
161
+ /**
162
+ * Pesos dinâmicos do meta_signal por profundidade de funil.
163
+ * Fundo: comportamento pesa mais (intent + dist).
164
+ * Topo: perfil pesa mais (ltv).
165
+ * Default (mid/unknown): balanceado.
166
+ */
167
+ export function computeMetaSignalWeights(funnelLevel: FunnelLevel | string | null | undefined): MetaSignalWeights {
168
+ if (funnelLevel === 'bottom' || funnelLevel === 'conversion') {
169
+ return { intent: 0.5, ltv: 0.2, dist: 0.3 };
170
+ }
171
+ if (funnelLevel === 'top') {
172
+ return { intent: 0.2, ltv: 0.6, dist: 0.2 };
173
+ }
174
+ return { intent: 0.4, ltv: 0.4, dist: 0.2 };
175
+ }
176
+
177
+ /**
178
+ * Quantiza meta_signal contínuo em bucket legível.
179
+ * Usado em criação de públicos e leitura de BI.
180
+ */
181
+ export function metaSignalBucket(score: number | null | undefined): MetaSignalBucket {
182
+ if (!score) return 'cold';
183
+ if (score >= 0.8) return 'hot';
184
+ if (score >= 0.6) return 'warm';
185
+ return 'cold';
186
+ }
@@ -0,0 +1,11 @@
1
+ -- CDP Edge — Schema LTV Feedback Loop
2
+ -- Fecha o ciclo preditivo: Purchase real → corrige predicted_ltv_value
3
+ -- Execução: wrangler d1 execute cdp-edge-db --file=schema-ltv-feedback.sql --remote
4
+ --
5
+ -- Idempotência: ALTER TABLE não suporta IF NOT EXISTS no SQLite.
6
+ -- Se a coluna já existir, o comando gera erro mas não afeta dados existentes.
7
+ -- Seguro executar mais de uma vez.
8
+
9
+ ALTER TABLE user_profiles ADD COLUMN real_ltv_value REAL;
10
+ ALTER TABLE user_profiles ADD COLUMN ltv_accuracy REAL; -- 1 - |pred-real|/real (0–1, maior = melhor)
11
+ ALTER TABLE user_profiles ADD COLUMN ltv_feedback_at TEXT; -- timestamp do último feedback
@@ -0,0 +1,251 @@
1
+ /**
2
+ * CDP Edge — Server Types
3
+ * Tipos para o Cloudflare Worker e bindings
4
+ */
5
+
6
+ import { D1Database, KVNamespace, R2Bucket } from '@cloudflare/workers-types';
7
+
8
+ // ── Environment Bindings ─────────────────────────────────────────────────────
9
+ export interface Env {
10
+ // D1 Database
11
+ DB?: D1Database;
12
+
13
+ // KV Namespace
14
+ GEO_CACHE?: KVNamespace;
15
+
16
+ // R2 Bucket
17
+ AUDIT_LOGS?: R2Bucket;
18
+
19
+ // Workers AI
20
+ AI?: any;
21
+
22
+ // Rate Limiter
23
+ RATE_LIMITER?: any;
24
+
25
+ // Public Variables
26
+ META_PIXEL_ID?: string;
27
+ GA4_MEASUREMENT_ID?: string;
28
+ TIKTOK_PIXEL_ID?: string;
29
+ SITE_DOMAIN?: string;
30
+
31
+ // Secrets
32
+ META_ACCESS_TOKEN?: string;
33
+ GA4_API_SECRET?: string;
34
+ TIKTOK_ACCESS_TOKEN?: string;
35
+ WA_WEBHOOK_VERIFY_TOKEN?: string;
36
+ WHATSAPP_ACCESS_TOKEN?: string;
37
+ WHATSAPP_PHONE_NUMBER_ID?: string;
38
+ WA_NOTIFY_NUMBER?: string;
39
+ CALLMEBOT_PHONE?: string;
40
+ WEBHOOK_SECRET_TICTO?: string;
41
+ WEBHOOK_SECRET_HOTMART?: string;
42
+ WEBHOOK_SECRET_KIWIFY?: string;
43
+ META_TEST_CODE?: string;
44
+ META_AD_ACCOUNT_ID?: string;
45
+ META_AUDIENCE_ID?: string;
46
+ PINTEREST_ACCESS_TOKEN?: string;
47
+ PINTEREST_AD_ACCOUNT_ID?: string;
48
+ REDDIT_ACCESS_TOKEN?: string;
49
+ REDDIT_AD_ACCOUNT_ID?: string;
50
+ LINKEDIN_ACCESS_TOKEN?: string;
51
+ LINKEDIN_CONVERSION_ID?: string;
52
+ LINKEDIN_AD_ACCOUNT_ID?: string;
53
+ SPOTIFY_ACCESS_TOKEN?: string;
54
+ SPOTIFY_AD_ACCOUNT_ID?: string;
55
+
56
+ // Email and Notification
57
+ RESEND_API_KEY?: string;
58
+ RESEND_FROM_EMAIL?: string;
59
+ CALLMEBOT_APIKEY?: string;
60
+ }
61
+
62
+ // ── Event Payload Types ───────────────────────────────────────────────────────
63
+ export interface TrackPayload {
64
+ eventName?: string;
65
+ eventId?: string;
66
+ event_id?: string;
67
+ userId?: string;
68
+ email?: string | null;
69
+ phone?: string | null;
70
+ firstName?: string | null;
71
+ lastName?: string | null;
72
+ city?: string | null;
73
+ state?: string | null;
74
+ zip?: string | null;
75
+ country?: string | null;
76
+ dob?: string | null;
77
+
78
+ // Identifiers
79
+ fbp?: string | null;
80
+ fbc?: string | null;
81
+ ttp?: string | null;
82
+ gaClientId?: string | null;
83
+
84
+ // Parameters
85
+ value?: number | null;
86
+ currency?: string | null;
87
+ contentIds?: string[] | null;
88
+ contentName?: string | null;
89
+ contentType?: string | null;
90
+ pageUrl?: string | null;
91
+ orderId?: string | null;
92
+ productName?: string | null;
93
+
94
+ // Quantum Tracking Details
95
+ intent_score?: string | number | null;
96
+ intentScoreNum?: number | null;
97
+ intent_bucket?: string | null;
98
+ intent_penalized?: boolean;
99
+ metaSignal?: number | null;
100
+ metaSignalBucket?: string | null;
101
+ distanceBucket?: string | null;
102
+ distanceKm?: number | null;
103
+ funnel_stage?: string | null;
104
+ funnelDepth?: string | null;
105
+ funnelLevel?: string | null;
106
+ internalEvent?: string | null;
107
+ botScore?: number | null;
108
+
109
+ // Real Estate
110
+ property_lat?: string | number | null;
111
+ propertyLat?: string | number | null;
112
+ property_lng?: string | number | null;
113
+ propertyLng?: string | number | null;
114
+
115
+ // Engagement
116
+ engagementScore?: number | null;
117
+ intentionLevel?: string | null;
118
+ userScore?: number | null;
119
+ scrollScore?: number | null;
120
+ timeLevel?: string | null;
121
+
122
+ // UTM
123
+ utmSource?: string | null;
124
+ utmMedium?: string | null;
125
+ utmCampaign?: string | null;
126
+ utmContent?: string | null;
127
+ utmTerm?: string | null;
128
+ utmRestored?: boolean;
129
+
130
+ // LTV
131
+ ltvClass?: string | null;
132
+ ltvScore?: number | null;
133
+
134
+ // Additional fields
135
+ [key: string]: any;
136
+ }
137
+
138
+ export interface BehavioralData {
139
+ engagement_score?: number;
140
+ totalScore?: number;
141
+ intention_level?: string;
142
+ user_score?: number;
143
+ scroll_score?: number;
144
+ time_level?: string;
145
+ email?: string | null;
146
+ phone?: string | null;
147
+ first_name?: string | null;
148
+ firstName?: string | null;
149
+ last_name?: string | null;
150
+ lastName?: string | null;
151
+ city?: string | null;
152
+ state?: string | null;
153
+ zip?: string | null;
154
+ dob?: string | null;
155
+ }
156
+
157
+ // ── Webhook Types ─────────────────────────────────────────────────────────────
158
+ export interface HotmartWebhook {
159
+ data?: {
160
+ buyer?: {
161
+ email?: string;
162
+ phone?: string;
163
+ name?: string;
164
+ };
165
+ purchase?: {
166
+ status?: string;
167
+ transaction?: string;
168
+ price?: {
169
+ value?: number;
170
+ currency_value?: string;
171
+ };
172
+ };
173
+ product?: {
174
+ id?: string;
175
+ ucode?: string;
176
+ name?: string;
177
+ };
178
+ };
179
+ [key: string]: any;
180
+ }
181
+
182
+ export interface KiwifyWebhook {
183
+ order_status?: string;
184
+ order_id?: string;
185
+ order_value?: string;
186
+ Customer?: {
187
+ email?: string;
188
+ mobile?: string;
189
+ full_name?: string;
190
+ };
191
+ Product?: {
192
+ product_id?: string;
193
+ product_name?: string;
194
+ };
195
+ [key: string]: any;
196
+ }
197
+
198
+ export interface TictoWebhook {
199
+ status?: string;
200
+ customer?: {
201
+ email?: string;
202
+ phone?: string;
203
+ name?: string;
204
+ };
205
+ order?: {
206
+ hash?: string;
207
+ transaction_hash?: string;
208
+ id?: string;
209
+ paid_amount?: number;
210
+ total?: number;
211
+ amount?: number;
212
+ };
213
+ item?: {
214
+ product_id?: string;
215
+ product_name?: string;
216
+ };
217
+ tracking?: {
218
+ user_id?: string;
219
+ fbclid?: string;
220
+ utm_source?: string;
221
+ src?: string;
222
+ utm_medium?: string;
223
+ utm_campaign?: string;
224
+ utm_content?: string;
225
+ };
226
+ url_params?: {
227
+ user_id?: string;
228
+ fbclid?: string;
229
+ utm_source?: string;
230
+ src?: string;
231
+ utm_medium?: string;
232
+ utm_campaign?: string;
233
+ utm_content?: string;
234
+ };
235
+ [key: string]: any;
236
+ }
237
+
238
+ // ── Queue Message Types ───────────────────────────────────────────────────────
239
+ export interface QueueMessage {
240
+ eventType: string;
241
+ payload: TrackPayload;
242
+ platform: string;
243
+ attempt?: number;
244
+ }
245
+
246
+ // ── Promise Settled Result Helpers ─────────────────────────────────────────────
247
+ export interface PromiseResult<T> {
248
+ status: 'fulfilled' | 'rejected';
249
+ value?: T;
250
+ reason?: Error | string;
251
+ }
@@ -1,9 +1,10 @@
1
1
  name = "server-edge-tracker"
2
2
  # Entry point: worker.js (monólito original, 100% compatível)
3
- # Para usar a versão modular ES Modules: altere para main = "index.js"
4
- main = "index.js"
3
+ # Para usar a versão modular ES Modules: altere para main = "index.ts"
4
+ main = "index.ts"
5
5
  compatibility_date = "2025-01-01"
6
6
  compatibility_flags = ["nodejs_compat"]
7
+ workers_dev = true
7
8
 
8
9
  # ── Worker Routes — same-domain tracking (imune a bloqueios) ─────────────────
9
10
  # Substituir SEU_DOMINIO pelo domínio do cliente antes do deploy
@@ -25,10 +26,10 @@ zone_name = "lancamentosabc.com.br"
25
26
 
26
27
  # ── Variáveis públicas (não são segredos) ─────────────────────────────────────
27
28
  [vars]
28
- META_PIXEL_ID = "SEU_META_PIXEL_ID"
29
- GA4_MEASUREMENT_ID = "G-XXXXXXXXXX"
30
- TIKTOK_PIXEL_ID = "CXXXXXXXXXXXXXXX"
31
- SITE_DOMAIN = "SEU_DOMINIO"
29
+ META_PIXEL_ID = ""
30
+ GA4_MEASUREMENT_ID = ""
31
+ TIKTOK_PIXEL_ID = ""
32
+ SITE_DOMAIN = "lancamentosabc.com.br"
32
33
 
33
34
  # ── Banco D1 ──────────────────────────────────────────────────────────────────
34
35
  # Após criar o banco com "wrangler d1 create cdp-edge-db",
@@ -95,6 +96,22 @@ namespace_id = "1001"
95
96
  limit = 60
96
97
  period = 60
97
98
 
99
+ # ── Observabilidade — Logs + Traces persistidos no painel Cloudflare ─────────
100
+ [observability]
101
+ enabled = false
102
+ head_sampling_rate = 1
103
+
104
+ [observability.logs]
105
+ enabled = true
106
+ head_sampling_rate = 1
107
+ persist = true
108
+ invocation_logs = true
109
+
110
+ [observability.traces]
111
+ enabled = false
112
+ persist = true
113
+ head_sampling_rate = 1
114
+
98
115
  # ── Secrets (NÃO ficam aqui — configurar via CLI) ─────────────────────────────
99
116
  # wrangler secret put META_ACCESS_TOKEN ← token Meta CAPI (obrigatório)
100
117
  # wrangler secret put GA4_API_SECRET ← secret GA4 Measurement Protocol (obrigatório)
@@ -107,6 +124,7 @@ period = 60
107
124
  # wrangler secret put RESEND_API_KEY ← API Key do Resend (resend.com)
108
125
  # wrangler secret put RESEND_FROM_EMAIL ← Remetente verificado ex: "CDP Edge <noreply@seudominio.com.br>"
109
126
  # wrangler secret put WA_WEBHOOK_VERIFY_TOKEN ← Token de verificação do webhook WhatsApp (você define — qualquer string segura)
127
+ # wrangler secret put WEBHOOK_SECRET_TICTO ← HMAC-SHA256 Ticto
110
128
  # wrangler secret put PINTEREST_ACCESS_TOKEN ← Bearer token Pinterest Conversions API
111
129
  # wrangler secret put PINTEREST_AD_ACCOUNT_ID ← ID da conta de anúncios Pinterest (ex: 549755813XXX)
112
130
  # wrangler secret put REDDIT_ACCESS_TOKEN ← Bearer token Reddit Conversions API