Pular para o conteúdo principal
Incorpore a caixa de entrada do WhatsApp (Chat) do Pilot Status no seu próprio SaaS, white-label, por meio de um iframe hospedado mais o SDK @pilot-status/embed.

Pré-requisitos compartilhados (Chat + Connect)

  1. Chave de API do tenant (ps_, com escopo de tenant) — gerada em Perfil → API. Ela nunca sai do seu backend. (Uma chave com escopo de número só pode incorporar o próprio número.)
  2. SDK<script src="https://embed.pilotstatus.com.br/embed.js"> ou npm i @pilot-status/embed; chame PilotStatus.init().
  3. allowedOrigins — ao emitir o token do Chat, liste as origens exatas (scheme://host[:port], 1–20) autorizadas a incorporar.
  4. Token por superfície — o Chat usa POST /v1/embed/sessions (surface: "chat", um JWT de curta duração mantido em memória). O Connect usa POST /v1/numbers/remote-pairing (um token de pareamento na URL — veja Incorpore a Página de Connect).
  5. Renovação — o TTL do token do Chat é de 15 min com uma janela de atualização deslizante; ao expirar por completo, o SDK chama onSessionExpired.

Fluxo do Chat

1

Configuração única

Seu backend lista os números com GET /v1/numbers (x-api-key) e armazena o mapa cliente → [whatsappNumberIds].
2

Emita um token a cada carregamento de página

Seu frontend solicita um token ao seu backend; o backend chama:
curl -X POST "https://pilotstatus.com.br/v1/embed/sessions" \
  -H "x-api-key: ps_your_tenant_key" \
  -H "Content-Type: application/json" \
  -d '{
    "surface": "chat",
    "whatsappNumberIds": ["wn_abc123"],
    "allowedOrigins": ["https://app.yoursaas.com"],
    "ttlSeconds": 900
  }'
Resposta 201: { token, surface, whatsappNumberIds, allowedOrigins, expiresAt }. O backend encaminha apenas o JWT para o frontend.
3

Monte a caixa de entrada

<script src="https://embed.pilotstatus.com.br/embed.js"></script>
<div id="inbox"></div>
<script>
  PilotStatus.init();
  const { token } = await fetch("/api/my-saas/embed-token").then(r => r.json());
  const chat = PilotStatus.chat.mount("#inbox", {
    token, locale: "en", theme: "light",
    onUnreadCount: (n) => {},
    onConversationOpened: (id) => {},
    onSessionExpired: async () => {
      const { token: t } = await fetch("/api/my-saas/embed-token").then(r => r.json());
      chat.destroy();
      PilotStatus.chat.mount("#inbox", { token: t, locale: "en" });
    },
  });
  // chat.setTheme("dark"); chat.setLocale("pt"); chat.destroy();
</script>

Protocolo postMessage (pai ↔ iframe)

Envelope: { source: "pilot-status-embed", v: 1, type, payload }. Mensagens sem esse source são ignoradas; a origem é validada em ambos os lados.
PassoQuemMensagem
ASDK (pai)cria <iframe src="chat.pilotstatus.com.br/?parentOrigin=<origin>"> (sandbox allow-scripts allow-same-origin allow-forms allow-popups)
Biframe → paiready (o SDK valida origin === chat.pilotstatus.com.br)
CSDK → iframeinit{ token, locale, theme } (o iframe valida origin === parentOrigin)
Diframemantém o token em memória apenas — nunca na URL/localStorage
Eiframe → pairesize{height}, chat:unread-count{count}, chat:conversation-opened{conversationId}, session-expired
SDK → iframeset-theme{theme}, set-locale{locale} em tempo de execução

Modelo de segurança

  • A x-api-key fica apenas no seu backend; o navegador só manipula o JWT de curta duração.
  • O JWT tem escopo rígido para os seus whatsappNumberIds — as rotas do chat reverificam a cada requisição (defesa em profundidade).
  • Token apenas em memória, TTL de ~15 min com atualização deslizante; allowedOrigins fixa quem pode incorporar; a origem do postMessage é validada em ambos os lados.

Relacionados