Skip to main content

Cadastro de WhatsApp para builders de IA

Você está construindo um CRM ou SaaS no Lovable, Replit, Bolt, v0 ou Cursor, e seus clientes precisam conectar o próprio número oficial de WhatsApp. Esta página entrega quatro prompts. Cole no seu builder, na ordem, e o fluxo de cadastro funciona.
Prefere partir de um código que já roda? Baixe o demo (licença MIT) deste fluxo exato (backend Node + frontend React, com modelos de botão prontos): embedded-signup-demo.zip. Jogue no seu builder, ou rode local com npm run dev.

Por que o botão hospedado, e não FB.login na sua página

Seu builder publica num domínio dele — algo.lovable.app, algo.replit.app, uma URL de preview nova a cada deploy. Esse domínio muda por projeto, e quase sempre por push. A Meta valida o domínio que executa o FB.login. Colocar o botão do Facebook direto na sua própria página significaria registrar cada um desses domínios no app Meta da Pilot Status, um a um, para sempre. Isso não escala, e não está disponível. O botão hospedado resolve isso rodando o cadastro dentro de um iframe servido por connect.pilotstatus.com.br — um domínio que a Pilot Status já possui e já registrou. O Facebook checa a origem do iframe, não a da sua página. Sua página pode viver em qualquer domínio, inclusive numa URL de preview que não existia cinco minutos atrás, e o popup abre normalmente. Nada para registrar. Nada para esperar. Este é o caminho certo para apps hospedados em builder, não um remendo.
Se você tem um domínio fixo e quer o botão do Facebook na sua própria marcação, use Embedded Signup no seu próprio app — controle total do botão, ao custo de operar o SDK JavaScript do Facebook por conta própria. Para todos os detalhes do embed hospedado (parâmetros da URL, eventos de postMessage e branding.button), veja Incorpore a Página de Conexão.

Antes de começar

  1. Uma conta Pilot Status com um slot de número livre no plano.
  2. Uma chave de API ps_ com escopo de tenant, na aba API do seu perfil (/profile). Chave com escopo de número não serve — gerar um link de cadastro cria um número novo, então a chave não pode estar presa a um número existente.
  3. Um backend. Todos os builders acima conseguem rodar um: um servidor no Replit, um route handler Next.js no v0, uma Supabase Edge Function no Lovable ou no Bolt. Ele é necessário porque o browser não pode chamar a API da Pilot Status diretamente.
A chave ps_ é um segredo de servidor. Ela autoriza tudo na sua conta. Nunca pode aparecer em código de frontend, nem em variável com prefixo VITE_, NEXT_PUBLIC_, REACT_APP_ ou PUBLIC_ — esses prefixos são compilados dentro do bundle JavaScript que seus usuários baixam. Todos os prompts abaixo repetem isso, porque os builders de IA erram nesse ponto por padrão.

Como as peças se encaixam

A chave ps_ só existe na última linha.

Os quatro prompts

Cole na ordem. Cada um é autossuficiente — a IA que lê o prompt não tem acesso a esta página, então tudo que ela precisa está escrito dentro dele.

Prompt 1 — Rota de backend que gera o token de cadastro

Gerar um link cria um número placeholder e consome um slot do plano na hora. Gere quando o cliente estiver realmente prestes a clicar, não no carregamento da página.

Prompt 2 — Iframe no frontend com o sandbox correto

Prompt 3 — Tratar o connect:paired e salvar o número

Prompt 4 — Registrar o webhook que entrega as mensagens recebidas

Erros que a IA costuma cometer

Estes cinco respondem por quase toda integração quebrada. Se algo não funcionar, confira nesta ordem.
O builder lê de VITE_PILOT_KEY ou NEXT_PUBLIC_PILOT_KEY porque é o caminho mais rápido para o fetch compilar. Esses prefixos embutem o valor no bundle publicado, então quem abrir o DevTools é dono da sua conta. A chave fica numa variável de servidor comum, como PILOT_TENANT_KEY, lida só por código de backend. Se você achar a chave no bundle, rotacione ela no painel antes de corrigir o código.
Sem allow-popups e allow-popups-to-escape-sandbox, o popup do Facebook é bloqueado pelo browser sem erro, sem aviso no console e sem falha visível. O botão simplesmente não faz nada ao ser clicado, o que faz todo mundo caçar bug na lógica do token. O atributo completo é sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox".
O código gerado faz fetch("https://pilotstatus.com.br/v1/...") — ou lê GET /api/health — de dentro de um componente. O CORS da API é uma allowlist estrita dos domínios da própria Pilot Status, então uma requisição de *.lovable.app ou *.replit.app nunca recebe o header Access-Control-Allow-Origin e é bloqueada. Nenhuma chave de API muda isso. Neste fluxo o frontend nem precisa disso: o botão já vem pronto dentro do iframe. Toda chamada à Pilot Status passa pelo seu backend, e a única serve para gerar o link.
POST /v1/webhooks aceita um corpo sem events, responde sucesso e depois não entrega nada. Não há aviso. Envie um array explícito — ["messages"] para número META, ou ["*"] para tudo.
A IA conhece a receita padrão do Facebook Embedded Signup e vai produzi-la com prazer. Numa URL *.lovable.app ou *.replit.app isso não funciona: a Meta valida a origem que executa o FB.login, e esse domínio não está registrado no app Meta da Pilot Status — nem pode estar, já que muda por projeto e por deploy. O iframe hospedado existe exatamente para evitar isso: o SDK do Facebook, o appId e o config_id vivem dentro dele, no domínio da Pilot Status. Se um prompt começar a puxar connect.facebook.net/en_US/sdk.js, servir appId/config_id para o cliente ou ler /api/health no browser, você está no caminho errado — neste fluxo nada disso existe no seu app; o iframe cuida de tudo.

Sobre o estilo do botão

A aparência do botão hospedado vem do objeto branding.button enviado na geração do token — veja o Prompt 1. Essa requisição é autenticada pela sua chave ps_, então o estilo é sempre atribuível à sua conta. Deliberadamente não existe query parameter para o estilo do botão. Não tente passar cores, rótulos ou logo na URL do iframe; serão ignorados. O marcador “secured by pilotstatus.com.br” abaixo do botão aparece por padrão. O branding.button.hideProvenance consegue escondê-lo, mas só no modo botão e só quando esse recurso estiver habilitado para a sua conta — caso contrário o valor é ignorado e o marcador continua aparecendo. Fale com o suporte se precisar disso.

Próximos passos

O número está conectado e os webhooks estão chegando. Agora envie alguma coisa.

Enviar mensagens

Seu primeiro envio com POST /v1/messages/send — templates, texto livre e mídia.

Referência do embed hospedado

Todos os parâmetros da URL do iframe, os eventos de postMessage e as opções de branding.button.

Embedded Signup no seu próprio app

A alternativa com controle total, para quando você tem um domínio fixo e integra o SDK do Facebook.