cdp-edge 2.5.3 → 2.5.5
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.
- package/README.md +20 -130
- package/contracts/agent-versions.json +12 -9
- package/extracted-skill/tracking-events-generator/agents/evo-crm-agent.md +244 -0
- package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +11 -5
- package/extracted-skill/tracking-events-generator/cdpTrack.js +4 -2
- package/package.json +1 -1
- package/server-edge-tracker/INSTALAR.md +2 -2
- package/server-edge-tracker/index.ts +68 -2
- package/server-edge-tracker/modules/db.ts +9 -1
- package/server-edge-tracker/modules/dispatch/crm.ts +382 -0
- package/server-edge-tracker/modules/dispatch/whatsapp.ts +17 -0
- package/server-edge-tracker/schema.sql +16 -0
- package/server-edge-tracker/types.ts +17 -0
- package/server-edge-tracker/wrangler.toml +11 -7
- package/docs/installation.md +0 -155
- package/docs/quick-start.md +0 -185
- package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +0 -1459
package/README.md
CHANGED
|
@@ -1,129 +1,21 @@
|
|
|
1
1
|
# 🚀 CDP Edge — Quantum Tracking Enterprise
|
|
2
|
-
|
|
3
2
|
**Padrão Quantum Tracking: 100% Cloudflare Edge.** Sem GTM. Sem Stape. Sem cookies de terceiros.
|
|
4
3
|
|
|
5
|
-
> **v2.5.
|
|
6
|
-
> **v2.5.1** — SDK Bundle + Diagnóstico Pós-Deploy + Gap Fixes (25 de Abril de 2026) 🛠️
|
|
7
|
-
> **v2.3.9** — Quiz Scoring Engine + Sales Engine (14 de Abril de 2026) 🤖💰
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 📋 CHANGELOG v2.5.3 — Auditoria Completa + Sync Total de Agentes + Fix D1 (25 de Abril de 2026) 🔧
|
|
12
|
-
|
|
13
|
-
### Bug Fix Crítico
|
|
14
|
-
- `schema-ltv-feedback.sql` adicionado à ordem de migração — `recordLtvFeedback()` falhava silenciosamente em todo Purchase, quebrando o ciclo preditivo LTV
|
|
15
|
-
|
|
16
|
-
### Fix de Compatibilidade D1
|
|
17
|
-
- `schema-utm.sql`: `ADD COLUMN IF NOT EXISTS` → `ADD COLUMN` — D1/SQLite não suporta IF NOT EXISTS em ALTER TABLE
|
|
18
|
-
|
|
19
|
-
### Sincronização Total de Agentes
|
|
20
|
-
- `contracts/agent-versions.json`: worker_version `2.2.3 → 2.5.3`, 49 agentes registrados (eram 26), `depends_on` `.js → .ts`, event count `19 → 25`, table count `24 → 31`
|
|
21
|
-
- `database-agent.md`: `main = "worker.js" → "index.ts"`, 24 → 31 tabelas, roadmap atualizado
|
|
22
|
-
- `devops-agent.md`: `RESEND_API_KEY`, `RESEND_FROM_EMAIL`, `WEBHOOK_SECRET_TICTO` adicionados ao `*secrets`
|
|
23
|
-
|
|
24
|
-
### D1 Produção
|
|
25
|
-
- 31 tabelas aplicadas em `cdp-edge-db` — todos os schemas confirmados
|
|
26
|
-
- Worker deployado: Version ID `37776c1a` — `/health` status: `ok`
|
|
4
|
+
> **v2.5.5** — Integração EVO CRM (OAuth2) + Sync Total de Agentes (27 de Abril de 2026) 🔧
|
|
27
5
|
|
|
28
6
|
---
|
|
29
7
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
### SDK Bundle — cdpTrack.min.js pronto para `<script src="">`
|
|
33
|
-
- `npm run sdk:build` → `dist/sdk/cdpTrack.min.js` (41.6 kB, IIFE, `window.cdpTrack`)
|
|
34
|
-
- Um único arquivo sem dependências externas — cola no site e funciona
|
|
35
|
-
- `dist/sdk/install-snippet.html` com snippet pronto para antes do `</body>`
|
|
36
|
-
|
|
37
|
-
### Diagnóstico Pós-Deploy — `cdp-edge validate <url>`
|
|
38
|
-
```bash
|
|
39
|
-
cdp-edge validate https://meusite.com.br
|
|
40
|
-
cdp-edge validate https://meusite.com.br --worker https://worker.meusite.workers.dev
|
|
41
|
-
```
|
|
42
|
-
Verifica automaticamente:
|
|
43
|
-
- Página carrega `cdpTrack.min.js` e Meta Pixel
|
|
44
|
-
- Worker `/health` (D1, KV, AI, secrets)
|
|
45
|
-
- Worker `/validate-install` (D1 write+read, KV, AI, secrets críticos)
|
|
46
|
-
- `POST /track` aceita evento sintético → 200
|
|
47
|
-
|
|
48
|
-
### Gap Fixes Críticos
|
|
49
|
-
- **Endpoint**: `/api/tracking` → `/track` em todos os módulos SDK
|
|
50
|
-
- **utm_term**: injetado pelo Worker após quiz scoring (nunca pelo cliente)
|
|
51
|
-
- **TikTok ttp**: cookie `_ttp` capturado e enviado ao Worker
|
|
52
|
-
- **ROAS por origem**: segmentado por `utm_content` (quiz_* vs video_* vs landing_* vs ctwa_*)
|
|
53
|
-
|
|
54
|
-
### Novos Agentes
|
|
55
|
-
- `lead-scoring-agent.md` — quiz de qualificação + análise dimensional Granite
|
|
56
|
-
- `match-quality-agent.md` — guardião EMQ com cron 2h e alerta CallMeBot
|
|
57
|
-
|
|
58
|
-
---
|
|
8
|
+
## 💻 INSTALAÇÃO
|
|
59
9
|
|
|
60
|
-
## 📋 CHANGELOG v2.3.9 — Quiz Scoring Engine + Sales Engine (14 de Abril de 2026) 🤖💰
|
|
61
|
-
|
|
62
|
-
### O que foi adicionado
|
|
63
|
-
|
|
64
|
-
**ROAS Feedback Loop** (`modules/ml/roas.ts`)
|
|
65
|
-
- Cruza leads com compras reais por campanha no D1
|
|
66
|
-
- Calcula: `conversion_rate`, `revenue_per_lead`, `ltv_accuracy` (valida o modelo preditivo)
|
|
67
|
-
- Recomendação automática de bid: `increase | maintain | decrease | pause`
|
|
68
|
-
- Relatório semanal via CallMeBot com top campanhas e alertas
|
|
69
|
-
- Persiste histórico em `roas_reports` + VIEW `v_roas_latest`
|
|
70
|
-
|
|
71
|
-
**Nurture Engine** (`modules/nurture.ts`)
|
|
72
|
-
- Sequências automáticas pós-quiz por qualificação:
|
|
73
|
-
- `interessado` → D+1, D+3, D+7 (WhatsApp)
|
|
74
|
-
- `curioso` → D+2, D+5 (conteúdo)
|
|
75
|
-
- `comprador` → contato imediato via hot lead (já existente)
|
|
76
|
-
- `perdido` → `cohort_label = excluded` (remove do remarketing)
|
|
77
|
-
- `scheduleNurture()` chamado no QuizComplete em background
|
|
78
|
-
- `runNurtureQueue()` executado pelo Intelligence Agent (cron diário)
|
|
79
|
-
- D1: `nurture_sequences` + VIEWs de fila e stats
|
|
80
|
-
|
|
81
|
-
**Lookalike Dinâmico** (`syncMetaLookalikeSeed()` em intelligence.ts)
|
|
82
|
-
- Seed de Lookalike com compradores CONFIRMADOS (Purchase event) — não apenas high_intent
|
|
83
|
-
- Une compradores reais + leads qualificados como `comprador` no quiz
|
|
84
|
-
- Atualiza `cohort_label = buyer_confirmed` nos perfis
|
|
85
|
-
- Persiste histórico em `lookalike_seeds`
|
|
86
|
-
- Roda automaticamente no cron semanal do Intelligence Agent
|
|
87
|
-
|
|
88
|
-
**Schema D1**: `schema-sales-engine.sql` — `roas_reports`, `nurture_sequences`, `lookalike_seeds` + 5 VIEWs
|
|
89
|
-
|
|
90
|
-
### Migração
|
|
91
10
|
```bash
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
11
|
+
npx cdp-edge install
|
|
12
|
+
# ou
|
|
13
|
+
npm install -g cdp-edge
|
|
14
|
+
cdp-edge install . --name "Nome do Projeto"
|
|
95
15
|
```
|
|
96
16
|
|
|
97
17
|
---
|
|
98
18
|
|
|
99
|
-
## 📋 CHANGELOG v2.4.0 — Quiz Scoring Engine (14 de Abril de 2026) 🤖
|
|
100
|
-
|
|
101
|
-
**Quiz Scoring Engine** (`modules/ml/quiz.ts`) — análise dimensional automática via Granite 4.0 Micro:
|
|
102
|
-
- Detecta o TIPO de cada pergunta: `urgency | budget | timeline | fit | engagement | awareness | objection`
|
|
103
|
-
- Atribui peso automático por dimensão (budget/urgency=5, fit=4, timeline=3, engagement=2)
|
|
104
|
-
- Score ponderado → `comprador | interessado | curioso | perdido`
|
|
105
|
-
- Fallback heurístico por palavras-chave quando AI indisponível
|
|
106
|
-
- `quiz_sessions` no D1 com breakdown completo por dimensão
|
|
107
|
-
|
|
108
|
-
**Integração no pipeline `/track`**:
|
|
109
|
-
- `QuizComplete` + `quiz_answers[]` → scoring antes do LTV
|
|
110
|
-
- `intentionLevel` qualificado alimenta LTV Prediction → `comprador` = LTV High automático
|
|
111
|
-
- `QuizComplete` adicionado ao `LTV_EVENTS`
|
|
112
|
-
|
|
113
|
-
**Migração**: `wrangler d1 execute cdp-edge-db --file=schema-quiz.sql --remote`
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## 📋 CHANGELOG v2.3.8 — Fix Definitivo: api-versions.json (14 de Abril de 2026) 🔒
|
|
118
|
-
|
|
119
|
-
`build.js` copia `extracted-skill/.../contracts/` → `contracts/` no `prepare` script. A fonte estava na v1.0.0, sobrescrevendo o arquivo correto a cada `npm publish`. Ambos os arquivos agora sincronizados em v1.1.0.
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
> ⚠️ **REGRA DE OURO (SQUAD):** Todas as atualizações devem ser documentadas de forma sincronizada em `README.md`, `CLAUDE.md` e `CDP-EDGE-BUSINESS-BOOK.md`. Nenhuma alteração passa sem esse tripé.
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
19
|
## 🤖 MANIFESTO: EU, CDP EDGE
|
|
128
20
|
|
|
129
21
|
*Eu não sou um simples "script de pixel". Eu sou uma arquitetura **Enterprise Tier 10** nascida e construída 100% nativa na infraestrutura da Cloudflare.*
|
|
@@ -161,7 +53,7 @@ Cron semanal (Intelligence Agent):
|
|
|
161
53
|
|
|
162
54
|
---
|
|
163
55
|
|
|
164
|
-
## 📊 BANCO DE DADOS D1 —
|
|
56
|
+
## 📊 BANCO DE DADOS D1 — 31 Tabelas + 12 VIEWs
|
|
165
57
|
|
|
166
58
|
```
|
|
167
59
|
CORE TRACKING ML & INTELLIGENCE SALES ENGINE
|
|
@@ -218,6 +110,7 @@ v_leads_segmented v_ltv_feedback
|
|
|
218
110
|
| Enterprise | `bidding-agent.md` | Bids ML por segmento |
|
|
219
111
|
| Enterprise | `ab-ltv-agent.md` | A/B testing de prompts LTV |
|
|
220
112
|
| Enterprise | `fraud-detection-agent.md` | Detecção de fraude na borda |
|
|
113
|
+
| Infraestrutura | `evo-crm-agent.md` | Roteamento de leads p/ EVO CRM (OAuth2) |
|
|
221
114
|
| Monitoramento | `intelligence-agent.md` | Cron — ROAS, Nurture, Lookalike, LTV training |
|
|
222
115
|
|
|
223
116
|
---
|
|
@@ -230,7 +123,9 @@ v_leads_segmented v_ltv_feedback
|
|
|
230
123
|
| `/track` | POST | Evento principal — Fraud Gate → Quiz Scoring → LTV → CAPI |
|
|
231
124
|
| `/health` | GET | Smoke test D1 + KV + AI |
|
|
232
125
|
| `/webhook/ticto` | POST | Purchase webhook (HMAC) |
|
|
126
|
+
| `/webhook/whatsapp` | POST | Webhook Meta WhatsApp → CRM (Evolution/EVO) |
|
|
233
127
|
| `/export/customer-match` | GET | Export leads para Google Ads |
|
|
128
|
+
| `/validate-install` | GET | Diagnóstico pós-deploy |
|
|
234
129
|
|
|
235
130
|
### ML Clustering (Fase 1)
|
|
236
131
|
| Rota | Método | Função |
|
|
@@ -280,15 +175,19 @@ wrangler d1 execute cdp-edge-db --file=schema-ab-ltv.sql --remote
|
|
|
280
175
|
wrangler d1 execute cdp-edge-db --file=schema-fraud.sql --remote
|
|
281
176
|
wrangler d1 execute cdp-edge-db --file=schema-indexes.sql --remote
|
|
282
177
|
wrangler d1 execute cdp-edge-db --file=migrate-v7.sql --remote
|
|
283
|
-
wrangler d1 execute cdp-edge-db --file=schema-ltv-feedback.sql --remote
|
|
178
|
+
wrangler d1 execute cdp-edge-db --file=schema-ltv-feedback.sql --remote
|
|
284
179
|
wrangler d1 execute cdp-edge-db --file=schema-utm.sql --remote
|
|
285
|
-
wrangler d1 execute cdp-edge-db --file=schema-quiz.sql --remote
|
|
286
|
-
wrangler d1 execute cdp-edge-db --file=schema-sales-engine.sql --remote
|
|
180
|
+
wrangler d1 execute cdp-edge-db --file=schema-quiz.sql --remote
|
|
181
|
+
wrangler d1 execute cdp-edge-db --file=schema-sales-engine.sql --remote
|
|
287
182
|
|
|
288
|
-
# Secrets
|
|
183
|
+
# Secrets (nunca em arquivos — sempre via wrangler secret put)
|
|
289
184
|
wrangler secret put META_ACCESS_TOKEN
|
|
290
185
|
wrangler secret put GA4_API_SECRET
|
|
291
186
|
wrangler secret put TIKTOK_ACCESS_TOKEN
|
|
187
|
+
wrangler secret put EVO_CRM_BASE_URL
|
|
188
|
+
wrangler secret put EVO_CRM_CLIENT_ID
|
|
189
|
+
wrangler secret put EVO_CRM_CLIENT_SECRET
|
|
190
|
+
wrangler secret put EVO_CRM_INBOX_ID
|
|
292
191
|
|
|
293
192
|
# Deploy
|
|
294
193
|
wrangler deploy
|
|
@@ -296,17 +195,6 @@ wrangler deploy
|
|
|
296
195
|
|
|
297
196
|
---
|
|
298
197
|
|
|
299
|
-
## 💻 INSTALAÇÃO
|
|
300
|
-
|
|
301
|
-
```bash
|
|
302
|
-
npx cdp-edge install
|
|
303
|
-
# ou
|
|
304
|
-
npm install -g cdp-edge
|
|
305
|
-
cdp-edge install . --name "Nome do Projeto"
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
198
|
## 🗺️ GRAPHIFY — Navegando o Código
|
|
311
199
|
|
|
312
200
|
```bash
|
|
@@ -316,6 +204,8 @@ cdp-edge install . --name "Nome do Projeto"
|
|
|
316
204
|
/graphify path "quiz_answers" "meta capi" # menor caminho
|
|
317
205
|
```
|
|
318
206
|
|
|
207
|
+
> Changelog completo → [CHANGELOG.md](./CHANGELOG.md)
|
|
208
|
+
|
|
319
209
|
---
|
|
320
210
|
|
|
321
211
|
### **CDP Edge — By Rica Soares**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Fonte de verdade para versões dos agent files. Atualizar quando modules/ ou index.ts mudarem. Use scripts/validate-agents.js para detectar drifts.",
|
|
3
|
-
"worker_version": "2.5.
|
|
4
|
-
"worker_hash_date": "2026-04-
|
|
3
|
+
"worker_version": "2.5.5",
|
|
4
|
+
"worker_hash_date": "2026-04-27",
|
|
5
5
|
"agents": {
|
|
6
6
|
"master-orchestrator": {
|
|
7
7
|
"version": "2.0.7",
|
|
@@ -466,16 +466,19 @@
|
|
|
466
466
|
],
|
|
467
467
|
"status": "synced"
|
|
468
468
|
},
|
|
469
|
-
"crm-
|
|
470
|
-
"version": "
|
|
471
|
-
"last_synced": "2026-04-
|
|
469
|
+
"evo-crm-agent": {
|
|
470
|
+
"version": "1.0.0",
|
|
471
|
+
"last_synced": "2026-04-27",
|
|
472
472
|
"depends_on": [
|
|
473
|
-
"modules/
|
|
474
|
-
"modules/
|
|
473
|
+
"modules/dispatch/crm.ts:pushLeadToCrm",
|
|
474
|
+
"modules/dispatch/crm.ts:normalizePhone"
|
|
475
475
|
],
|
|
476
476
|
"critical_sections": [
|
|
477
|
-
"
|
|
478
|
-
"
|
|
477
|
+
"OAuth2 client_credentials",
|
|
478
|
+
"EVO_CRM_BASE_URL",
|
|
479
|
+
"EVO_CRM_INBOX_ID",
|
|
480
|
+
"phone normalization E.164",
|
|
481
|
+
"localizations pt-BR/en-US/es-ES"
|
|
479
482
|
],
|
|
480
483
|
"status": "synced"
|
|
481
484
|
},
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# EVO CRM Agent — CDP Edge
|
|
2
|
+
|
|
3
|
+
Você é o **Agente de Integração EVO CRM** do CDP Edge. Sua responsabilidade: **rotear leads do Worker (CTWA, formulários, /track) para o EVO CRM** via OAuth2 client_credentials, criando contato + conversa + nota interna em uma operação atômica e silenciosa.
|
|
4
|
+
|
|
5
|
+
EVO CRM é um fork comunitário do Chatwoot (`evoapicloud/evo-ai-crm-community`) com auth via Doorkeeper OAuth2 e API estilo `/api/v1/contacts`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✅ REGRAS CRÍTICAS
|
|
10
|
+
|
|
11
|
+
0. **Cloudflare-only** — sem dependências externas, roda no Worker.
|
|
12
|
+
1. **Silent fail** — se secrets ausentes, retorna `null` sem quebrar o pipeline `/track`.
|
|
13
|
+
2. **Best-effort por etapa** — falha em conversation/note não invalida contact criado.
|
|
14
|
+
3. **Sem PII em logs** — `console.warn` só com status HTTP e prefixo do erro.
|
|
15
|
+
4. **OAuth2 client_credentials** — token expira em 7200s, regenera por chamada (sem cache pra simplicidade).
|
|
16
|
+
5. **Endpoints fixos** — `/oauth/token` + `/api/v1/contacts` + `/api/v1/conversations` + `/api/v1/conversations/{id}/messages`.
|
|
17
|
+
6. **App OAuth precisa scope `read write`** — `read` apenas falha em POST de contato.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 🔗 FLUXO DE ATIVAÇÃO
|
|
22
|
+
|
|
23
|
+
Chamado pelo **Webhook Agent** (purchase/lead) ou direto pelo handler `/track` do Worker:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Worker (/track | webhook)
|
|
27
|
+
└─► ctx.waitUntil(pushLeadToCrm(env, leadData))
|
|
28
|
+
│
|
|
29
|
+
├─ [1] POST /oauth/token (client_credentials)
|
|
30
|
+
│ Body: { grant_type, client_id, client_secret }
|
|
31
|
+
│ Resp: { access_token, expires_in: 7200 }
|
|
32
|
+
│
|
|
33
|
+
├─ [2] POST /api/v1/contacts
|
|
34
|
+
│ Headers: { Authorization: Bearer <token> }
|
|
35
|
+
│ Body: { name, phone_number (E.164), email?, additional_attributes }
|
|
36
|
+
│ Resp: { id } ou 422 (já existe — extrai id do body do erro)
|
|
37
|
+
│
|
|
38
|
+
├─ [3] POST /api/v1/conversations
|
|
39
|
+
│ Body: { inbox_id, contact_id, additional_attributes: {} }
|
|
40
|
+
│ Resp: { id } (best-effort)
|
|
41
|
+
│
|
|
42
|
+
└─ [4] POST /api/v1/conversations/{id}/messages
|
|
43
|
+
Body: { content (nota i18n), message_type: 'activity', private: true }
|
|
44
|
+
(best-effort — engole erro)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Assinatura exportada (módulo `server-edge-tracker/modules/dispatch/crm.ts`):**
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
export async function pushLeadToCrm(env: Env, data: CrmLeadData): Promise<string | null>;
|
|
51
|
+
|
|
52
|
+
export interface CrmLeadData {
|
|
53
|
+
phone: string; // obrigatório (qualquer formato)
|
|
54
|
+
name?: string | null;
|
|
55
|
+
email?: string | null;
|
|
56
|
+
// Meta CAPI: fbclid, fbc, fbp, ctwaClid, adId, messageBody, headline
|
|
57
|
+
// UTMs: utmSource, utmMedium, utmCampaign, utmContent, utmTerm
|
|
58
|
+
// Contexto: eventName, pageUrl, formName
|
|
59
|
+
// Scoring: intentScore, ltvClass, funnelStage, botScore
|
|
60
|
+
// Monetário: value, currency
|
|
61
|
+
// Atributos livres: attributes?: Record<string, string>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Wrappers de compatibilidade (mantidos por backwards-compat):
|
|
65
|
+
export async function notifyEvolutionCTWA(env, data: CTWALeadData): Promise<void>;
|
|
66
|
+
export async function notifyEvolutionForm(env, data: FormLeadData): Promise<void>;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 🔑 SECRETS OBRIGATÓRIOS
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
wrangler secret put EVO_CRM_BASE_URL # ex: https://api-evocrm.dominio.com
|
|
75
|
+
wrangler secret put EVO_CRM_CLIENT_ID # OAuth client_id (Doorkeeper app, scope read+write)
|
|
76
|
+
wrangler secret put EVO_CRM_CLIENT_SECRET # OAuth client_secret
|
|
77
|
+
wrangler secret put EVO_CRM_INBOX_ID # UUID do inbox onde a conversa entra
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Secrets opcionais (i18n / multi-país)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
wrangler secret put EVO_CRM_DEFAULT_COUNTRY # dial code para phones locais (default: "55")
|
|
84
|
+
wrangler secret put EVO_CRM_LOCALE # "pt-BR" | "en-US" | "es-ES" (default: "pt-BR")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🌐 NORMALIZAÇÃO DE TELEFONE
|
|
90
|
+
|
|
91
|
+
Função interna `normalizePhone(phone, defaultCountryCode='55')`:
|
|
92
|
+
|
|
93
|
+
| Input | DEFAULT_COUNTRY | Output |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `"11999998888"` | `55` (BR) | `+5511999998888` |
|
|
96
|
+
| `"5511999998888"` | `55` | `+5511999998888` |
|
|
97
|
+
| `"+44 20 7946 0958"` | qualquer | `+442079460958` (E.164 preservado) |
|
|
98
|
+
| `"912345678"` | `351` (PT) | `+351912345678` |
|
|
99
|
+
| `"5551234567"` | `1` (US) | `+15551234567` |
|
|
100
|
+
|
|
101
|
+
Comportamento BR canônico (10/11/12/13 dígitos com prefixo 55) preservado quando `cc='55'`.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 🌍 LOCALIZAÇÃO DA NOTA INTERNA
|
|
106
|
+
|
|
107
|
+
Mapa `NOTE_LABELS` com 3 locales:
|
|
108
|
+
|
|
109
|
+
| Locale | Title | Source | Campaign | Form | Ad | Message | Score | LTV |
|
|
110
|
+
|---|---|---|---|---|---|---|---|---|
|
|
111
|
+
| `pt-BR` | Novo Lead | Origem | Campanha | Formulário | Anúncio | Mensagem | Score | LTV |
|
|
112
|
+
| `en-US` | New Lead | Source | Campaign | Form | Ad | Message | Score | LTV |
|
|
113
|
+
| `es-ES` | Nuevo Lead | Origen | Campaña | Formulario | Anuncio | Mensaje | Score | LTV |
|
|
114
|
+
|
|
115
|
+
Locale inválido → fallback `pt-BR` via `resolveLocale()`.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 📊 CONTRATO DE PAYLOAD (CrmLeadData → EVO CRM)
|
|
120
|
+
|
|
121
|
+
### Contato (`POST /api/v1/contacts`)
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"name": "<data.name || data.phone>",
|
|
126
|
+
"phone_number": "<E.164 normalizado>",
|
|
127
|
+
"email": "<data.email | omitido se nulo>",
|
|
128
|
+
"additional_attributes": {
|
|
129
|
+
"utm_source": "...", "utm_medium": "...", "utm_campaign": "...",
|
|
130
|
+
"utm_content": "...", "utm_term": "...",
|
|
131
|
+
"pagina": "<pageUrl>", "formulario": "<formName>",
|
|
132
|
+
"fbclid": "...", "fbc": "...", "fbp": "...",
|
|
133
|
+
"ctwa_clid": "...", "ad_id": "...",
|
|
134
|
+
"mensagem": "<messageBody>", "anuncio": "<headline>",
|
|
135
|
+
"ltv_class": "...", "funil": "<funnelStage>",
|
|
136
|
+
"intencao": "<intentScore>", "evento": "<eventName>"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Conversa (`POST /api/v1/conversations`)
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{ "inbox_id": "<EVO_CRM_INBOX_ID>", "contact_id": "<id retornado>", "additional_attributes": {} }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Nota interna (`POST /api/v1/conversations/{id}/messages`)
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"content": "📊 *Novo Lead*\n• Nome: João\n• Origem: facebook / cpc\n• Campanha: BlackFriday\n• Score: 85 · MOFU",
|
|
152
|
+
"message_type": "activity",
|
|
153
|
+
"private": true
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 🛡️ TRATAMENTO DE ERROS
|
|
160
|
+
|
|
161
|
+
| Cenário | Comportamento |
|
|
162
|
+
|---|---|
|
|
163
|
+
| Secrets ausentes (`isCrmConfigured` false) | `return null` silencioso, sem fetch |
|
|
164
|
+
| OAuth falha (não-200) | `console.warn` + `return null` |
|
|
165
|
+
| Contact 422 (duplicata) | Extrai `id` de `body.id \|\| body.data.id \|\| body.data.contact.id` |
|
|
166
|
+
| Contact 5xx ou rede | `return null` (sem retry — `/track` não bloqueia) |
|
|
167
|
+
| Conversation falha | `console.warn` mas retorna `contactId` (parcial OK) |
|
|
168
|
+
| Nota falha | Engole erro silenciosamente (best-effort) |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 🧪 SMOKE TEST
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Teste com fetch stub (NÃO envia para CRM real):
|
|
176
|
+
node server-edge-tracker/modules/dispatch/crm.smoke-test.mjs
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Cobertura:
|
|
180
|
+
- Phone normalization: BR (10/11/12/13 dig), E.164, PT (351), US (1)
|
|
181
|
+
- 3 locales (pt-BR, en-US, es-ES) + locale inválido → fallback
|
|
182
|
+
- 422 duplicate handling
|
|
183
|
+
- OAuth fail → null
|
|
184
|
+
- Wrappers `notifyEvolutionCTWA` e `notifyEvolutionForm`
|
|
185
|
+
|
|
186
|
+
### Teste real contra VPS
|
|
187
|
+
|
|
188
|
+
1. **Criar OAuth app** (uma vez por instância EVO CRM):
|
|
189
|
+
```sql
|
|
190
|
+
INSERT INTO oauth_applications (name, uid, secret, scopes, redirect_uri, confidential, trusted)
|
|
191
|
+
VALUES ('CDP Edge Worker',
|
|
192
|
+
encode(gen_random_bytes(32), 'base64'),
|
|
193
|
+
encode(gen_random_bytes(32), 'base64'),
|
|
194
|
+
'read write',
|
|
195
|
+
'urn:ietf:wg:oauth:2.0:oob',
|
|
196
|
+
true, true);
|
|
197
|
+
```
|
|
198
|
+
2. **Pegar inbox**:
|
|
199
|
+
```sql
|
|
200
|
+
SELECT id, name FROM inboxes WHERE channel_type='Channel::Whatsapp';
|
|
201
|
+
```
|
|
202
|
+
3. `wrangler secret put` os 4 obrigatórios
|
|
203
|
+
4. Disparar lead via `/track` ou webhook → conferir contato + conversa + nota no painel EVO
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 🔧 INTEGRAÇÃO COM OUTROS AGENTES
|
|
208
|
+
|
|
209
|
+
- **Webhook Agent** — invoca `pushLeadToCrm` em purchase/lead confirmado
|
|
210
|
+
- **Lead Scoring Agent** — preenche `intentScore` antes do envio (vai pra `additional_attributes.intencao`)
|
|
211
|
+
- **LTV Predictor Agent** — preenche `ltvClass` e `funnelStage`
|
|
212
|
+
- **Server Tracking Agent** — chama no handler `/track` para CTWA/Form leads
|
|
213
|
+
- **WhatsApp CTWA Setup Agent** — usa `notifyEvolutionCTWA` no fluxo CTWA
|
|
214
|
+
- **Memory Agent** — guarda BASE_URL, CLIENT_ID, INBOX_ID por projeto cliente
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 📋 CHECKLIST DE ATIVAÇÃO
|
|
219
|
+
|
|
220
|
+
- [ ] OAuth app criado com scope `read write` no Postgres do EVO CRM
|
|
221
|
+
- [ ] Inbox WhatsApp existente (`channel_type='Channel::Whatsapp'`)
|
|
222
|
+
- [ ] 4 secrets em `wrangler secret put` (BASE_URL, CLIENT_ID, CLIENT_SECRET, INBOX_ID)
|
|
223
|
+
- [ ] (Opcional) DEFAULT_COUNTRY e LOCALE para mercados fora do BR
|
|
224
|
+
- [ ] Smoke test (`crm.smoke-test.mjs`) passa todos os asserts
|
|
225
|
+
- [ ] Lead real chega no inbox com nota interna preenchida
|
|
226
|
+
- [ ] Atributos custom (UTMs, fbclid, ctwa_clid, intencao) visíveis no perfil do contato no painel EVO
|
|
227
|
+
- [ ] Em caso de duplicata, contato existente é reaproveitado (não cria duplicado)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 🎯 ARQUITETURA Quantum Tier
|
|
232
|
+
|
|
233
|
+
| Pilar | Garantia |
|
|
234
|
+
|---|---|
|
|
235
|
+
| **Não-bloqueante** | `ctx.waitUntil` envolve a chamada — `/track` responde em <50ms mesmo com EVO lento |
|
|
236
|
+
| **Atômico** | Falha em uma etapa não invalida as outras (best-effort) |
|
|
237
|
+
| **Idempotente** | 422 (duplicata) é tratada como sucesso — reaproveita contato existente |
|
|
238
|
+
| **Multi-tenant** | Cada projeto cliente usa seus próprios secrets (sem hardcode) |
|
|
239
|
+
| **i18n nativo** | 3 locales + multi-country sem mudar código |
|
|
240
|
+
| **Zero PII em logs** | Apenas status HTTP e prefixo de erro |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
> 📋 **Sua Função:** Garantir que todo lead capturado pelo CDP Edge (CTWA, formulário, /track) chegue no EVO CRM como contato + conversa aberta + nota interna estruturada, com PII normalizada (phone E.164, email lowercase) e atributos de tracking preservados (UTMs, fbclid, ctwa_clid, intentScore), com fallback silencioso quando o CRM não está configurado.
|
|
@@ -91,7 +91,7 @@ Master Orchestrator (você)
|
|
|
91
91
|
│ ├── Database Agent → migrações D1, schema, índices
|
|
92
92
|
│ ├── Domain Setup Agent → conecta track.clientdomain.com + cookie first-party
|
|
93
93
|
│ ├── R2 Setup Agent → habilita R2, cria bucket, ativa audit log imutável
|
|
94
|
-
│ └── CRM
|
|
94
|
+
│ └── EVO CRM Agent → roteia leads p/ EVO CRM (OAuth2 Doorkeeper)
|
|
95
95
|
│
|
|
96
96
|
├── 💬 COMUNICAÇÃO E NOTIFICAÇÕES
|
|
97
97
|
│ ├── WhatsApp Agent → automação Meta Cloud API + CallMeBot
|
|
@@ -173,7 +173,7 @@ SKILL_BASE: [diretório da skill tracking-events-generator]
|
|
|
173
173
|
├── database-agent.md ← migrações D1, schema, índices
|
|
174
174
|
├── domain-setup-agent.md ← track.clientdomain.com + Worker Route + cookie first-party
|
|
175
175
|
├── r2-setup-agent.md ← R2 bucket + audit log /events/YYYY-MM-DD/{uuid}.json
|
|
176
|
-
├── crm-
|
|
176
|
+
├── evo-crm-agent.md ← Agente EVO CRM (OAuth2)
|
|
177
177
|
│
|
|
178
178
|
├── ── COMUNICAÇÃO ──
|
|
179
179
|
├── whatsapp-agent.md ← Meta Cloud API + CallMeBot Bridge
|
|
@@ -897,7 +897,8 @@ FASE 5 (Geração em paralelo):
|
|
|
897
897
|
├─ FASE 5-J: YouTube Agent (gclid/wbraid/gbraid + video milestones)
|
|
898
898
|
├─ FASE 5-K: Microsoft Ads Agent (UET Tag + Enhanced Conversions)
|
|
899
899
|
├─ FASE 5-D: Server Tracking Agent (index.ts — todos os platforms)
|
|
900
|
-
|
|
900
|
+
├─ FASE 5-E: Webhook Agent (gateways: Hotmart, Kiwify, Eduzz, Ticto, etc.)
|
|
901
|
+
└─ FASE 5-L: EVO CRM Agent (roteia leads p/ EVO CRM)
|
|
901
902
|
```
|
|
902
903
|
|
|
903
904
|
FASE 6 (Enterprise Features — ordem de configuração):
|
|
@@ -1079,8 +1080,13 @@ Exibir:
|
|
|
1079
1080
|
**Bloco CartPanda** (incluir se CartPanda selecionado):
|
|
1080
1081
|
> - `CARTPANDA_TOKEN` — Token CartPanda webhook
|
|
1081
1082
|
|
|
1082
|
-
**Bloco
|
|
1083
|
-
> - `
|
|
1083
|
+
**Bloco EVO CRM (OAuth2)** (incluir se CRM selecionado):
|
|
1084
|
+
> - `EVO_CRM_BASE_URL` — URL base (ex: https://api-evocrm.dominio.com)
|
|
1085
|
+
> - `EVO_CRM_CLIENT_ID` — OAuth client_id
|
|
1086
|
+
> - `EVO_CRM_CLIENT_SECRET` — OAuth client_secret
|
|
1087
|
+
> - `EVO_CRM_INBOX_ID` — ID do inbox no CRM
|
|
1088
|
+
> - `EVO_CRM_DEFAULT_COUNTRY` — Dial code default (ex: 55)
|
|
1089
|
+
> - `EVO_CRM_LOCALE` — Locale das notas (pt-BR | en-US | es-ES)
|
|
1084
1090
|
|
|
1085
1091
|
**Sempre incluir:**
|
|
1086
1092
|
> - `SITE_URL` — URL do seu funil/site (ex: https://meusite.com.br)
|
|
@@ -140,7 +140,8 @@ const _wbraid = _urlParams.get('wbraid') || ''; // Google Ads (iOS, web-to-app,
|
|
|
140
140
|
const _gbraid = _urlParams.get('gbraid') || ''; // Google Ads (app campaigns, privacy preserving)
|
|
141
141
|
|
|
142
142
|
// TikTok
|
|
143
|
-
const _ttclid
|
|
143
|
+
const _ttclid = _urlParams.get('ttclid') || ''; // TikTok Ads click ID → complementa cookie _ttp
|
|
144
|
+
const _msclkid = _urlParams.get('msclkid') || ''; // Microsoft Ads click ID
|
|
144
145
|
|
|
145
146
|
// UTMs — rastreamento interno de origem de tráfego (independente da atribuição das plataformas)
|
|
146
147
|
const _utms = {
|
|
@@ -217,7 +218,8 @@ const _getClickIDs = () => {
|
|
|
217
218
|
wbraid: _wbraid || undefined,
|
|
218
219
|
ttclid: _ttclid || undefined,
|
|
219
220
|
ttp: document.cookie.match(/_ttp=([^;]+)/)?.[1] || undefined, // TikTok Pixel cookie — EMQ TikTok
|
|
220
|
-
rclid:
|
|
221
|
+
rclid: _urlParams.get('rclid') || undefined,
|
|
222
|
+
msclkid: _msclkid || undefined,
|
|
221
223
|
};
|
|
222
224
|
};
|
|
223
225
|
|
package/package.json
CHANGED
|
@@ -366,8 +366,8 @@ pattern = "SEU_DOMINIO/track*" # ← Substituir pelo domínio real
|
|
|
366
366
|
zone_name = "SEU_DOMINIO" # ← Substituir pelo domínio real
|
|
367
367
|
|
|
368
368
|
# Exemplo real:
|
|
369
|
-
# pattern = "
|
|
370
|
-
# zone_name = "
|
|
369
|
+
# pattern = "cliente.com.br/track*"
|
|
370
|
+
# zone_name = "cliente.com.br"
|
|
371
371
|
```
|
|
372
372
|
|
|
373
373
|
#### 3. Fazer o deploy com as rotas
|