Skip to main content

Pareamento remoto — link de pareamento hospedado

Para plataformas SaaS que gerenciam números para clientes finais, o Pareamento Remoto permite gerar um link de pareamento em vez de exibir o QR code você mesmo. Seu cliente abre o link, escaneia o QR (ou executa o Meta Embedded Signup), e o número conecta à sua conta.
Modal Pairing Link com o link de conexão hospedado

Uma sessão de pareamento remoto — envie o link hospedado ao seu cliente; quando ele escanear o QR, o webhook number.connected dispara.

POST /v1/numbers/remote-pairing

Mesmo corpo de POST /v1/numbers, mais opções específicas de pareamento:
Resposta 201:
Diferenças em relação a POST /v1/numbers:
  • A instância é criada no estado CLOSE (desconectada) — nenhum QR code é retornado.
  • remotePairingUrl é o link público para o cliente final. O endpoint apenas devolve o link — a entrega ao cliente é sua (sua própria interface, e-mail, chat, onde ele estiver).
  • Quando o cliente final abre a URL e conecta, o webhook number.connected dispara normalmente.

linkToApiKey — aposentado

Aceito, mas não faz nada. Enviá-lo não é erro, e nenhuma chave de API é alterada: linkedApiKeyId na resposta é sempre null. Ele reapontava uma chave com escopo de número para o número recém-pareado, tirando-a silenciosamente do número ao qual estava presa.

warnings no 201

Um pareamento bem-sucedido pode voltar com um array warnings de strings. Ele só aparece quando há algo a relatar. Para provider=PILOT_STATUS, externalRef e redirectUrl são aceitos mas não podem ser honrados: eles viajam na sessão assinada da Meta, e o link de pareamento por QR não carrega sessão nenhuma. Cada um deles é relatado em warnings — e o pareamento continua dando certo. Não é um 400:
Antes eles eram descartados em silêncio. Se você precisa de algum dos dois, use provider=META — é o ramo Meta que os leva adiante na sessão assinada.

Histórico: decida nesta chamada

Quem abre o link conecta na hora, e o provider entrega o histórico do aparelho logo em seguida — uma vez só. Não existe um momento no meio para um PATCH, então esta rota aceita o bloco settings das configurações do número, nos dois providers:
Resposta 201 para META:
  • settings.historyImportEnabled tem padrão true. Omita o bloco para manter o histórico do aparelho; mande false para descartá-lo.
  • numberId é a linha provisória em que a conexão vai cair. Use-o para acompanhar a conexão (GET /v1/numbers/{id}/status) ou para dar PATCH antes de a pessoa terminar.
  • Um bloco inválido responde 400 INVALID_SETTINGS e nada é criado.
  • No PILOT_STATUS, o bloco só é aplicado quando esta chamada cria o número.
Mudou em 2026-09-16. Antes disso, provider=META ignorava settings sem avisar e o 201 não trazia numberId: a única forma de definir o histórico num link Meta era um PATCH na linha provisória, com o id tirado de dentro do token do link. Leia o numberId do corpo — o token é um formato interno e pode mudar.
O corpo também aceita um objeto opcional branding para sobrescrever o logo/cores/título da página de conexão para apenas este link (registrado no token). Ele sobrepõe a marca salva do tenant. Veja Marca da página de conexão.

Números Meta: metaFlow

Para números da API oficial Meta Cloud o link de pareamento pode executar fluxos Meta em vez do pareamento por QR:
  • metaFlow: "embedded" — a página hospedada executa o Meta Embedded Signup. Quem abrir o link autentica no Facebook dentro do diálogo da própria Meta e escolhe ali o business portfolio e o número de telefone.
  • metaFlow: "credentials" — a página hospedada coleta as credenciais de uma WABA existente. Ela também exibe um checkbox rotear webhooks recebidos para a Pilot Status (ligado por padrão) que inscreve automaticamente essa WABA no callback da Pilot, dispensando qualquer configuração manual de webhook — veja entrega de webhook.

Quem autentica decide de quem é a WABA

