<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Beça IA</title>
  <link>https://becaia.com.br/</link>
  <description>Blog do Weslley Maioli, o Beca: tráfego pago, automação de WhatsApp e IA aplicada a marketing, com o que funcionou, o que quebrou e o passo a passo.</description>
  <language>pt-br</language>
  <lastBuildDate>Tue, 13 Oct 2026 09:00:00 -0300</lastBuildDate>
  <atom:link href="https://becaia.com.br/feed.xml" rel="self" type="application/rss+xml"/>
  <item>
    <title>Prova social falsa em página de grupo de ofertas: por que eu não uso</title>
    <link>https://becaia.com.br/artigos/prova-social-falsa-pagina-grupo-ofertas/</link>
    <guid isPermaLink="true">https://becaia.com.br/artigos/prova-social-falsa-pagina-grupo-ofertas/</guid>
    <pubDate>Tue, 13 Oct 2026 09:00:00 -0300</pubDate>
    <dc:creator>Weslley Maioli</dc:creator>
    <category>Opinião sem hype</category>
    <description>Contador de vagas que sempre cai e “fulano entrou agora” gerado por script são publicidade enganosa pelo CDC. O que eu coloquei no lugar, com dado real.</description>
    <content:encoded><![CDATA[<p><strong>Resposta rápida:</strong> Contador de vagas que cai igual pra todo visitante e aviso de “fulano entrou no grupo agora” gerado por script com nomes fixos são publicidade enganosa pelo Código de Defesa do Consumidor (art. 37) e podem configurar crime (art. 67). No lugar, use dado real: número de membros com data, ofertas que de fato saíram no grupo e escassez só quando o grupo está perto do limite de 1.024 pessoas.</p>
<p>Quando fui montar a página de convite do meu grupo de ofertas no WhatsApp, abri o código de uma página de grupo de achadinhos que eu tinha como referência, pra entender como ela convertia. Os dois gatilhos mais fortes dela eram inventados.</p>
<h2 id="o-que-tinha-no-codigo">O que tinha no código</h2>
<p>Três peças, todas rodando no navegador de quem visita:</p>
<ul><li>uma lista fixa de nomes e sobrenomes comuns;</li><li>um script que, a cada 1,8 a 3 segundos, sorteia um nome dessa lista e mostra “✅ fulano entrou no grupo agora”;</li><li>um contador que começa em “Restam 25 vagas hoje” e desce até 5 enquanto a pessoa está na página.</li></ul>
<p>Simplificando, a lógica era esta (o código é meu, reescrito pra mostrar o padrão):</p>
<pre><code class="language-js">const nomes = [&quot;Ana Souza&quot;, &quot;Bruno Lima&quot;, &quot;Carla Dias&quot;];  // lista fixa
let vagas = 25;
function passo() {
  if (vagas &lt;= 5) return;                              // para em 5: &quot;últimas vagas&quot;
  vagas--;                                             // as vagas só descem
  mostrar(`✅ ${sortear(nomes)} entrou no grupo agora`);  // ninguém entrou
  setTimeout(passo, 1800 + Math.random() * 1200);      // de 1,8 a 3 segundos
}
passo();</code></pre>
<p>Ninguém entrou no grupo naquele momento, e as vagas não estavam acabando. Basta recarregar a página: o contador volta pra 25 e os mesmos nomes recomeçam a entrar.</p>
<h2 id="por-que-eu-nao-uso-o-que-diz-a-lei">Por que eu não uso: o que diz a lei</h2>
<p>O Código de Defesa do Consumidor proíbe publicidade enganosa no artigo 37. O parágrafo primeiro define como enganosa a informação “inteira ou parcialmente falsa” capaz de induzir o consumidor ao erro sobre quantidade, características ou quaisquer outros dados do produto ou serviço. Um contador de vagas que não conta nada e uma lista de pessoas que não entraram são exatamente isso.</p>
<p>O artigo 67 vai além e trata como crime fazer ou promover publicidade que se sabe ou deveria saber ser enganosa, com pena de detenção de três meses a um ano e multa. Grupo de ofertas com link de afiliado existe pra vender, então a página de convite é publicidade. Não sou advogado. É a leitura direta do texto da lei, que está nas fontes no fim do artigo.</p>
<h2 id="o-problema-que-vem-antes-da-lei-confianca">O problema que vem antes da lei: confiança</h2>
<p>Grupo de ofertas vive de uma coisa: a pessoa acreditar que o “70% OFF” é de verdade. Quem recarrega a página e vê as mesmas 25 vagas, ou vê o mesmo nome “entrar” duas vezes, tem um motivo pra desconfiar de tudo que vem depois, inclusive das ofertas. O gatilho inventado empresta conversão de hoje contra a credibilidade de amanhã.</p>
<h2 id="o-que-eu-coloquei-no-lugar">O que eu coloquei no lugar</h2>
<div class="tabela"><table><thead><tr><th scope="col">O que a referência usava</th><th scope="col">O que eu uso</th></tr></thead><tbody><tr><td>“Fulano entrou agora” a cada 2 ou 3 segundos, inventado</td><td>Saiu. Uma página estática não sabe quem entrou de verdade. Com servidor, dá pra mostrar cliques reais no botão, sem inventar nome.</td></tr><tr><td>“Restam 25 vagas” caindo até 5, igual pra todo mundo</td><td>Número real de membros, com data. “Restam X vagas” só aparece quando o grupo passa de 900 pessoas, perto do limite de 1.024 do WhatsApp.</td></tr><tr><td>Um depoimento assinado só com nome e inicial, sem como conferir</td><td>Um carrossel com ofertas reais da Shopee, com foto, desconto e a data em que foram garimpadas.</td></tr></tbody></table></div>
<p>Duas regras fazem isso funcionar:</p>
<ol><li><strong>Todo número tem data.</strong> Na minha página, o aviso de membros some sozinho quando a contagem fica mais velha que 10 dias. Número velho vira mentira sem ninguém perceber.</li><li><strong>Escassez só quando é real.</strong> Grupo de WhatsApp lota de verdade. Quando chega perto do limite, o aviso aparece, e é a hora de criar o próximo grupo e trocar o link.</li></ol>
<h2 id="mas-a-versao-falsa-nao-converte-mais">Mas a versão falsa não converte mais?</h2>
<p>Pode converter. Eu não testei a versão falsa contra a verdadeira, e não vou testar: não faço teste A/B de coisa que eu não publicaria. O risco da versão falsa é jurídico e de confiança, e os dois aparecem depois que a campanha já gastou o dinheiro.</p>
<h2 id="checklist-antes-de-publicar-uma-pagina-de-convite">Checklist antes de publicar uma página de convite</h2>
<ul><li>Cada número da página tem fonte e data?</li><li>O contador mostra algo que aconteceu de fato, ou só anima a tela?</li><li>Se a pessoa recarregar a página, ela vê a mesma coisa (e isso continua verdade)?</li><li>A escassez tem um limite real por trás?</li><li>As ofertas mostradas como prova são reais e têm data?</li></ul>]]></content:encoded>
  </item>
  <item>
    <title>Como medir o clique em “Entrar no grupo do WhatsApp” no GTM sem perder a conversão</title>
    <link>https://becaia.com.br/artigos/gtm-medir-clique-entrar-grupo-whatsapp/</link>
    <guid isPermaLink="true">https://becaia.com.br/artigos/gtm-medir-clique-entrar-grupo-whatsapp/</guid>
    <pubDate>Tue, 13 Oct 2026 09:00:00 -0300</pubDate>
    <dc:creator>Weslley Maioli</dc:creator>
    <category>Bastidores de gestor de tráfego</category>
    <description>Página intermediária com dataLayer, eventCallback e eventTimeout: a tag de conversão dispara antes de a pessoa ir pro WhatsApp. Código, configuração e teste.</description>
    <content:encoded><![CDATA[<p><strong>Resposta rápida:</strong> Mande o botão para uma página intermediária sua, como /entrar/, que empurra um evento no dataLayer com eventCallback e eventTimeout e só redireciona pro WhatsApp depois que o GTM termina de processar as tags. Se o GTM estiver bloqueado, o eventTimeout libera o redirecionamento em 1,5 segundo. No GTM, um acionador de Evento personalizado dispara as tags de conversão.</p>
<p>Botão de WhatsApp é um dos piores lugares pra medir conversão. A pessoa toca, o app abre por cima, e a sua página sai da tela antes de a tag terminar de mandar o evento. Na página do meu grupo de ofertas eu resolvi isso com uma página intermediária pequena, e é ela que eu mostro aqui.</p>
<h2 id="por-que-o-clique-se-perde">Por que o clique se perde</h2>
<p>Num link direto pro <code>chat.whatsapp.com</code>, a navegação começa no toque. As tags de conversão (Meta, Google Ads, GA4) são carregadas e disparadas de forma assíncrona, então podem não terminar antes de a página ser trocada. No celular, o app do WhatsApp ainda abre por cima e deixa a aba em segundo plano.</p>
<p>O resultado é conversão subnotificada: a campanha manda gente pro grupo, e a conta de anúncio não fica sabendo. Com menos conversão registrada, a otimização da campanha trabalha com menos sinal.</p>
<h2 id="o-fluxo">O fluxo</h2>
<ol><li>Todo botão “Entrar no grupo” da página aponta pra <code>/entrar/</code>, uma página sua.</li><li>A <code>/entrar/</code> carrega o GTM e empurra o evento <code>entrar_grupo</code> no <code>dataLayer</code>.</li><li>O <code>eventCallback</code> só redireciona depois que o GTM termina de processar as tags desse evento.</li><li>O <code>eventTimeout</code> de 1.500 milissegundos garante o redirecionamento mesmo se alguma tag travar.</li><li>No celular, a página tenta abrir o app com <code>whatsapp://chat?code=</code>. Se a página continuar visível 1,5 segundo depois, ela vai pro link web.</li></ol>
<h2 id="o-codigo-da-pagina-entrar">O código da página /entrar/</h2>
<p>O snippet padrão do GTM vai no <code>&lt;head&gt;</code> dessa página, igual em qualquer outra. Depois, este script:</p>
<pre><code class="language-html">&lt;script&gt;
  var link = &quot;https://chat.whatsapp.com/SEU-CODIGO&quot;;
  var convite = link.match(/chat\.whatsapp\.com\/(?:invite\/)?([A-Za-z0-9]{10,40})/);
  var celular = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
  var foi = false;

  function abrir() {
    if (foi) return;  // nunca redireciona duas vezes
    foi = true;
    if (convite &amp;&amp; celular) {
      window.location.href = &quot;whatsapp://chat?code=&quot; + convite[1];
      setTimeout(function () { if (!document.hidden) window.location.href = link; }, 1500);
    } else {
      window.location.href = link;
    }
  }

  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    event: &quot;entrar_grupo&quot;,
    eventCallback: function () { setTimeout(abrir, 100); },
    eventTimeout: 1500
  });
  setTimeout(abrir, 1500);  // GTM bloqueado: vai do mesmo jeito
