- Você é um usuário normal da D-API (sua API key secreta fica no seu backend).
- Seu cliente conecta o número dele; a conexão fica sob a sua conta, identificada por
um
connectionId. - Cobrança da Meta é do seu cliente (ele usa o método de pagamento dele na WABA). A D-API só cobra a taxa de plataforma por conexão de você.
Como funciona
O Embedded Signup da Meta não roda no domínio do seu SaaS (a Meta exige domínios cadastrados). Por isso o fluxo roda numa página hospedada pela D-API (connect.d-api.cloud), que você abre como popup. O resultado volta pro seu app
por postMessage. Seu domínio não precisa ser cadastrado na Meta.
1
Crie uma publishable key
Uma publishable key (A resposta traz o
pk_live_…) é pública de propósito: só inicia o
onboarding — não envia mensagens nem lê dados. Crie no painel em
Integrações → Publishable Keys, ou via API com a sua API key secreta:key uma única vez — guarde.2
Abra o connect no seu frontend
Use o pacote Isso abre o popup hospedado, roda o Embedded Signup e resolve com o
d-api-sdk (entry d-api-sdk/connect):connectionId (e o access token da Meta). Também dá
para fazer o handshake “na mão”, em HTML puro — veja o
exemplo completo.3
Associe o connectionId ao seu cliente
Guarde o
connectionId retornado no seu banco, ligado ao cliente correspondente.
A partir daí, esse número é uma conexão normal da D-API.4
Envie mensagens pelo seu backend
Com a sua API key secreta (nunca no navegador), o
connectionId é o
sessionId nas rotas normais de envio:Coexistência (manter o app WhatsApp Business)
Por padrão o fluxo cria/onboarda um número novo na API Oficial. Se o seu cliente quer manter o app WhatsApp Business no mesmo número (coexistência), passemode: "coexistence":
connectionId. A diferença é o que a Meta pede ao cliente: escanear um QR no app
WhatsApp Business. A sincronização do histórico começa automaticamente e pode
levar alguns minutos; oriente o cliente a manter o app aberto.
Webhooks
Você define o webhook da conexão noconnect.start({ webhookUrl }), e pode alterar
depois pela rota de atualização de sessão. Os eventos chegam com o id da
conexão/sessão no payload — então você pode apontar todas as conexões para um único
endpoint e distribuir pelo connectionId.
O resultado chega por dois caminhos
Toda conexão criada pelo connect hospedado é anunciada duas vezes, sem opção a configurar:- o
connect.start()resolve no navegador com oconnectionIde o access token; - um webhook
session.createdvai para owebhookUrldaquela conexão, com todos os dados — incluindo o access token e o seumetadata.
window.opener utilizável, então o postMessage que o
start() espera nunca chega: a promise pode rejeitar com “Conexão cancelada”
enquanto a conexão foi criada normalmente. Em webview de app nativo vale o mesmo.
Correlacionar com o seu cliente (metadata)
metadata é um JSON livre seu, guardado com a conexão e devolvido intacto em
data.metadata do session.created. É o que permite ligar a conexão ao tenant
sem depender do retorno síncrono:
start() lança erro antes de abrir a janela. Guarde ali
identificadores, não objetos de negócio inteiros.
metadata é opcional e novo na versão 1.3.0 do SDK. Integrações em 1.2.0
ou anterior seguem funcionando sem alteração — apenas recebem metadata: {}
no evento.Formato dos eventos (webhookMode)
Você escolhe em que formato os eventos chegam:
webhookMode).
Access token da Meta
O resultado doconnect.start() traz também o access token da Meta daquela
conexão, já descriptografado — útil se você quer chamar a Graph API direto (por
exemplo, para gerenciar templates ou o catálogo por fora da D-API):
accessTokenKind: "permanent", accessTokenExpiresAt: null) — é o caso normal aqui. Os outros tipos
aparecem quando o token foi trocado manualmente: long_lived (~60 dias) e
short_lived (~1 hora).
Se preferir que o token não trafegue pelo navegador, ignore o campo e busque no
seu backend quando precisar, com a sua API key secreta:
Quando a conexão falha
Se o onboarding não conclui, a janela hospedada mostra o motivo em português, com o código embaixo — um print dela já serve para o suporte. O mesmo motivo volta para o seu app:connect.start() rejeita com essa mensagem. Se você fez o
handshake na mão, cheque o ok antes de ler o data — numa falha não existe
data, e ler data.connectionId direto faz você perder o motivo real:
trial_required, plan_not_ready e billing_not_ready são sobre a sua
conta D-API, não a do seu cliente — é a taxa de plataforma por conexão. A
cobrança das conversas continua sendo da conta Meta do cliente.Cobrança (pass-through)
A cobrança das conversas pela Meta é do seu cliente: durante o Embedded Signup ele usa o método de pagamento da conta Meta dele na WABA. A D-API não cobra as conversas — só a taxa de plataforma por conexão ativa, na sua conta. Lembre o seu cliente de finalizar o método de pagamento no WhatsApp Manager, se a Meta pedir.Segurança
- A publishable key é pública por design: escopo
onboardapenas, com rate limit. O pior caso de vazamento é alguém iniciar um popup de Embedded Signup — sem acesso a dados nem envio. - A sua API key secreta nunca vai para o navegador — só no seu backend.
- O resultado do onboarding só é entregue para a janela que abriu o popup, e o SDK valida que a mensagem veio do domínio hospedado da D-API.
- O access token da Meta volta pela página hospedada no fragmento da URL
(
#…), que o navegador nunca envia ao servidor — ele não aparece em log de acesso nem emReferer, e a página o apaga da barra de endereço depois de repassar. Daí em diante a responsabilidade é sua: mande para o backend e não persista no browser.
Exemplo completo (HTML puro)
Um único arquivo, sem framework, mostrando o fluxo popup + postMessage passo a passo.
