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
- Uma conta Pilot Status que comporte mais um número. No plano Free, isso quer dizer espaço na cota do plano. Em um plano pago, quer dizer espaço, ou uma forma de pagar por um número extra (saldo na carteira ou cartão salvo).
- 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. - 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.
Como as peças se encaixam
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 na hora. Ele não consome slot do plano e não é cobrado: o número conta — e o número extra é cobrado — na ativação, quando o signup é concluído (Cobrado na primeira conexão). Gere quando o cliente estiver realmente prestes a clicar, não no carregamento da página. Um placeholder que o cliente abandona fica na sua lista de números como “Aguardando conexão” até ser removido automaticamente, 24 horas depois de criado, ou até você apagá-lo pelo painel
/numbers.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.A chave ps_ vaza para o browser
A chave ps_ vaza para o browser
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.Falta allow-popups no sandbox do iframe
Falta allow-popups no sandbox do iframe
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".Chamar a API da Pilot Status direto do browser
Chamar a API da Pilot Status direto do browser
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.Registrar webhook sem o array events
Registrar webhook sem o array events
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.Tentar rodar FB.login no domínio do próprio builder
Tentar rodar FB.login no domínio do próprio builder
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 objetobranding.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.