Conexão do WhatsApp
Como o cliente final conecta o número dele pela página do hub, e o recurso de conexão.
O cliente final conecta o número de WhatsApp Business dele ao seu workspace por uma página hospedada pelo hub (Embedded Signup da Meta). Seu app não fala com a Meta: abre uma sessão de conexão, redireciona o cliente para a url devolvida e acompanha o resultado por redirect de volta e por eventos.
Pré-requisito: URLs de retorno
O returnUrl da sessão precisa estar na allowlist do seu app, cadastrada pela equipe Matra no painel. A comparação é por origin + pathname, sem query nem fragmento: o hub normaliza a URL enviada e exige que seja igual a uma entrada. Fora da lista: 400 com code: "RETURN_URL_NOT_ALLOWED".
Abrir uma sessão
POST /v1/workspaces/:workspaceId/connect-sessions
{ "kind": "whatsapp", "returnUrl": "https://app.exemplo.com/settings/whatsapp" }
Corpo estrito (campo extra é rejeitado). Resposta 201:
{
"id": "...",
"url": "https://.../connect#s=cs_...",
"expiresAt": "2026-10-22T15:15:00.000Z"
}
- A
urlé de uso único e vale 15 minutos (expiresAt). Ela carrega um token de acesso: não registre em log nem exiba para quem não é o cliente que vai conectar. - Repetir o
POSTabre uma sessão nova; as anteriores seguem válidas até expirar ou serem usadas.
| Status | code |
Quando |
|---|---|---|
| 400 | RETURN_URL_NOT_ALLOWED |
returnUrl fora da allowlist do app |
| 404 | WORKSPACE_NOT_FOUND |
workspace inexistente ou de outro app |
| 409 | WORKSPACE_DISABLED |
workspace desativado |
| 503 | META_WEBHOOK_NOT_READY |
a Meta ainda não está entregando webhooks ao hub; uma conexão feita agora nasceria sem receber eventos. Tente de novo em instantes; se persistir, avise a equipe Matra |
Retorno do cliente
Ao terminar, cancelar ou falhar, a página redireciona o navegador para o seu returnUrl com parâmetros na query:
| Query | Significado |
|---|---|
status=connected&connectionId=<id> |
Conexão criada. Ainda está pending: a ativação roda em segundo plano |
status=cancelled |
O cliente fechou ou cancelou o fluxo da Meta |
status=error&code=<code> |
Falha (META_EXCHANGE_FAILED, META_GRANT_INVALID, META_ASSET_MISMATCH, META_PHONE_AMBIGUOUS, COEXISTENCE_INCOMPLETE, PHONE_ALREADY_CONNECTED, WABA_ALREADY_CONNECTED, META_UNAVAILABLE…) |
O redirect é só uma dica de interface: o navegador do cliente pode fechar antes dele. A fonte da verdade é o evento connection.created (e GET /v1/connections).
PHONE_ALREADY_CONNECTED e WABA_ALREADY_CONNECTED indicam que o número ou a conta já estão ligados a uma conexão viva em outro workspace; o hub não diz de quem. META_PHONE_AMBIGUOUS sai no Coexistence quando a Meta não informa o número e a conta tem zero ou mais de um número do app WhatsApp Business já migrado; COEXISTENCE_INCOMPLETE, quando o número do app ainda não terminou a migração para a API. Nos dois casos nada é criado e o cliente pode tentar de novo com uma sessão nova.
Sessão de um workspace desativado, ou de um app desativado pela Matra, deixa de valer na hora, mesmo dentro dos 15 minutos: a página mostra que o link não vale mais e nenhuma conexão é criada.
Conexões
| Método e rota | O que faz | Respostas |
|---|---|---|
GET /v1/connections |
Lista (paginada) as conexões do app. Filtros: workspaceId, status |
200 |
GET /v1/connections/:connectionId |
Busca uma conexão | 200, 404 |
DELETE /v1/connections/:connectionId |
Desconecta; repetir é seguro | 200, 404 |
GET /v1/connections/:connectionId/usage |
Uso do nível gratuito de mensagens de serviço no mês (ver Custos) | 200, 404 |
GET /v1/connections/:connectionId/consumption?month=YYYY-MM |
Mensagens entregues no mês por categoria e tipo de cobrança, contadas pelo hub e pela Meta (ver Custos) | 200, 400, 404 |
Resposta de uma conexão ({ "connection": ... } no GET por id e no DELETE; item de data na listagem):
{
"id": "...",
"kind": "whatsapp",
"status": "active",
"errorCode": null,
"workspaceId": "...",
"whatsapp": {
"wabaId": "123456789012345",
"phoneNumberId": "109876543210987",
"displayPhoneNumber": "+55 43 99999-0000",
"verifiedName": "Clínica Exemplo",
"isOnBizApp": true,
"qualityRating": "GREEN"
},
"sync": [
{ "type": "smb_app_state_sync", "status": "accepted", "progress": null },
{ "type": "history", "status": "accepted", "progress": 40 }
],
"tokenExpiresAt": null,
"health": {
"status": "LIMITED",
"issues": [
{
"entityType": "PHONE_NUMBER",
"canSendMessage": "LIMITED",
"errors": [],
"additionalInfo": ["Your display name has not been approved yet. Your message limit will increase after the display name is approved."]
}
],
"checkedAt": "2026-10-23T16:36:19.000Z"
},
"createdAt": "2026-10-22T15:00:00.000Z",
"updatedAt": "2026-10-22T15:00:00.000Z"
}
status:pending(criada, ativação em andamento),active,error(precisa de atenção, vererrorCode) oudisconnected.errorCodesó vem preenchido emerror:offboarded(a Meta retirou o número do parceiro; volta aactivese a Meta reconectar),token_expired(a credencial venceu; o cliente precisa conectar de novo),onboarding_incomplete(a ativação não terminou em 2 h),activation_failed,subscribe_failed:<código>eregister_failed:<código>. Trate valores desconhecidos como erro genérico.whatsapp.isOnBizAppétruequando o número continua no app WhatsApp Business do cliente (Coexistence).syncaparece só em Coexistence: o hub pede à Meta a sincronização do estado do app (smb_app_state_sync) e do histórico de 180 dias (history).status:requested,accepted,completedoufailed.progress(0 a 100, ounullantes do primeiro bloco) é o maior progresso informado pela Meta no histórico; em 100 ohistoryviracompletede sai umconnection.updated. O histórico recusado pelo cliente no app virafailed(comconnection.updated). O conteúdo sincronizado chega pelos eventoswhatsapp.history.receivedewhatsapp.contacts.synced.tokenExpiresAténullquando a credencial não tem prazo.healthé ohealth_statusda Meta para o número, lido na ativação e de hora em hora (nullantes da primeira leitura).statusé ocan_send_messagegeral:AVAILABLE,LIMITED(envia com limite menor) ouBLOCKED(conversa iniciada pela empresa não sai; resposta dentro da janela pode sair).issueslista só as entidades (PHONE_NUMBER,WABA,BUSINESS,APP) que não estãoAVAILABLEou que têm erro de mensagem, comerrors(code,descriptionesolutioncomo a Meta devolve, em inglês) eadditionalInfo; erros da API de chamadas (138xxx) ficam de fora. Exemplos vistos:141010(empresa sem verificação,LIMITED),141006(sem forma de pagamento,BLOCKED) e o nome de exibição em análise (additionalInfo,LIMITED). Quando o estado ou os códigos mudam, saiconnection.updatedcommeta: { "health": "changed" }; a primeira leitura só avisa quando não estáAVAILABLE.- Nenhuma resposta traz token, PIN ou qualquer credencial.
Reconectar. Uma nova sessão para o mesmo workspace e o mesmo número reaproveita a mesma conexão (mesmo id): ela volta a pending e emite connection.updated, não connection.created. Um número que está active, pending ou error em outro workspace não pode ser conectado: o 409 vai para a página de conexão, não para o seu app. O que o seu app vê é o retorno do navegador com ?status=error&code=PHONE_ALREADY_CONNECTED (ou WABA_ALREADY_CONNECTED), e nenhum connection.created é emitido.
Desconectar. DELETE marca disconnected, apaga a credencial no hub e cancela a assinatura de webhooks da conta quando nenhuma outra conexão a usa. O número do cliente não é desregistrado na Meta. Responde 200 com a conexão; chamar de novo devolve a conexão atual sem novo evento. Conexões desconectadas há mais de 30 dias são apagadas do hub.