waiaconnect

Documentação / Completo

Onboarding hospedado (o seu cliente conecta o número dele)

POST /v1/onboarding-links

Markdown

Quando o número é do seu cliente e você não quer pedir as credenciais da Meta dele, ele mesmo conecta o WhatsApp numa página com a sua marca — sem login, sem ver a nossa marca, sem que você toque no app da Meta. Há duas formas, escolha a sua:

1. Mande um link (sem código). Você gera o link no painel (Números → 'Enviar link a um cliente') e o passa você mesmo (por WhatsApp, e-mail, o que for). Ideal para conectar clientes um a um. Você não precisa escrever nada.

2. Integre no seu sistema (com código). O seu cliente toca em 'Conectar WhatsApp' dentro do SEU produto e se conecta sozinho, sem que você intervenha. O fluxo: o seu backend chama POST /v1/onboarding-links com label, externalId (e returnOrigin se for usar popup) → recebe uma url → você o manda para lá por redirecionamento (o mais robusto, recomendado no celular) ou popup (parece embutido). Quando termina, o resultado chega por postMessage E pelo webhook connection.created com o seu externalId. O snippet de frontend e os exemplos de backend estão abaixo.

Nos dois casos você pode fixar o modo (cloud_api/coexistence; se omitir, a página conecta por Coexistence sem oferecer escolha) e o idioma da página (lang: es/it/en/pt, ou omitir para detectar pelo navegador). O token é de uso único e vence: o vencimento é configurável (expiresIn em segundos, de 5 min a 30 dias) — pela API o padrão é 1 hora, pelo painel 7 dias.

curl -X POST https://api.waiaconnect.com/v1/onboarding-links \
  -H "Authorization: Bearer wc_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"López Pharmacy","externalId":"pharmacy-42","lang":"es","returnOrigin":"https://app.yourcompany.com","expiresIn":3600}'
# → { "url": "https://…/c/<token>", "expiresAt": "…", "publicId": "onb_…" }
# Send url to your customer. It is shown ONCE; the token is consumed only when they connect.
// Call this from YOUR backend when your customer taps "Connect WhatsApp".
const res = await fetch("https://api.waiaconnect.com/v1/onboarding-links", {
  method: "POST",
  headers: { "Authorization": "Bearer wc_live_YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
  "label": "López Pharmacy",
  "externalId": "pharmacy-42",
  "lang": "es",
  "returnOrigin": "https://app.yourcompany.com",
  "expiresIn": 3600
})
});
const { url } = await res.json(); // https://…/c/<token> — hand this to the browser (redirect or popup)
<?php
// Call this from YOUR backend when your customer taps "Connect WhatsApp".
$ch = curl_init("https://api.waiaconnect.com/v1/onboarding-links");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer wc_live_YOUR_API_KEY", "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode(["label" => "López Pharmacy","externalId" => "pharmacy-42","lang" => "es","returnOrigin" => "https://app.yourcompany.com","expiresIn" => 3600]),
]);
$out = json_decode(curl_exec($ch), true);
echo $out["url"]; // https://…/c/<token> — hand this to the browser (redirect or popup)
import requests
# Call this from YOUR backend when your customer taps "Connect WhatsApp".
res = requests.post("https://api.waiaconnect.com/v1/onboarding-links",
  headers={"Authorization": "Bearer wc_live_YOUR_API_KEY", "Content-Type": "application/json"},
  json={"label":"López Pharmacy","externalId":"pharmacy-42","lang":"es","returnOrigin":"https://app.yourcompany.com","expiresIn":3600})
res.raise_for_status()
print(res.json()["url"])  # https://…/c/<token> — hand this to the browser (redirect or popup)

Você gera um link com a marca do seu cliente; ele conecta o número dele sem entrar no seu painel nem ver a nossa marca. Você recebe connection.created com o seu externalId. O detalhe completo (diagrama, snippet de popup e por que nunca em um iframe) está dentro do painel, com os seus dados reais.

A URL (com o token) é devolvida UMA única vez, como uma API key — mande-a e guarde-a.

O token só é consumido quando a conexão é criada, não ao abrir a página. Se o seu cliente fechar a janela ou a internet cair, ele abre de novo o MESMO link e continua — até vencer.

O webhook é a fonte confiável. O postMessage se perde se o seu cliente fechar a janela; o webhook connection.created não. Confirme do lado do servidor com esse evento.

⚠ Nunca dentro de um iframe: o popup do Embedded Signup quebra ali (restrições de popup + bloqueio de cookies de terceiros). Use redirecionamento ou popup de primeiro nível.

Para o popup, returnOrigin é obrigatório: é a origem https exata à qual respondemos (nunca *), validada ao criar o link.

O seu cliente NUNCA vê a nossa marca nem entra no seu painel; você NUNCA entra na conta da Meta dele. Configure a sua marca em Ajustes → Marca.

Estados do link: pending (sem uso), used (já conectou), expired (venceu), revoked (você cancelou). Revogue um pendente com DELETE /v1/onboarding-links/:id.

Seção Links do painel: ali você vê os que mandou (os dois tipos), para qual cliente, quando vencem e o andamento — não aberto / aberto e não concluído / concluído. Um link sem atividade era ambíguo: não havia como distinguir "não viu" de "travou no formulário", que pedem ações diferentes.

🔒 Da abertura registramos só que aconteceu e quando (primeira, última e quantas vezes). Nada do visitante: nem IP, nem navegador, nem referer. GET /v1/onboarding-links devolve engagement, firstOpenedAt, lastOpenedAt e openCount.

Pelo painel você pode copiar de novo a URL de um link que continue vivo (owner/admin, auditado). A de um usado/vencido/revogado não é revelada: não serve para nada.