vek1 — Migração Evolution API → WhatsApp Cloud API (Meta oficial)
Migração Evolution → WhatsApp Cloud API (Meta oficial)
Spec de migração do canal WhatsApp do vek1 de Evolution API (Baileys, não-oficial) para a WhatsApp Cloud API da Meta (oficial).
Estado atual da integração: vek1/integrations linha "Evolution API".
TL;DR — direção tomada (2026-05-27)
- Arquitetura multi-tenant desde dia 1: cada agent vek1 conecta um número de lojista, não um número da Kodama. Embedded Signup obrigatório.
- Destino: Solution Partner (vek1 toma payment ownership das WABAs dos lojistas → cobra conversation cost no plano com margem).
- Gate: Tech Provider — Meta exige operar como Tech Provider antes de aprovar Solution Partner. Cronograma realista: lança em Tech Provider em ~1 mês, aplica Solution Partner em paralelo, aprovação em meses.
- Trilha dupla na Fase 0: Trilha A (técnica/Meta) + Trilha B (prep Solution Partner: financeiro/legal/business).
- Código é o mesmo nos 2 modelos: muda só
wa_payment_ownershipflag por agent + chamada APItake_payment_ownershipquando aprovado.
1. Motivação
- Evolution API é Baileys-based (engenharia reversa do WhatsApp Web) → risco constante de ban do número, bloqueio de novos features, dependência de um container self-hosted no VPS Hermes (
evolution.kodama.solutions). - Cloud API da Meta é o canal oficial e suportado, com SLA, escala automática, e features nativas (templates, interactive messages, flows, click-to-chat ads).
- Pré-requisito pra crescer fora de tier pequeno: lojistas que escalam não toleram número banido.
- Trade-off: custo per-conversation (não mais "grátis"), janela 24h, templates aprovados pra mensagens proativas.
2. Estado atual (a substituir)
Arquivos envolvidos hoje (referência rápida):
| Camada | Arquivo |
|---|---|
| Client HTTP | src/lib/evolution-api.ts |
| Instance management (criar/QR/delete) | src/lib/evolution-instance.ts |
| Webhook receiver | src/app/api/webhooks/evolution/route.ts |
| Message handler (lead/order/chat orchestration) | src/lib/whatsapp-handler.ts |
| Provisioning route (gera QR pro agent) | src/app/api/agents/[id]/instance/route.ts |
| UI conexão | src/components/agents/agent-settings-client.tsx, src/components/agents/whatsapp-tester.tsx |
| Schema (agents table) | src/lib/db/schema.ts linhas 109-116 (evolution_instance_id, whatsapp_enabled, whatsapp_phone_number) |
| Outbound notifications | src/lib/order-notifications.ts (notificações de pedido — provavelmente fora da janela 24h) |
| Proof attachment (PIX) | src/lib/order-proofs.ts (download base64 via Evolution getBase64FromMediaMessage) |
Container Evolution: evolution.kodama.solutions no VPS Hermes (v2.3.x).
3. Modelo: Solution Partner (destino) via Tech Provider (gate)
Multi-tenant desde dia 1. Cada agent vek1 conecta um número do lojista, não da Kodama.
Solution Partner é destino estratégico, mas Meta exige operar como Tech Provider primeiro.
3.1 — Os 3 níveis Meta (2026)
| Nível | Como entra | Quem paga Meta | Aprovação |
|---|---|---|---|
| Tech Provider | Self-service, app review | Lojista direto (cartão dele na WABA dele) | 5-14 dias (app review) |
| Solution Partner | Aplicação formal Meta | vek1 (payment ownership das WABAs dos lojistas) | Semanas a meses, Meta pode rejeitar |
| Descontinuado | — | Não aceita novos |
3.2 — Por que Solution Partner é o destino
- vek1 cobra plano único do lojista (software + cota de conversations + overage com margem)
- Lojista vê 1 fatura só (Kodama) — não 2 (Kodama + Meta direto)
- vek1 capta margem em conversations (revenue share natural do modelo SaaS)
- Acesso a features Solution Partner-only (BSP Hub, programmatic onboarding APIs, melhor suporte Meta)
- Branding e onboarding mais limpos (lojista não precisa criar conta Meta business completa)
3.3 — Por que Tech Provider é o gate
- Meta não aprova Solution Partner sem app já funcionando + tração demonstrável
- Tech Provider é self-service e sai em dias; Solution Partner application leva meses
- Mesma arquitetura técnica nos 2: embedded signup, schema, webhook. Solution Partner adiciona só
take_payment_ownershipna hora de conectar lojista - Migrar Tech Provider → Solution Partner é incremental, não rip-and-replace
3.4 — Trilha A: Pré-requisitos Meta técnicos (Tech Provider)
Começar imediatamente.
- Meta Business Manager — criar a conta business da Kodama
- Business Verification (KYC) Kodama — CNPJ, comprovante endereço, doc de domínio. Pode ser rejeitado, prepara pra reenviar. Aprovação obrigatória pra Solution Partner depois
- App no Meta for Developers (único pro vek1 todo)
- Tipo: Business, vinculado ao Business Kodama
- Produto WhatsApp adicionado
- Anotar:
app_id,app_secret
- System User Kodama com role Admin + assets (app + WABA Kodama opcional)
- Permanent Access Token salvo seguro — usado pra operações app-level (subscribe webhooks, refresh tokens, app config)
- Embedded Signup Configuration no app
- Settings → WhatsApp → Embedded Signup → cria config
- Anotar
config_id— usado noFB.logindo frontend
- App Review submetido com permissões:
whatsapp_business_managementwhatsapp_business_messagingbusiness_management- Requer: screencast do fluxo, política de privacidade publicada, termos de uso publicados, descrição clara de cada permissão
- Verify token — string arbitrária (
openssl rand -hex 32), salva no env comoWA_VERIFY_TOKEN. Configurada no webhook Meta + no env vek1 - WABA Kodama (opcional, só pra testes) — número de teste interno antes de ter lojista real
IMPORTANTE: WABAs/Phone Numbers/Tokens dos lojistas não são criados em Fase 0 — são criados em runtime via embedded signup quando o lojista conecta o agent.
3.5 — Trilha B: Prep Solution Partner (paralelo à Trilha A)
Não bloqueia lançamento técnico. Coleta material em paralelo pra aplicar quando Trilha A estiver no ar.
B.1 — Financeiro
- Cartão corporativo CNPJ Kodama com limite alto (R$10-50k inicial). Meta vai cobrar conversations de todos os lojistas em vek1 após payment ownership
- Conta bancária PJ ativa, extrato 3 meses
- DRE/balanço últimos 12m (se houver — Meta às vezes pede)
- Linha de crédito Meta — Meta concede limite após aprovação. Antes disso, cartão é o método
B.2 — Legal
- Termos de uso publicados em
vek1.com/terms - Política de privacidade em
vek1.com/privacy - Acordo de processamento de dados (LGPD/GDPR) — modelo Meta
- Acceptable Use Policy (AUP) pros lojistas — anti-spam, anti-marketing abusivo, conformidade WhatsApp Commerce Policy
- Compliance program interno documentado: como vek1 fiscaliza lojistas, processo de suspensão, reporting Meta
B.3 — Negócio
- Business plan / pitch deck: mercado, projeção 12-24m de lojistas e volume de conversations
- Customer commitment letters: 3-10 lojistas (atuais ou pipeline) em papel timbrado dizendo que querem contratar vek1 como Solution Partner se aprovado
- Casos de uso: prints/vídeos do produto, fluxos típicos, métricas (tempo de resposta, taxa de fechamento, etc.)
- Capacidade técnica: diagrama de arquitetura (reusa vek1/architecture), equipe, SLA interno, plano de suporte
B.4 — Aplicação formal
- Portal: https://business.facebook.com/business/help/solution-partner
- Submete B.1, B.2, B.3
- Meta entrevista (call em inglês com solutions engineer)
- Decisão: semanas a meses
3.6 — Risco operacional Solution Partner
Estes são os trade-offs que payment ownership traz — internalizar antes de aplicar:
- Inadimplência do lojista vira problema da vek1. Lojista usa 10k conversations, não paga plano vek1; Meta cobra vek1 do mesmo jeito. Precisa controle de crédito interno: monitor de uso, suspensão automática em atraso, cap de conversations no plano
- Compliance violation do lojista pode banir WABA inteira. Se um lojista manda spam pesado, Meta pode penalizar todos os lojistas no app vek1. Precisa auto-suspend agent quando Meta sinalizar violação (webhook
account_alerts) - Volume mínimo no Solution Partner Agreement pode existir. Se vek1 não atingir, multa ou downgrade
- Responsabilidade legal por dados dos lojistas sobe — vek1 vira processor formal de dados Meta
4. Mudanças de código
4.1 Schema (src/lib/db/schema.ts)
Acrescentar (sem remover os antigos durante transição):
// agents table
waProvider: text('wa_provider').default('evolution').notNull(), // 'evolution' | 'meta'
waPhoneNumberId: text('wa_phone_number_id'), // Meta phone_number_id (do lojista)
waWabaId: text('wa_waba_id'), // Meta WABA id (do lojista)
waAccessToken: text('wa_access_token'), // token do lojista — CRIPTOGRAFADO
waTokenRefreshedAt: timestamp('wa_token_refreshed_at'), // tokens long-lived precisam refresh ocasional
waPaymentOwnership: text('wa_payment_ownership').default('customer').notNull(), // 'customer' | 'vek1'
waBusinessVerificationStatus: text('wa_business_verification_status'), // status KYC do lojista pra mensagens out-of-window
waConnectedAt: timestamp('wa_connected_at'),
Nova tabela wa_templates (templates aprovados Meta, por agent):
{
id, agentId (fk), wabaId, name, language, category,
status, // 'PENDING' | 'APPROVED' | 'REJECTED' | 'PAUSED'
componentsSchema (jsonb), // validar params no envio
createdAt, lastStatusUpdateAt
}
Nova tabela wa_message_statuses (delivery/read/failed tracking):
{
id, messageId (fk → messages_history), waMessageId, // wamid.XXX
status, // 'sent' | 'delivered' | 'read' | 'failed'
errorCode, errorTitle, errorDetails (jsonb),
recipientId, timestamp
}
Manter evolution_instance_id, whatsapp_phone_number, whatsapp_enabled durante a migração. Remover só depois que todos os agents migrarem.
Lembrete schema-migrator: db:push --force dropa indexes hnsw — re-aplicar init/01-init.sql do vek1-api depois.
4.1.1 Payment Ownership flip
Quando Solution Partner aprovado e cada lojista aceita migrar:
// pseudo
async function takePaymentOwnership(agentId) {
const agent = await getAgent(agentId);
const res = await fetch(
`https://graph.facebook.com/v21.0/${agent.waWabaId}/take_payment_ownership`,
{ method: 'POST', headers: { Authorization: `Bearer ${SYSTEM_USER_TOKEN}` } }
);
// ... handle response
await updateAgent(agentId, { waPaymentOwnership: 'vek1' });
}
Pricing implication: quando waPaymentOwnership='vek1', vek1 captura cost no token_usage (extender pra conversation_usage) e cobra no plano. Detalhar em vek1/pricing-design.
Lembrete schema-migrator: db:push --force dropa indexes hnsw — re-aplicar init/01-init.sql do vek1-api depois (vide vek1-api/gotchas).
4.2 Novo client (src/lib/whatsapp-cloud.ts)
Substitui evolution-api.ts + evolution-instance.ts. Endpoints base:
| Operação | HTTP |
|---|---|
| Send text | POST /v21.0/{phone_number_id}/messages body { messaging_product:"whatsapp", to, type:"text", text:{body} } |
| Send image (URL) | POST /v21.0/{phone_number_id}/messages body { ..., type:"image", image:{link, caption} } |
| Send image (media id) | image:{id, caption} |
| Send template | type:"template", template:{name, language:{code}, components:[...]} |
| Download media | GET /v21.0/{media_id} → resposta tem URL temp (5min); GET na URL com Authorization: Bearer {token} |
| Get media metadata | GET /v21.0/{media_id} |
| Mark as read | POST /v21.0/{phone_number_id}/messages body { status:"read", message_id } |
Auth header: Authorization: Bearer {access_token}. Sem apikey: como Evolution.
Base URL: https://graph.facebook.com/v21.0 (versão fixar em env, atualizar trimestralmente).
4.3 Webhook receiver (src/app/api/webhooks/whatsapp/route.ts)
Novo endpoint paralelo (sem matar /api/webhooks/evolution).
GET (verification handshake)
const mode = searchParams.get('hub.mode');
const token = searchParams.get('hub.verify_token');
const challenge = searchParams.get('hub.challenge');
if (mode === 'subscribe' && token === process.env.WA_VERIFY_TOKEN) {
return new Response(challenge, { status: 200 });
}
return new Response('forbidden', { status: 403 });
POST
- Validar
X-Hub-Signature-256: HMAC-SHA256 do raw body comapp_secret. Sem isso qualquer um manda payload e dispara mensagens. - Payload shape:
{ "object": "whatsapp_business_account", "entry": [{ "id": "<waba_id>", "changes": [{ "field": "messages", "value": { "messaging_product": "whatsapp", "metadata": { "phone_number_id": "...", "display_phone_number": "..." }, "contacts": [{ "profile": { "name": "..." }, "wa_id": "5511999..." }], "messages": [{ "from": "5511999...", "id": "wamid....", "timestamp": "1716808800", "type": "text", "text": { "body": "oi" } // ou type:"image", image:{id, mime_type, sha256, caption?} // ou type:"document", document:{id, mime_type, filename, caption?} // ou type:"audio", "video", "sticker", "location", "contacts", "interactive" }], "statuses": [{ "id":"wamid...", "status":"sent|delivered|read|failed", "timestamp":"...", "recipient_id":"...", "errors":[...] }] } }] }] } - Lookup do agent: por
wa_phone_number_id = value.metadata.phone_number_id(substituifindByEvolutionInstance) fromé phone direto E.164 sem+— joga fora toda lógica de JID/LID (whatsapp-handler.tslinhas 243-258)- Statuses (delivered/read/failed) — armazenar em
messages.metadataou nova tabelawa_message_statuses. Failed temerrors[]comcodeetitle. - Retornar 200 sempre (mesma regra do Evolution: Meta retenta com backoff agressivo)
4.4 Handler (src/lib/whatsapp-handler.ts)
Adapta EvolutionWebhookMessage para shape novo (manter interface, normalizar no webhook):
- Remove
senderPn,isLid— Cloud API não tem instanceId→phoneNumberId(renomear o conceito; mantém shape pra reaproveitar o handler durante transição)- Media:
media.rawMessagedeixa de ser envelope Baileys, vira{ media_id }. Download via Cloud API antes de chamarattachWhatsAppProofToOrder:const meta = await getMediaMetadata(media_id); const buf = await downloadMedia(meta.url); await attachWhatsAppProofToOrder(orderId, { buffer: buf, mimeType: meta.mime_type, ... }); sendWhatsappText/sendWhatsappImage: dispatch poragent.wa_provider:if (agent.waProvider === 'meta') return whatsappCloud.sendText(...); return evolutionSendText(...);
4.5 Provisioning route (/api/agents/[id]/wa-connect)
QR code não existe na Cloud API. Embedded Signup obrigatório (sem fallback de "form admin" — modelo é multi-tenant).
Fluxo end-to-end (lojista clica "Conectar WhatsApp" em /agents/[id]/settings):
- Frontend dispara
FB.logincom{ config_id: <EMBEDDED_SIGNUP_CONFIG_ID>, response_type: 'code', override_default_response_type: true, extras: { setup: { ... } } } - Popup Meta abre. Lojista logga FB pessoal, cria Business + WABA + adiciona número, confirma SMS — tudo dentro do popup
- Popup fecha. Callback recebe
code+ (via message channel)phone_number_id+waba_id - Frontend POST
/api/agents/[id]/wa-connectcom payload - Backend:
- Troca
codepor access token long-lived do lojista:GET /v21.0/oauth/access_token?client_id={app_id}&client_secret={app_secret}&code={code} - Subscribe webhook na WABA do lojista:
POST /v21.0/{waba_id}/subscribed_apps(auth: token do lojista) - Registra número (necessário 1x):
POST /v21.0/{phone_number_id}/registerbody{ messaging_product:'whatsapp', pin:'<PIN>' }(PIN vem do lojista no signup) - Solution Partner only (quando aprovado):
POST /{waba_id}/take_payment_ownership(auth: System User token vek1) - Cria templates iniciais na WABA do lojista (vide 4.7)
- Grava no agent:
waProvider='meta',waPhoneNumberId,waWabaId,waAccessToken(criptografado),waPaymentOwnership('customer'ou'vek1'),waConnectedAt
- Troca
Token refresh:
- Token de embedded signup é long-lived (60 dias). Refresh com
GET /v21.0/oauth/access_token?grant_type=fb_exchange_token&fb_exchange_token={current}antes de expirar - Cron diário ou on-demand quando call falha com 401: refresh + update
waTokenRefreshedAt
4.6 UI (agent-settings-client.tsx + whatsapp-tester.tsx)
- Remove tela de QR
- Substitui por botão "Conectar WhatsApp" que dispara
FB.login(embedded signup) - Mostra status
connected | disconnected | pending_verification | suspended - Mostra número conectado + perfil + status de templates pendentes/aprovados
- Botão "Desconectar" →
DELETE /v21.0/{waba_id}/subscribed_apps - Quando Solution Partner ativo: badge mostrando billing (
Você paga via plano vek1vsCobrado direto pela Meta)
4.7 Templates (por WABA do lojista)
Janela de 24h: free-form messages só dentro de 24h da última mensagem do user. Fora disso → precisa template aprovado pela Meta.
Templates são por WABA — vek1 precisa criar os templates em cada WABA de lojista quando ele conecta. Não tem "template global vek1".
Identificar fluxos que mandam mensagem fora da janela e converter pra template:
| Fluxo | Arquivo | Dentro/Fora janela | Ação |
|---|---|---|---|
| Resposta do chat IA | whatsapp-handler.ts linha 366 |
Dentro (sempre é resposta a msg do user) | OK, free-form |
| Ack comprovante PIX | whatsapp-handler.ts linhas 305-307 |
Dentro | OK, free-form |
| Notificação status pedido | order-notifications.ts |
Fora (lojista marca shipped/delivered horas depois) | Template order_status_update (UTILITY) |
| Lembrete pagamento | order-notifications.ts |
Fora | Template payment_reminder (UTILITY) |
| Reengajamento lead frio | TBD | Fora | Template lead_reengagement (MARKETING) |
Auto-criação no /api/agents/[id]/wa-connect:
const STANDARD_TEMPLATES = [
{ name: 'order_status_update', category: 'UTILITY', ... },
{ name: 'payment_reminder', category: 'UTILITY', ... },
{ name: 'lead_reengagement', category: 'MARKETING', ... },
];
for (const tpl of STANDARD_TEMPLATES) {
await createTemplate(wabaId, lojistaToken, tpl);
}
// Aprovação Meta: 1-48h. Webhook 'message_template_status_update' notifica.
// Notificar lojista por email quando todos aprovarem.
Categorias: MARKETING (mais caro), UTILITY (atualização de transação, mais barato), AUTHENTICATION (OTP, não usamos), SERVICE (free dentro de janela aberta).
Webhook message_template_status_update atualiza wa_templates.status por agent.
5. Plano de migração (2 trilhas em paralelo)
Trilha A — Técnica (Tech Provider → Solution Partner code-ready)
Fase A0 — Setup Meta (semana 1-2)
- Business Manager Kodama criado
- Business Verification (KYC) submetida — começa imediatamente (1-3 semanas)
- App "vek1 WhatsApp" criado no Meta for Developers
- System User + Permanent Access Token salvo seguro (1Password/Bitwarden)
- Embedded Signup Configuration criada no app → anotar
config_id - Verify token gerado (
openssl rand -hex 32) + envWA_VERIFY_TOKEN - Número de teste interno (chip pré-pago ou VoIP) registrado em WABA Kodama pra smoke tests
Fase A1 — Schema + dual provider (semana 2)
- Schema migration:
wa_provider,wa_phone_number_id,wa_waba_id,wa_access_token,wa_token_refreshed_at,wa_payment_ownership,wa_business_verification_status,wa_connected_at - Tabelas novas:
wa_templates,wa_message_statuses - Default
wa_provider = 'evolution'em agents existentes - Smoke test: Evolution continua funcionando 100%
Fase A2 — Novo client + webhook (semanas 2-3)
-
lib/whatsapp-cloud.ts— send text/image/template + download media + template CRUD -
lib/wa-token-encryption.ts— encrypt/decrypt tokens (mesmo pattern AbacatePay) -
/api/webhooks/whatsapp/route.tscom signature verification (X-Hub-Signature-256) - Adapter no
whatsapp-handler.ts(dispatch poragent.waProvider) - Handler de
message_template_status_update→ atualizawa_templates - Handler de
account_alerts→ suspende agent automaticamente - Tests unitários: dispatch, signature verification, encryption
Fase A3 — App Review submission (semana 3)
- Política de privacidade publicada em
vek1.com/privacy - Termos de uso publicados em
vek1.com/terms - Screencast embedded signup + envio de mensagem (5min)
- Descrições de uso de cada permissão Meta
- Submeter App Review → aguardar 5-14 dias
Fase A4 — Embedded Signup + onboarding UI (semanas 3-4, paralelo ao app review)
- Facebook JS SDK integrado em
agent-settings-client.tsx - Botão "Conectar WhatsApp" dispara
FB.logincomconfig_id -
/api/agents/[id]/wa-connect— code→token exchange, subscribe webhook, register number, criar templates iniciais - UI mostra status + perfil + templates pendentes
- Endpoint
/api/agents/[id]/wa-disconnect
Fase A5 — Templates aprovados (paralelo desde A2)
- Catálogo standard de templates definido (
order_status_update,payment_reminder,lead_reengagement) - Templates criados via API na WABA Kodama de teste → aprovados → validados
-
order-notifications.tsadaptado pra usar template quando provider=meta - Cron de notificação por email pro lojista quando todos templates aprovam
Fase A6 — Piloto interno (semana 5, depois App Review aprovado)
- Conectar 1 agent interno via embedded signup (número de teste Kodama)
- Validar end-to-end: receive → chat backend → reply → templates → statuses
- Monitorar 1 semana
Fase A7 — Rollout lojistas Tech Provider (semanas 6-8)
- Comunicar lojistas existentes sobre migração (e-mail + in-app banner)
- Lojista executa: deslogar Evolution → deletar conta WhatsApp celular → aguardar 24h → conectar via embedded signup
- Migrar 1 lojista por dia, monitorar
- Quando todos migrarem: derruba container Evolution, deleta
evolution_instance_id, removelib/evolution-*.tse/api/webhooks/evolution
Fase A8 — Solution Partner activation (quando Trilha B aprovar)
- Para cada agent ativo: chamar
POST /{waba_id}/take_payment_ownership - Atualizar
wa_payment_ownership = 'vek1' - Estender
token_usage→conversation_usagecapturando custo por conversation - Atualizar vek1/pricing-design e billing pra incluir cota de conversations + overage
- Comunicar lojistas: agora pagam tudo via plano vek1 (1 fatura só)
Trilha B — Solution Partner application (paralelo, leva meses)
Fase B0 — Material base (semanas 1-3, paralelo a A0-A3)
- Cartão corporativo CNPJ com limite alto (R$10-50k)
- Conta bancária PJ confirmada + extrato 3m
- Termos de uso, política de privacidade, AUP publicados (mesmos da Trilha A3)
- Acordo de processamento de dados LGPD/GDPR redigido
- Compliance program interno documentado (em vek1/compliance-program nova nota)
Fase B1 — Business case + letters (semanas 2-6, conforme tração)
- Pitch deck: mercado, projeção 12-24m, métricas vek1
- 3-10 customer commitment letters em papel timbrado
- Casos de uso: screencasts, métricas, depoimentos
- Diagrama de arquitetura (reusa vek1/architecture)
- Plano de suporte + SLA documentado
Fase B2 — Aplicação (mês 2-3, depois Tech Provider rodando)
- Submeter no portal Solution Partner Meta
- Entrevista solutions engineer Meta (call em inglês)
- Responder follow-ups Meta
Fase B3 — Aprovação (mês 3-8, sem prazo garantido)
- Solution Partner Agreement assinado
- Linha de crédito Meta concedida
- Dispara Fase A8 (Solution Partner activation)
6. Gotchas / riscos
Técnicos
- Número não pode estar em 2 lugares: número em Evolution (Baileys) precisa deslogar + remover do WhatsApp antes de registrar via Cloud API. Janela de downtime por lojista durante migração — comunica antecedência.
- Custo per-conversation (não per-message): janela 24h = 1 conversation. Brasil aprox (preços mudam):
utility~$0.008,marketing~$0.0625,servicegrátis em janela aberta. Modelar em vek1/pricing-design — pode comer margem se lojista manda muito broadcast. - Rate limit Meta: tier 1 começa em 250 conversations/24h, escala automático conforme uso saudável.
- 9º dígito brasileiro:
wa_idàs vezes vem sem o 9 que o lead tem cadastrado com. Normalizar antes de comparar comleads.external_id(ver lógica emwhatsapp-handler.ts:243-258— adaptar, não copiar). - Sem
fromMeautomático: webhook só recebe inbound. Statuses (sent/delivered/read/failed) vêm emvalue.statuses[]separado. Atualmente o handler ignorafromMe(vide linha 210) — manter ignorando. - Media URL expira em 5 min: baixar imediatamente no webhook, não armazenar URL.
- Template params: variáveis em templates são posicionais (
{{1}},{{2}}). Validar contracomponents_schemaantes de enviar pra evitar 400. message_template_status_update: Meta avisa quando aprovar/rejeitar template — também passa no webhook. Handler obrigatório.- App in Live Mode: app fica em dev mode até passar review da Meta. Em dev mode, só números de teste recebem. Submeter pra app review antes de produção.
- Encrypted token at rest: tokens long-lived por lojista — nunca logar
wa_access_token. Usar mesma estratégia de vek1/decisions sobre tokens AbacatePay. - Token refresh: tokens long-lived (60 dias) precisam refresh. Cron diário verificando
waTokenRefreshedAt < now - 50 diaschama refresh.
Solution Partner-específicos
- Inadimplência lojista vira problema vek1: lojista usa 10k conversations, não paga plano vek1, Meta cobra vek1 mesmo assim. Controle de crédito: monitor de uso real-time, auto-suspend agent em atraso, cap de conversations no plano.
- Compliance violation de 1 lojista pode banir WABA inteira: spam pesado de 1 lojista penaliza o app vek1 inteiro. Auto-suspend agent quando webhook
account_alertschega + revisão manual + processo de unbanning Meta documentado. - Volume mínimo Solution Partner Agreement: pode ter mínimos contratuais. Se não atingir, multa ou downgrade. Negociar antes de assinar.
- Responsabilidade legal de dados: vek1 vira data processor formal Meta. LGPD compliance reforçado, audit log obrigatório (já tem
audit_log). - Onboarding KYC do lojista: lojista ainda precisa fazer business verification própria pra mensagens marketing fora de janela (
MARKETINGtemplates).SERVICE/UTILITYrodam sem verification do lojista.
7. Decisões tomadas vs em aberto
Tomadas (2026-05-27)
- ✅ Destino: Solution Partner (vek1 com payment ownership, cobra plano único)
- ✅ Gate: Tech Provider (Meta exige histórico antes de aprovar Solution Partner)
- ✅ Onboarding: Embedded Signup obrigatório (multi-tenant desde dia 1, sem form admin)
- ✅ Templates: por WABA do lojista (auto-criados no
/wa-connect) - ✅ Schema:
wa_templatesewa_message_statusestabelas próprias (vide §4.1)
Em aberto
- Lidar com lojistas legados que não querem migrar: manter Evolution indefinidamente ou forçar deadline?
- Pricing pass-through enquanto Tech Provider: lojista paga Meta direto (Opção 1). Quando Solution Partner: vek1 absorve no plano (Opção 2). Detalhar tiers em vek1/pricing-design
- Fallback se Cloud API cai: provavelmente não (mesmo número não roda nos 2), mas alarmar via Sentry/monitor + comunicação ao lojista
- Quem dispara
take_payment_ownership: opt-in do lojista (botão na UI) ou bulk migration automático quando Solution Partner aprovar? - Como tratar lojistas que não querem que vek1 tome payment ownership (lojista grande, prefere fatura Meta separada): manter
wa_payment_ownership='customer'indefinidamente como tier alternativo? Provavelmente sim — dois tiers de plano. - Onboarding: precisa criar Business da Kodama pro próprio uso ou já tem? (verificar)
8. Referências
Docs Meta
- Cloud API overview: https://developers.facebook.com/docs/whatsapp/cloud-api
- Embedded Signup: https://developers.facebook.com/docs/whatsapp/embedded-signup
- Webhook signature: https://developers.facebook.com/docs/messenger-platform/webhooks#payload-verification
- Pricing: https://developers.facebook.com/docs/whatsapp/pricing
- Tech Provider: https://developers.facebook.com/docs/whatsapp/embedded-signup/tech-providers
- Solution Partner (programa): https://business.facebook.com/business/help/solution-partner
- Payment ownership API: https://developers.facebook.com/docs/whatsapp/business-management-api/payment-management
- App Review: https://developers.facebook.com/docs/app-review
- Business Verification: https://www.facebook.com/business/help/2058515294227817
Notas vek1
- Estado atual integração: vek1/integrations
- Gotchas vek1: vek1/gotchas
- Decisões arquiteturais: vek1/decisions
- Pricing design (atualizar pós-Solution Partner): vek1/pricing-design
- Arquitetura (input pro pitch deck Trilha B): vek1/architecture
- Compliance program (criar): vek1/compliance-program
- Gap correspondente: vek1/gaps GAP-23