A página hospedada executa o mesmo componente de Embedded Signup que o painel executa, com o mesmo app e a mesma configuração. Nada no token de pareamento fixa um business portfolio. Ou seja: a WABA nasce no portfólio de quem fizer login no Facebook naquele diálogo — o cliente final, se ele abrir o link, ou você, se um operador do seu time abrir e selecionar o seu próprio portfólio. Se o seu modelo é “a WABA é nossa e nós pagamos a Meta”, faça o seu operador rodar o link; se é “o cliente é dono da WABA dele”, mande o link para o cliente. A chamada de API é idêntica nos dois casos.
O Embedded Signup aqui é configurado com featureType: "whatsapp_business_app_onboarding" — o onboarding de coexistência da Meta. O diálogo exibe o QR que o dono do número escaneia com o app WhatsApp Business no celular dele, então o telefone ainda precisa estar ao alcance naquele momento, seja quem for que estiver conduzindo o diálogo.
O seletor de portfólio não aparece para o perfil do Facebook que é dono do app de desenvolvedor. Isso é regra da Meta, não defeito: um portfólio que possui o app fica de fora do Embedded Signup do próprio app, e a Meta esconde um seletor vazio. Qualquer outro perfil — inclusive o seu, como cliente desta plataforma — vê o seletor. Se estiver testando, use um perfil do Facebook sem papel nenhum no app.

POST /v1/numbers/meta — a alternativa BYO-WABA

Se você já tem metaPhoneId + metaWabaId + um token de system user no servidor, o POST /v1/numbers/meta anexa o número sem link e sem interação do cliente final. Ele é agnóstico quanto a coexistência — anexa um número em coexistência e um número Cloud API puro do mesmo jeito. O que ele não faz é nada do que o Embedded Signup faz por você:
O BYO-WABA não consegue fazer o pareamento de coexistência. Transformar um telefone que está vivo no app WhatsApp Business num número Cloud API acontece dentro do diálogo de Embedded Signup da Meta — não existe API para isso, nem nossa nem da Meta. O POST /v1/numbers/meta é como você registra um número que o pareamento já produziu, não como pareia um.Ele também nunca pede o histórico do aparelho à Meta, então nada é importado para aquele número. Se você precisa do histórico, pareie pelo Embedded Signup.
Os dois provedores emitem duas credenciais diferentes, e elas não expiram no mesmo relógio. Leia o expiresAt do 201 em vez de fixar qualquer um dos dois números no código. Num link META, o número também aparece imediatamente como placeholder cujo GET /v1/numbers/{id}/status responde PENDING e depois EXPIRED — é esse o sinal para mostrar ao seu cliente a mensagem de “o link expirou” e emitir um novo.

A origem da página de conexão (postMessage)

Quando você embute a página de conexão no seu próprio site, ela conversa com a página hospedeira por postMessage (connect:paired, connect:error, connect:expired). Os dois provedores usam o mesmo host — ele vem de uma única configuração da plataforma, não do provedor — e em produção esse host é:
Trave o seu listener nele e recuse toda outra origem.
Melhor do que fixar no código: derive-a do link que a API acabou de devolver — new URL(response.remotePairingUrl).origin. O host é uma configuração de deploy; um receptor preso a um literal quebra em silêncio se ela mudar, enquanto um derivado do remotePairingUrl não tem como divergir.

Erros

Os dois 402 trazem as figuras por trás da recusa — plan, maxNumbers, currentNumberCount, extras, proratedTotal, walletBalance e currency — ao lado de error e code. A recusa do plano Free para um telefone que já existe traz só plan, maxNumbers e currentNumberCount. Ramifique pelo código: os dois compartilham o status, mas não a solução. Veja erros de capacidade.
Corrigido: INSUFFICIENT_FUNDS nesta rota agora responde 402, o mesmo status do POST /v1/numbers. Antes vinha como 500.
Mudou. Um plano com a cota simplesmente cheia também respondia INSUFFICIENT_FUNDS aqui, e o corpo não trazia nada além do código repetido nos dois campos. Agora responde PLAN_NUMBER_LIMIT_REACHED, com as figuras de capacidade junto.

Endpoints públicos por token (sem autenticação)

A página de pareamento usa estes endpoints baseados em token — você também pode chamá-los para construir sua própria UI de conexão: Observações:
  • Plano Free: quando o workspace que emitiu o link está no Free, a cota dele já está cheia e este número nunca conectou, POST /v1/remote-pairing/{token}/connect responde 402 com { "error": "PLAN_NUMBER_LIMIT_REACHED", "code": "PLAN_NUMBER_LIMIT_REACHED", "message": "…", "plan": "FREE", "maxNumbers": 1, "currentNumberCount": 1 }, e nenhum QR code é gerado. O message é uma frase bilíngue, English | Português. Quem está com o link não tem como resolver: o workspace precisa comprar um número extra ou subir de plano. Veja Plano Free.
  • Este é o token PILOT_STATUS (QR): expira após 24 horas ou mediante conexão bem-sucedida (uso único). Um link META de Embedded Signup é uma credencial diferente, com TTL de 30 minutos — veja Validade do link.
  • POST .../connect só funciona quando o estado não é OPEN.
  • O mesmo estado de conexão exibido no painel e nos webhooks (number.created, number.connected) se aplica.