&lt;/script&gt;</code></pre>
<p>Três detalhes que fazem diferença:</p>
<ul><li><strong>O <code>setTimeout</code> extra no fim.</strong> O <code>eventTimeout</code> só funciona se o GTM carregar. Se um bloqueador de anúncio impedir o <code>gtm.js</code>, o <code>dataLayer.push</code> vira um item numa lista que ninguém lê, e o <code>eventCallback</code> nunca é chamado. Sem esse último <code>setTimeout</code>, a pessoa ficaria presa na tela de “Abrindo o WhatsApp”.</li><li><strong>A variável <code>foi</code>.</strong> Garante que o redirecionamento aconteça uma vez só, venha do callback ou do tempo limite.</li><li><strong>O link com <code>?mode=</code> no fim.</strong> O link que o WhatsApp copia pode vir com parâmetros depois do código. A expressão regular pega só o código, e o resto não atrapalha.</li></ul>
<h2 id="configurando-no-gtm">Configurando no GTM</h2>
<ol><li>Crie um acionador do tipo <strong>Evento personalizado</strong> com o nome de evento <code>entrar_grupo</code>.</li><li>Pendure nele as tags de conversão: o evento Lead no pixel da Meta, a conversão do Google Ads e, no GA4, o evento recomendado <code>generate_lead</code>.</li><li>Publique e confira no modo de visualização do GTM se o evento aparece quando você toca em “Entrar no grupo”.</li></ol>
<h2 id="como-eu-testei-sem-sujar-os-numeros">Como eu testei sem sujar os números</h2>
<p>Testar com o GTM de verdade dispararia conversões reais na conta de anúncio. Então, no navegador de teste, interceptei o carregamento do <code>googletagmanager.com</code> e do <code>chat.whatsapp.com</code>. No lugar do GTM entrou um script que só chama o <code>eventCallback</code>, e o link do WhatsApp foi respondido pelo próprio teste, sem abrir o grupo.</p>
<div class="tabela"><table><thead><tr><th scope="col">Cenário</th><th scope="col">Tempo até ir pro WhatsApp</th></tr></thead><tbody><tr><td>GTM respondendo</td><td>0,24 segundo</td></tr><tr><td>GTM bloqueado</td><td>1,56 segundo (o tempo limite)</td></tr></tbody></table></div>
<p>Com o GTM funcionando, a pessoa sai mais rápido do que esperaria um tempo fixo. Com ele bloqueado, perde um segundo e meio, mas chega no grupo.</p>
<h2 id="cuidados">Cuidados</h2>
<ul><li><strong>Tire a /entrar/ do Google.</strong> Coloque <code>noindex</code> na página e um <code>Disallow</code> no <code>robots.txt</code>. Robô que visita essa página dispara evento de conversão.</li><li><strong>Não duplique a conversão.</strong> Se a página já tem o pixel da Meta instalado direto, além do GTM, o mesmo clique conta duas vezes. Escolha um caminho.</li><li><strong>Guarde o link num lugar só.</strong> Grupo de WhatsApp lota com 1.024 pessoas. Quando acontecer, você troca o link num arquivo, e todos os botões e mensagens antigas continuam levando pro grupo certo.</li></ul>]]></content:encoded>
  </item>
  <item>
    <title>Evolution API: erro 500 “Cannot convert undefined or null to object” ao listar grupos</title>
    <link>https://becaia.com.br/artigos/evolution-api-erro-500-listar-grupos/</link>
    <guid isPermaLink="true">https://becaia.com.br/artigos/evolution-api-erro-500-listar-grupos/</guid>
    <pubDate>Tue, 13 Oct 2026 09:00:00 -0300</pubDate>
    <dc:creator>Weslley Maioli</dc:creator>
    <category>Automação de atendimento e vendas</category>
    <description>Na Evolution API 2.4.0, as rotas de grupo dão erro 500 mesmo com o WhatsApp conectado. A causa está no Express 5, e o conserto é uma linha.</description>
    <content:encoded><![CDATA[<p><strong>Resposta rápida:</strong> Na Evolution API 2.4.0, as rotas GET de grupo, como fetchAllGroups e findGroupInfos, dão erro 500 com a mensagem “Cannot convert undefined or null to object” porque o validador faz Object.assign no corpo da requisição, e no Express 5 esse corpo é undefined quando você não envia nada. O conserto é mandar um JSON vazio, {}, no corpo do GET, com Content-Type application/json.</p>
<p>Conectei o WhatsApp de um painel de ofertas na Evolution API, o pareamento pelo QR code funcionou, e na hora de listar os grupos veio isto:</p>
<pre><code class="language-json">{&quot;status&quot;: 500, &quot;error&quot;: &quot;Internal Server Error&quot;, &quot;response&quot;: {&quot;message&quot;: &quot;Cannot convert undefined or null to object&quot;}}</code></pre>
<p>O WhatsApp estava conectado: o <code>connectionState</code> respondia <code>open</code>. Mesmo assim, toda chamada de grupo voltava com esse erro, e voltava na hora. Erro que responde em zero segundo não chegou nem a falar com o WhatsApp. Quebrou antes.</p>
<h2 id="onde-o-erro-acontece">Onde o erro acontece</h2>
<p>Antes de executar uma rota, a Evolution passa a requisição por um validador. A rota <code>fetchAllGroups</code> usa um validador próprio (o que exige o parâmetro <code>getParticipants</code>), e a <code>findGroupInfos</code> usa o validador de grupo (o que exige o <code>groupJid</code>). Os dois fazem a mesma coisa no começo: pegam o corpo da requisição e copiam campos para dentro dele com <code>Object.assign</code>.</p>
<p>Este é o trecho do validador do <code>fetchAllGroups</code> no <code>dist/main.js</code> da versão 2.4.0. O código é minificado; reescrevi os nomes pra ficar legível:</p>
<pre><code class="language-js">const query = request.query;   // ?getParticipants=false
const body = request.body;     // corpo da requisição
Object.assign(body, query);    // se body for undefined, quebra aqui</code></pre>
<p>Quando <code>body</code> é <code>undefined</code>, o JavaScript lança <code>TypeError: Cannot convert undefined or null to object</code>, e a Evolution devolve exatamente essa mensagem dentro do erro 500.</p>
<h2 id="por-que-o-corpo-chega-undefined-o-express-5">Por que o corpo chega undefined: o Express 5</h2>
<p>A Evolution 2.4.0 roda em cima do Express 5. Conferi no container: o <code>node_modules/express/package.json</code> diz versão 5.2.1. E o guia oficial de migração do Express 5 lista esta mudança: o <code>req.body</code> agora vem <code>undefined</code> quando nenhum corpo foi lido. No Express 4 ele vinha como objeto vazio, <code>{}</code>.</p>
<p>Num GET comum, o cliente não manda corpo nem <code>Content-Type</code>. O leitor de JSON não roda, o <code>req.body</code> fica <code>undefined</code>, e o validador quebra ao tentar copiar campos para dentro dele. No Express 4 o mesmo código funcionaria, porque o corpo seria <code>{}</code>. Não testei a Evolution 2.3, então não afirmo em qual versão o problema começou.</p>
<h2 id="o-conserto-mande-no-corpo-do-get">O conserto: mande {} no corpo do GET</h2>
<p>Mande um JSON vazio no corpo, com o cabeçalho <code>Content-Type: application/json</code>. O leitor de JSON roda, o corpo vira <code>{}</code> e o validador segue normal.</p>
<p>Com curl (o <code>-X GET</code> é obrigatório, porque o <code>-d</code> sozinho transforma a chamada em POST):</p>
<pre><code class="language-bash">curl -X GET &quot;https://SUA-EVOLUTION/group/fetchAllGroups/SUA-INSTANCIA?getParticipants=false&quot; \
  -H &quot;apikey: SUA-CHAVE&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d &#x27;{}&#x27;</code></pre>
<p>Com Python e httpx, que foi o que usei no painel:</p>
<pre><code class="language-python">resposta = httpx.request(
    &quot;GET&quot;,
    f&quot;{url}/group/fetchAllGroups/{instancia}&quot;,
    params={&quot;getParticipants&quot;: &quot;false&quot;},
    json={},  # o corpo vazio que evita o erro 500
    headers={&quot;apikey&quot;: chave},
    timeout=240,
)</code></pre>
<p>Um cuidado: no navegador e no <code>fetch</code> do Node, GET com corpo não é permitido, e a chamada falha antes de sair. Faça essa chamada do servidor, com um cliente HTTP que aceite corpo em GET, como curl, httpx ou requests.</p>
<h2 id="mandar-corpo-em-get-e-gambiarra">Mandar corpo em GET é gambiarra?</h2>
<p>É um contorno para um bug do servidor, e o padrão HTTP permite. A RFC 9110 diz que conteúdo num GET não tem significado definido e que alguns servidores podem recusar a requisição. Aqui a Evolution aceita, lê o <code>{}</code> e ignora. Quando o validador for corrigido, o corpo vazio continua inofensivo.</p>
<h2 id="outras-pegadinhas-da-evolution-2-4-0-que-encontrei">Outras pegadinhas da Evolution 2.4.0 que encontrei</h2>
<div class="tabela"><table><thead><tr><th scope="col">O que acontece</th><th scope="col">O que fazer</th></tr></thead><tbody><tr><td><code>fetchAllGroups</code> busca a foto de cada grupo, um por um. Com 571 grupos, levou 70 segundos.</td><td>Rode em segundo plano, guarde a lista e use timeout alto (usei 240 s).</td></tr><tr><td><code>fetchInstances</code> ignora o filtro <code>?instanceName=</code> e devolve todas as instâncias do servidor.</td><td>Filtre pelo nome no seu código. Cuidado se o servidor tem instâncias de clientes.</td></tr><tr><td>Mandar mensagem com a instância desconectada deixou a requisição de <code>sendText</code> mais de 30 segundos sem resposta.</td><td>Confira o <code>connectionState</code> antes de enviar e use timeout curto.</td></tr></tbody></table></div>
<h2 id="como-saber-se-a-sua-evolution-tem-esse-problema">Como saber se a sua Evolution tem esse problema</h2>
<ol><li>Veja a versão: um GET na raiz da sua Evolution devolve a versão no JSON.</li><li>Veja o Express: <code>docker exec NOME-DO-CONTAINER grep &#x27;&quot;version&quot;&#x27; /evolution/node_modules/express/package.json</code>. Versão 5 é o sinal.</li><li>Faça o teste: chame o <code>fetchAllGroups</code> sem corpo e depois com <code>-d &#x27;{}&#x27;</code>. Se só a segunda funciona, é esse o problema.</li></ol>
<p>Versões em que testei: Evolution API 2.4.0 (imagem homolog), Baileys 7.0.0-rc13 e Express 5.2.1, em outubro de 2026.</p>]]></content:encoded>
  </item>
</channel>
</rss>
