Inteligencialy

Manual de Integração

API WhatsApp

Como conectar o WhatsApp dos seus clientes, ler conversas e enviar mensagens e templates pela API Central Inteligencialy.

1. Visão geral

A API Central Inteligencialy conecta o WhatsApp dos clientes do seu sistema à plataforma oficial da Meta (WhatsApp Cloud API). Com ela, o seu sistema:

A comunicação é sempre servidor a servidor: o seu backend chama a API. O navegador e o aplicativo do seu cliente nunca falam com ela diretamente, com uma única exceção descrita na seção 5: a tela da Meta devolve o navegador para a API ao final da conexão.

Conceitos

TermoO que é
ProdutoO seu sistema. É identificado pela credencial que a Inteligencialy entrega, e só enxerga os próprios dados.
Cliente (tenant_external_id)A empresa final que usa o seu sistema. Você escolhe o identificador (o id dela no seu banco, por exemplo); a API o guarda como texto e devolve do jeito que recebeu.
Conexão (connection_id)Um número de WhatsApp conectado. É a chave de quase todas as rotas.
Conversa (conversation_id)O histórico entre a conexão e um contato.
Janela de 24 horasPeríodo, aberto por uma mensagem recebida do contato, em que o texto livre é permitido e gratuito.
TemplateMensagem com texto aprovado pela Meta. É a única forma de iniciar conversa ou falar fora da janela, e é cobrada pela Meta.

Endereço e formato

2. Antes de começar

Combine com a Inteligencialy:

  1. Credencial de acesso. Um token no formato intely_live_…, entregue por canal seguro.
  2. URL de retorno. Um endereço https:// do seu sistema para onde o navegador do cliente volta depois de conectar o WhatsApp (seção 5, etapa 3). Ele é cadastrado do nosso lado; a API nunca aceita esse endereço vindo da requisição.

Cada cliente que for conectar o WhatsApp precisa de:

3. Autenticação

Envie a credencial no cabeçalho Authorization de todas as chamadas:

curl https://api.inteligencialy.com/integrations/whatsapp/connections \
  -H "Authorization: Bearer intely_live_SEU_TOKEN" \
  -H "Accept: application/json"

A credencial dá acesso a todas as conexões e conversas do seu produto. Guarde-a em variável de ambiente ou cofre de segredos do servidor. Nunca a coloque no front-end, no aplicativo, no repositório de código nem em logs.

Credencial ausente, inválida, revogada ou vencida responde 401 invalid_integration_credentials.

Não envie cabeçalhos que pareçam escolher cliente, conexão ou credencial por fora do Bearer (por exemplo X-Tenant-Id, X-Connection-Id, X-Waba-Id, X-Product-Key, X-Api-Key, Api-Key, Token): a API recusa a chamada com 422 invalid_payload. Quem você é sai só da credencial, e o recurso, só da URL.

Trocar a credencial

Para trocar sem interromper a integração: peça uma credencial nova, publique-a no seu servidor, confirme que as chamadas funcionam e só então avise a Inteligencialy para revogar a antiga. As duas funcionam ao mesmo tempo durante a troca.

4. Convenções da API

Formato de erro

{
  "error": "codigo_estavel",
  "message": "Mensagem legível para pessoas.",
  "request_id": "identificador-da-requisicao"
}

Erros gerais. Os erros próprios de cada rota estão na seção dela.

HTTPerrorSignificado
401invalid_integration_credentialsCredencial ausente, inválida, revogada ou vencida. Qualquer rota.
404not_foundRota inexistente ou identificador mal formado na URL. Qualquer rota.
404invalid_onboarding_state, invalid_whatsapp_connection, invalid_whatsapp_conversationRecurso não encontrado ou pertencente a outro produto. Os dois casos são indistinguíveis de propósito.
405method_not_allowedMétodo HTTP errado para a rota (por exemplo, GET numa rota POST). Qualquer rota.
422invalid_payloadCorpo ausente, com JSON inválido, campo com formato errado, campo não reconhecido, corpo grande demais ou parâmetro de consulta numa rota que não aceita nenhum.
422invalid_parametersParâmetro de consulta (query string) inválido ou não reconhecido nas listagens paginadas (conexões, contatos, conversas, mensagens e fila de saída).
429too_many_requestsLimite de chamadas atingido. Veja "Limite de chamadas" abaixo.
500internal_errorFalha inesperada do nosso lado. Informe o request_id.
503saas_authentication_unavailableA verificação da credencial está indisponível do nosso lado. Qualquer rota. Informe o request_id.
(outros)http_errorSituação HTTP genérica fora das listadas, como manutenção programada. Informe o request_id.

As etapas START e desconectar um número conferem o formato do corpo antes de validar os campos. Nelas também podem vir 400 invalid_json (corpo vazio ou que não é JSON), 413 payload_too_large, 415 unsupported_media_type (falta Content-Type: application/json) e 503 service_unavailable (configuração indisponível do nosso lado). Nas demais rotas esses casos respondem 422 invalid_payload. Os códigos 400 bad_request e 422 validation_error existem no tratamento genérico de erros, mas nenhuma rota de integração os devolve hoje; se aparecerem, trate como invalid_payload.

Campos não reconhecidos são recusados

Cada rota aceita exatamente os campos documentados. Um campo a mais responde 422: isso evita que um erro de digitação passe despercebido.

Chamadas sem dados enviam {}

Várias etapas são POST sem dados (exchange, connection, subscribe, entre outras). Envie sempre o corpo {}. Um POST sem corpo pode ser barrado pela proteção do servidor antes de chegar à API, com uma resposta 406 em HTML, sem error nem request_id.

Limite de chamadas

Os limites são por minuto. Alguns são compartilhados entre rotas:

RotasLimite
Todas as leituras: conexões, contatos, conversas, mensagens, fila de saída e lista de templates120 por minuto por credencial, somadas, e 180 por minuto por endereço IP (este também soma os dois envios abaixo)
Enviar texto60 por minuto por credencial
Enviar template20 por minuto por credencial
Criar, corrigir, sincronizar e provisionar templates, somadas10 por minuto por IP
Etapas da conexão (exchange, confirm-assets, connection, subscribe)30 por minuto por IP, cada uma; o GET …/assets, também 10 por minuto por credencial
Retomar a sincronização da Coexistência10 por minuto por IP
Desconectar um número10 por minuto por IP antes da autenticação e 5 por minuto por credencial autenticada

Em 429 too_many_requests por limite de chamadas, aguarde e repita somente aquela chamada, sem laço apertado. Nunca reinicie um fluxo inteiro por causa de um 429. Três 429 não se resolvem em segundos e têm tratamento próprio: template_daily_cap_reached e template_edit_too_soon (seção 9), e o too_many_requests da retomada da Coexistência, que vem do limite da própria Meta.

Idempotência

Os envios de mensagem exigem um idempotency_key criado por você: de 8 a 128 caracteres, começando por letra ou número, com letras, números, ., _, : e -. Use uma chave por mensagem lógica (por exemplo, lembrete-agendamento-8841).

A chave do texto vale dentro da conversa; a do template vale dentro da conexão. Como agir em cada resposta:

Paginação das listagens

ParâmetroUso
limitItens por página. Padrão 25, máximo 100.
cursorValor de next_cursor da página anterior. Use exatamente como recebeu.
orderdesc (padrão) ou asc. Em conexões, conversas, mensagens e fila de saída, desc traz os mais recentes primeiro; em contatos, a ordem é pelo nome, e desc vai de Z para A (use order=asc para ordem alfabética).
from, toPeríodo (conversas, mensagens e fila de saída). Formato AAAA-MM-DDThh:mm:ss, com fração de até 3 casas opcional e fuso Z ou ±hh:mm (sem fuso, vale UTC). Data sem hora é recusada. Com os dois, from não pode ser depois de to, e o intervalo é de no máximo 366 dias.

Valem para conexões, contatos, conversas, mensagens e fila de saída. Em conexões e contatos, from e to são aceitos mas não filtram nada. Qualquer outro parâmetro responde 422 invalid_parameters. A lista de templates não é paginada.

Ao repassar um horário com fuso positivo na URL, codifique o + como %2B; sem isso ele chega como espaço e a consulta é recusada. Usar Z (UTC) evita o problema.

As listagens respondem neste formato; next_cursor é null na última página:

{
  "data": [ ... ],
  "next_cursor": "MjAyNi0wOS0yMyAxODowMjo0NC4wMDB8NDIx",
  "request_id": "..."
}

Trate o cursor como opaco: não monte nem altere.

5. Conectar o WhatsApp de um cliente

A conexão usa o Cadastro Incorporado (Embedded Signup) da Meta: o seu cliente clica em "Conectar WhatsApp" no seu sistema, faz o processo na tela oficial da Meta e volta para o seu sistema já conectado. Todo o trabalho com a Meta fica com a API; o seu sistema faz um redirecionamento e uma sequência de chamadas.

  1. START: o seu backend pede o início da conexão e recebe a URL da tela da Meta.
  2. Tela da Meta: o navegador do cliente abre essa URL e ele conclui o processo.
  3. Retorno: a Meta devolve o navegador para a API, que o redireciona para a sua URL de retorno.
  4. EXCHANGE: o seu backend confirma a autorização, imediatamente.
  5. CONFIRM ASSETS: confirma qual conta e número do WhatsApp serão usados; se houver mais de uma opção, liste os ativos e peça a escolha.
  6. CONNECTION: cria a conexão e devolve o connection_id.
  7. SUBSCRIBE: ativa o recebimento de mensagens. Só aqui a conexão está pronta.

Etapa 1: START

POST/integrations/whatsapp/onboarding/start
{
  "tenant_external_id": "cliente-4821",
  "onboarding_mode": "business_app"
}

Resposta 201 (trecho):

{
  "status": "pending",
  "onboarding_id": "3f1c2a9e-5b7d-4c1e-9a2f-6d8e0b4c7a11",
  "state": "...",
  "expires_at": "2026-09-23T15:30:00+00:00",
  "meta": {
    "onboarding_mode": "business_app",
    "feature_type": "whatsapp_business_app_onboarding",
    "signup_dialog_url": "https://www.facebook.com/v26.0/dialog/oauth?...",
    ...
  },
  "request_id": "..."
}

O bloco meta traz também identificadores técnicos da configuração da Meta; você não precisa deles. Guarde o onboarding_id associado ao cliente. A sessão vale até expires_at (60 minutos).

HTTPerrorSignificado
409product_return_url_missingO seu produto está sem URL de retorno cadastrada, ou desativado. Fale com a Inteligencialy.
422invalid_payloadCampo ausente, com formato errado ou não reconhecido, ou parâmetro na URL.
403invalid_saas_contextO cadastro do seu produto do nosso lado está inconsistente. Fale com a Inteligencialy.
503saas_authentication_unavailable, meta_configuration_unavailable, coexistence_unavailable, encryption_unavailableConexão indisponível do nosso lado. Fale com a Inteligencialy.

Veja também os erros de formato do corpo exclusivos desta etapa, na seção 4.

Se meta.signup_dialog_url vier null, a conexão não está disponível do nosso lado: não monte a URL por conta própria, fale com a Inteligencialy.

standard ou business_app?

  • business_app (Coexistência): o número continua funcionando no aplicativo WhatsApp Business do celular do cliente, e também na API. Na tela da Meta, o cliente precisa escolher "Conectar um app do WhatsApp Business". A Meta desativa no número as mensagens temporárias, a visualização única, a localização em tempo real e as listas de transmissão, e mensagens de grupo não são sincronizadas.
  • standard: o número passa a funcionar só pela API e deixa de funcionar no aplicativo do celular.

Explique isso ao cliente antes de ele clicar. A escolha errada acontece dentro da tela da Meta, antes de a API poder impedir.

Etapa 2: abrir a tela da Meta

Leve o navegador do cliente para meta.signup_dialog_url, na janela inteira (redirecionamento, não popup nem iframe). Use a URL exatamente como recebeu: ela já contém tudo o que a Meta precisa e o vínculo seguro com a sessão. Não monte a URL por conta própria.

Em Coexistência, só abra a URL depois de conferir que meta.onboarding_mode é business_app e meta.feature_type é whatsapp_business_app_onboarding. Se não for, não abra e trate como erro.

Etapa 3: retorno para o seu sistema

Ao final, a Meta devolve o navegador para a API, que redireciona para a sua URL de retorno com:

https://seu-sistema.com/whatsapp/retorno?onboarding_id=3f1c2a9e-...&status=completed
statusO que fazer
completedSiga para a etapa 4 em até 30 segundos, com esse onboarding_id. O ideal é chamar o EXCHANGE no mesmo processamento que recebe o navegador de volta.
cancelledO cliente desistiu (vem sem onboarding_id). Não chame as próximas etapas; ofereça tentar de novo com um novo START.
failedO retorno não pôde ser concluído. Vem sem onboarding_id (use o que você guardou no START) e com error, um dos códigos abaixo. Não chame as próximas etapas.
https://seu-sistema.com/whatsapp/retorno?status=failed&error=onboarding_expired
errorO que fazer
onboarding_expiredA sessão venceu: passaram os 60 minutos sem o cliente concluir na Meta, ou o retorno foi repetido depois da janela de 30 segundos sem EXCHANGE. Ofereça recomeçar do START.
onboarding_consumedEsta sessão já seguiu para as próximas etapas (por exemplo, o cliente recarregou a página de retorno). Não recomece: continue o fluxo pelo onboarding_id que você guardou.
onboarding_result_conflict, invalid_onboarding_transitionA sessão recebeu um retorno diferente do primeiro ou está num estado que não aceita retorno. Ofereça recomeçar do START.
invalid_payloadA Meta devolveu o navegador sem a autorização. Ofereça recomeçar do START.
encryption_unavailable, meta_configuration_unavailableFalha do nosso lado. Fale com a Inteligencialy e ofereça recomeçar do START depois.

O retorno nunca traz códigos nem mensagens da Meta: a API já os recebeu e guardou. Se a API não conseguir identificar a sessão (um endereço de retorno adulterado, por exemplo), ela não sabe para onde devolver o navegador e mostra o erro em JSON na própria página.

Peça ao cliente para clicar em Concluir na tela da Meta assim que ela mostrar que a conta foi conectada. A autorização da Meta vale pouco tempo: uma tela deixada aberta por muitos minutos faz a etapa 4 falhar, e aí é preciso recomeçar do START.

Etapa 4: EXCHANGE

POST/integrations/whatsapp/onboarding/{onboarding_id}/exchange

Corpo {}. Resposta 200: {"status": "token_exchanged", "onboarding_id": "...", "idempotent": false, "request_id": "..."}. Chamar de novo depois de um sucesso devolve o mesmo resultado com "idempotent": true.

token_exchanged não conclui a conexão. Ele só confirma que a autorização da Meta foi trocada com sucesso. Não mostre sucesso nem erro final nessa resposta: siga imediatamente para o CONFIRM ASSETS com o mesmo onboarding_id. Mesmo quando a resposta for idempotente (por exemplo, depois de recarregar a sua página), retome as etapas que ainda faltam, sem reiniciar o cadastro na Meta.

Chame em até 30 segundos depois que o navegador voltar, e nunca com repetição automática. Se esta chamada falhar ou der tempo esgotado, não tente de novo em laço: a autorização da Meta pode já ter sido consumida. Mostre o erro e ofereça recomeçar do START.

HTTPerrorO que fazer
404invalid_onboarding_stateSessão inexistente. Recomece do START.
409onboarding_not_readyA sessão ainda não teve retorno concluído da Meta (inclusive quando o cliente nunca voltou da tela da Meta). Não insista: se o retorno não veio com completed, recomece do START.
410onboarding_expiredPassaram os 30 segundos depois do retorno. Recomece do START.
502token_exchange_failedA Meta recusou a autorização. Recomece do START.
503token_exchange_temporarily_unavailableFalha temporária ou resultado incerto. No máximo uma nova tentativa manual; se repetir, recomece do START.
503encryption_unavailableFalha do nosso lado. Fale com a Inteligencialy.

Etapa 5: CONFIRM ASSETS

POST/integrations/whatsapp/onboarding/{onboarding_id}/confirm-assets

Confirma a conta do WhatsApp (WABA) e o número que o cliente autorizou. Na maioria dos casos há exatamente um de cada: envie {} e a API resolve sozinha.

Chame primeiro o CONFIRM ASSETS com {}. Só se ele responder 409 assets_ambiguous, consulte GET …/assets, mostre as opções ao cliente e repita o confirm com a escolha. Não encerre o onboarding em token_exchanged nem em assets_ambiguous.

Resposta 200:

{
  "status": "assets_confirmed",
  "onboarding_id": "...",
  "waba_id": "912991988105750",
  "phone_number_id": "1073521112511032",
  "idempotent": false,
  "request_id": "..."
}

Depois de um confirm bem-sucedido, a escolha não muda mais nesta sessão: um novo confirm devolve os mesmos ativos com "idempotent": true, mesmo que traga outra seleção. Para trocar de número, recomece do START.

Se a resposta for 409 assets_ambiguous, o cliente autorizou mais de uma conta ou número. Liste as opções e confirme a escolhida:

GET/integrations/whatsapp/onboarding/{onboarding_id}/assets

Resposta 200 (trecho; os itens trazem também dados informativos da conta na Meta):

{
  "status": "assets_available",
  "selection_required": true,
  "wabas": [
    {
      "waba_id": "912991988105750",
      "name": "Barbearia Centro",
      "on_behalf_of_business": { "id": "...", "name": "Barbearia Centro Ltda" },
      "phone_numbers": [
        {
          "phone_number_id": "1073521112511032",
          "display_phone_number": "+55 47 8423-3257",
          "verified_name": "Barbearia Centro",
          "is_on_business_app": true
        }
      ]
    }
  ]
}

Esta listagem consulta a Meta e pode levar alguns segundos. Erros: 409 onboarding_not_ready (EXCHANGE ainda não feito), 422 assets_not_found, 422 meta_token_invalid, 502 asset_confirmation_failed, 503 asset_confirmation_temporarily_unavailable (inclusive quando a autorização traz contas demais para listar no tempo disponível) e 503 encryption_unavailable, com o mesmo significado da tabela abaixo.

Depois, repita o confirm com a escolha (os identificadores como texto, entre aspas):

{ "waba_id": "912991988105750", "phone_number_id": "1073521112511032" }

Mostre as opções ao cliente pelo nome da conta e do número, como apareceram na tela da Meta. Se os nomes vierem vazios e houver mais de uma opção, peça a decisão a uma pessoa em vez de escolher sozinho. A escolha é conferida pela API: não é possível confirmar um número que o cliente não autorizou.

Nomes e is_on_business_app são informativos e podem vir null: quando a consulta à Meta falha ou demora e, no caso de is_on_business_app, a partir do 51º número de uma mesma listagem. Em Coexistência, o confirm lê o sinal de novo para o número escolhido.

HTTPerrorO que fazer
409onboarding_not_readyO EXCHANGE ainda não foi feito para esta sessão.
409assets_ambiguousListe com GET …/assets e confirme com a escolha.
409asset_mismatchA escolha não está entre os autorizados. Corrija.
409coexistence_not_confirmedEm Coexistência, o cliente escolheu a opção padrão na Meta e o número saiu do aplicativo do celular. Avise o cliente com destaque. É definitivo para esta sessão.
422assets_not_foundA autorização não trouxe conta do WhatsApp utilizável. Confira com o cliente o que ele autorizou; se não resolver, recomece do START.
422meta_token_invalidA autorização da Meta não vale mais. É definitivo para esta sessão: recomece do START.
502asset_confirmation_failedA Meta recusou a confirmação. É definitivo para esta sessão: recomece do START.
503asset_confirmation_temporarily_unavailableFalha temporária. Uma nova tentativa manual.
503encryption_unavailableFalha do nosso lado. Fale com a Inteligencialy.

Etapa 6: CONNECTION

POST/integrations/whatsapp/onboarding/{onboarding_id}/connection

Corpo {}. Resposta 201 (ou 200 com "idempotent": true se já existia):

{
  "status": "connection_materialized",
  "connection_id": "7b2e4d10-9c3a-4f5e-8b1d-2a6c9e0f3b44",
  "onboarding_id": "...",
  "waba_id": "912991988105750",
  "phone_number_id": "1073521112511032",
  "idempotent": false,
  "request_id": "..."
}

Guarde o connection_id associado ao cliente. Ele identifica o número em todas as rotas seguintes.

HTTPerrorSignificado
409onboarding_not_readyO CONFIRM ASSETS ainda não foi concluído para esta sessão.
409connection_owner_conflictO número possui uma conexão ainda ativa, pendente ou ambígua em outra empresa ou Produto.
409connection_asset_conflictO número já está ligado a outra conta do WhatsApp.
503onboarding_storage_unavailable, connection_storage_unavailableFalha temporária do nosso lado. Repita esta etapa.

Uma conexão que já esteja disconnected é apenas histórico: depois de a desconexão ser confirmada, o mesmo número pode concluir um novo onboarding, inclusive para outro Produto ou tenant. A nova tentativa recebe outro connection_id; nunca reutilize o identificador histórico. Não há compartilhamento simultâneo: enquanto existir qualquer conexão que não seja disconnected, o conflito 409 continua sendo a resposta correta.

Etapa 7: SUBSCRIBE

POST/integrations/whatsapp/connections/{connection_id}/subscribe

Corpo {}. Resposta 200:

{
  "status": "active",
  "connection_id": "7b2e4d10-...",
  "idempotent": false,
  "coexistence_sync": "completed",
  "templates": {
    "status": "completed",
    "created": 5,
    "skipped": 0,
    "failed": 0,
    "deferred": 0,
    "pending_approval": ["confirmacao_agendamento"]
  },
  "request_id": "..."
}

Só mostre o WhatsApp como conectado quando vier "status": "active". Nesta etapa a API também:

coexistence_syncO que fazer
completedNada. Pedido aceito; os dados chegam depois.
not_applicableNada. A conexão não é de Coexistência.
failedChame a retomada (coexistence-sync, abaixo) dentro de 24 horas.
ambiguousA Meta pode ter aceitado. Não chame a retomada: um pedido repetido faz a Meta reenviar todo o histórico. Avise a Inteligencialy com o request_id.
templates.statusO que fazer
completedNada. Os de pending_approval aguardam a Meta.
partialParte falhou ou ficou para depois (deferred, quando o tempo da chamada acabou). Chame templates/provision (seção 9).
failedNada foi criado. Chame templates/provision.
skippedO seu produto não tem templates cadastrados. Nada a fazer.

Falha nessas duas tarefas não impede a ativação: a conexão fica ativa mesmo assim. Se o próprio subscribe falhar com 502 whatsapp_subscription_failed, 503 whatsapp_subscription_temporarily_unavailable ou 503 whatsapp_subscription_in_progress (quando já há um em andamento), repita apenas o subscribe, manualmente. Nunca volte ao EXCHANGE por causa disso. Já 409 whatsapp_connection_not_ready significa que a conexão não pode ser ativada com a autorização desta sessão (por exemplo, ela venceu): repetir não resolve; recomece do START.

Retomar a sincronização da Coexistência

POST/integrations/whatsapp/connections/{connection_id}/coexistence-sync

Corpo {}. Só é necessário quando o subscribe trouxe coexistence_sync igual a failed. A Meta aceita esse pedido apenas nas 24 horas seguintes à conexão. Contatos e histórico são pedidos separadamente; o que a Meta já aceitou numa tentativa anterior não é pedido de novo.

Resposta 200:

{
  "status": "completed",
  "connection_id": "7b2e4d10-...",
  "history_request_id": "...",
  "contacts_request_id": "...",
  "idempotent": false,
  "request_id": "..."
}
RespostaSignificado
200, "status": "completed"Pedido aceito pela Meta. Os dados chegam depois, aos poucos. Se já estava concluído, vem com "idempotent": true e a Meta não é chamada de novo.
200, "status": "not_applicable"Conexão sem Coexistência: não há o que sincronizar.
503 coexistence_sync_ambiguousA Meta pode ter aceitado, nesta ou numa tentativa anterior. Não repita: a API não chama a Meta de novo nesse caso, e a retomada fica com a Inteligencialy. Avise com o request_id.
502 coexistence_sync_failedRecusado ou falhou. Pode tentar de novo.
503 coexistence_sync_in_progressJá há uma sincronização em andamento. Aguarde cerca de 2 minutos.
503 coexistence_sync_temporarily_unavailableFalha temporária. Pode tentar de novo.
429 too_many_requestsLimite da Meta atingido. Tente de novo mais tarde, não em segundos.
409 whatsapp_connection_not_readyA conexão ainda não está ativa. Faça o subscribe antes.
409 meta_token_invalidA sessão que deu origem à conexão não tem autorização da Meta utilizável. Só uma nova conexão resolve. Atenção: aqui o status é 409, diferente do 422 com o mesmo código no CONFIRM ASSETS.
503 encryption_unavailableFalha do nosso lado. Fale com a Inteligencialy.

6. Conexões e contatos

Listar conexões

GET/integrations/whatsapp/connections
{
  "data": [
    {
      "connection_id": "7b2e4d10-...",
      "tenant_external_id": "cliente-4821",
      "waba_id": "912991988105750",
      "phone_number_id": "1073521112511032",
      "status": "active",
      "created_at": "2026-09-23T14:31:02+00:00",
      "last_activity_at": "2026-09-23T18:02:44+00:00"
    }
  ],
  "next_cursor": null,
  "request_id": "..."
}

Quando o cliente desconecta o número pelo celular, a Meta nem sempre avisa. Uma conexão que recebia mensagens e ficou em silêncio por muito tempo pode ter sido desconectada. Use last_activity_at para sinalizar isso ao cliente, sem tratar como certeza.

Desconectar um número

POST/integrations/whatsapp/connections/{connection_id}/disconnect

Use somente pelo backend do seu produto, depois de o usuário confirmar a ação. O corpo identifica a empresa que está autorizada no seu sistema:

{
  "tenant_external_id": "cliente-4821"
}

A credencial Bearer determina o produto; a API compara o tenant_external_id exatamente com a conexão. Não aceite esse valor do navegador e não envie product_key, número, WABA ou token Meta. Uma conexão de outra empresa ou produto responde como inexistente.

Resposta 200:

{
  "status": "disconnected",
  "connection_id": "7b2e4d10-...",
  "idempotent": false,
  "request_id": "..."
}

A API remove a assinatura de webhooks do App na WABA e, somente depois de a Meta confirmar, apaga os contatos, conversas, mensagens, fila de saída e eventos locais daquele número. A linha técnica da conexão e o histórico de operações permanecem. Desvincular a Plataforma de Negócios no aplicativo WhatsApp Business continua sendo tarefa do dono do número. Em Coexistência, status=disconnected significa que o vínculo foi encerrado na Central; não é confirmação de que a Plataforma de Negócios foi desvinculada no celular. O dono deve conferir no WhatsApp Business em Configurações → Conta → Plataforma de Negócios → Desconectar conta.

RespostaSignificado
200, "status": "disconnected"Desconexão confirmada e dados locais removidos. Com "idempotent": true, a Meta não foi chamada de novo; a API apenas concluiu uma limpeza local pendente.
404 whatsapp_connection_not_foundA conexão não existe para o produto e a empresa informados. Não use a resposta para tentar outros IDs.
409 whatsapp_connection_waba_sharedOutra conexão ainda não desconectada usa a mesma WABA. A API bloqueou a operação para não afetar outro número.
409 whatsapp_disconnect_unavailableNão há autorização Meta utilizável para remover a assinatura. Não há limpeza local.
502 whatsapp_disconnect_failedA Meta recusou a remoção. A conexão e os dados continuam preservados; corrija a causa antes de tentar novamente.
503 whatsapp_disconnect_ambiguousA Meta pode ter aplicado a remoção. Não repita: a API bloqueia nova chamada e preserva os dados. Avise a Inteligencialy com o request_id.
503 whatsapp_disconnect_temporarily_unavailableFalha temporária local. Se o número já estiver disconnected, repetir pode somente terminar a limpeza; nos demais casos, aguarde orientação.
400 invalid_json, 413 payload_too_large, 401 invalid_integration_credentials, 415 unsupported_media_type, 422 invalid_payload, 429 too_many_requestsCredencial, JSON, contrato ou limite inválido. Envie JSON com somente tenant_external_id, respeite o limite e nunca mova a credencial para o navegador.

Contatos (Coexistência)

GET/integrations/whatsapp/connections/{connection_id}/contacts

A agenda do celular do cliente, sincronizada na conexão. Cada item traz contact_id, wa_id (número com DDI, só dígitos), full_name e first_name (podem vir null) e synced_at (horário informado pela Meta para aquele dado do contato; pode vir null). Ordena pelo nome, e o padrão desc vai de Z para A: para ordem alfabética, envie order=asc. Pagina com limit e cursor.

Para mostrar nomes na sua lista de conversas sem copiar a agenda para o seu banco, filtre pelos números que estão na tela:

GET /integrations/whatsapp/connections/{connection_id}/contacts?wa_id=5511988887777,5519998406637

7. Conversas e mensagens recebidas

Listar conversas

GET/integrations/whatsapp/connections/{connection_id}/conversations
{
  "data": [
    {
      "conversation_id": "c4a1...",
      "contact_wa_id": "5511988887777",
      "last_direction": "inbound",
      "last_message_at": "2026-09-23T18:02:44+00:00",
      "message_count": 12,
      "service_window_open": true,
      "service_window_expires_at": "2026-09-24T18:02:44+00:00"
    }
  ],
  "next_cursor": null,
  "request_id": "..."
}

service_window_open diz se é possível responder com texto livre agora. service_window_expires_at é o fim da janela aberta pela última mensagem recebida: continua preenchido com uma data passada depois que a janela fecha, e vem null se o contato nunca escreveu. Decida pelo service_window_open.

last_message_at é o horário da última mensagem da conversa informado pela Meta (quando o contato enviou), não o horário em que ela chegou à API. Mudança de status de entrega não altera a conversa.

Mensagens de uma conversa

GET/integrations/whatsapp/connections/{connection_id}/conversations/{conversation_id}/messages
{
  "data": [
    {
      "message_id": "9d0e...",
      "meta_message_id": "wamid.HBgN...",
      "direction": "inbound",
      "type": "text",
      "text": "Oi, queria remarcar meu horário",
      "media_reference": null,
      "delivery_status": null,
      "event_timestamp": "2026-09-23T18:02:44+00:00"
    }
  ],
  "next_cursor": null,
  "request_id": "..."
}

Como saber que chegou mensagem nova

A API envia um webhook ao backend do Produto logo depois de registrar a mensagem recebida. O endereço e o segredo são configurados uma vez pela Inteligencialy para cada Produto; não há polling, nem token Bearer no navegador. O webhook só cobre mensagens inbound de conexões ativas. Status de entrega, ecos, histórico, contatos e templates não geram este aviso.

O destino recebe POST com JSON:

{
  "event_id": "d269cfad-7cf5-42c8-b0bf-1d6abcc7fc6e",
  "event_type": "whatsapp.message.received",
  "occurred_at": "2026-10-08T15:30:00.000Z",
  "product_key": "HORALI",
  "tenant_external_id": "empresa-123",
  "connection_id": "...",
  "conversation_id": "...",
  "message": {
    "message_id": "wamid.HBgN...",
    "direction": "inbound",
    "type": "text",
    "text": "Olá, quero agendar",
    "media_reference": null,
    "event_timestamp": "2026-10-08T15:30:00.000Z"
  }
}

8. Enviar mensagem de texto

POST/integrations/whatsapp/connections/{connection_id}/conversations/{conversation_id}/messages
{
  "idempotency_key": "resposta-8841-1",
  "text": "Claro! Temos horário amanhã às 15h. Confirma?",
  "on_closed_window": "queue"
}
CampoRegra
idempotency_keyObrigatório. Veja a seção 4.
textObrigatório. Até 4.096 bytes.
on_closed_windowreject (padrão) ou queue. O que fazer se a janela de 24 horas estiver fechada.
expires_atOpcional, para queue: até quando a mensagem pode esperar na fila, no formato de data da seção 4 (ex.: 2026-10-08T12:00:00Z). Precisa estar no futuro, no máximo 7 dias à frente; senão, 422 invalid_payload. Padrão: 48 horas.

Texto livre só pode ser enviado com a janela de 24 horas aberta, ou seja, até 24 horas depois da última mensagem recebida do contato. Com a janela fechada:

A chave de idempotência vale dentro desta conversa: repeti-la aqui devolve o resultado original; usar a mesma chave em outra conversa cria uma intenção independente.

RespostaSignificado
201Enviada. Traz outbound_id e meta_message_id.
202Na fila ("status": "queued"), aguardando a janela reabrir.
200"idempotent": true: essa chave já tinha sido usada; nada foi reenviado.
409 service_window_closedJanela fechada com reject.
409 outbound_queue_fullA fila dessa conversa atingiu o limite de 50 mensagens. Mensagens que já passaram do expires_at não contam.
409 outbound_intent_not_reusableA chave pertence a um envio já encerrado. Use outra chave.
409 outbound_intent_in_progressA mensagem dessa chave está sendo enviada agora por outra chamada. Repita com a mesma chave em alguns segundos; não use chave nova.
409 whatsapp_connection_not_readyA conexão não está ativa ou não pode enviar. Com queue e a janela fechada, a mensagem entra na fila mesmo assim e esta checagem acontece na hora de enviar.
422 message_text_too_longTexto acima de 4.096 bytes. Corpo muito maior que isso é recusado antes, como 422 invalid_payload.
502 whatsapp_message_failedA Meta recusou o envio.
503 whatsapp_message_temporarily_unavailableResultado incerto: a mensagem pode ter saído. Não repita com a mesma chave (responderia 409 outbound_intent_not_reusable); confira na conversa e, se não saiu, envie com chave nova.

Acompanhar a fila de saída

GET/integrations/whatsapp/connections/{connection_id}/outbound-queue

Lista os envios de texto da conexão, os que passaram pela fila e os enviados direto com a janela aberta, ordenados pelo momento do pedido. Aceita o filtro status (queued, sending, sent, expired ou failed), além da paginação e do período (que filtra pelo momento do pedido). Cada item traz outbound_id, status, meta_message_id, attempts, last_error, expires_at, sent_at e created_at; para ligar um item à conversa, guarde o outbound_id devolvido no envio.

9. Templates

Templates são mensagens com texto aprovado pela Meta. São a única forma de iniciar uma conversa ou de falar com um contato fora da janela de 24 horas. A Meta cobra os envios na conta do seu cliente, conforme a tabela de preços dela, que depende da categoria e de a janela estar aberta.

Normalmente você não precisa criar templates. Os templates que o seu produto usa são cadastrados pela Inteligencialy e criados automaticamente na conta de cada cliente na etapa SUBSCRIBE. Depois de criados, passam pela análise da Meta, que costuma levar de minutos a algumas horas. Até a aprovação, o envio responde 409 whatsapp_template_not_approved.

Listar templates da conexão

GET/integrations/whatsapp/connections/{connection_id}/templates

Resposta {"data": [...], "request_id": "..."}, sem paginação e com no máximo 100 itens. Cada item traz name, language, status (APPROVED, PENDING, REJECTED, PAUSED, DISABLED, ou ABSENT para o que a última sincronização não encontrou mais na Meta), approved_category, requested_category, variables_count e last_synced_at. A rota não aceita parâmetros na URL.

Enviar template

POST/integrations/whatsapp/connections/{connection_id}/template-messages
{
  "idempotency_key": "lembrete-agendamento-8841",
  "to": "5511988887777",
  "template": "lembrete_agendamento_vespera",
  "language": "pt_BR",
  "variables": ["Ana", "Corte de cabelo", "14:30"],
  "accept_category": "UTILITY"
}

Resposta 201 (ou 200 com "idempotent": true para uma chave já enviada):

{
  "status": "sent",
  "send_id": "...",
  "meta_message_id": "wamid.HBgN...",
  "template": "lembrete_agendamento_vespera",
  "language": "pt_BR",
  "category": "UTILITY",
  "idempotent": false,
  "request_id": "..."
}

A mensagem aparece na conversa daquele número, criada se ainda não existir. Se o contato responder, a janela de 24 horas abre e o restante da conversa pode seguir em texto livre, sem custo de template.

HTTPerrorSignificado
404whatsapp_template_not_foundNão existe template com esse nome e idioma nessa conexão.
409whatsapp_template_not_approvedAinda em análise, reprovado ou pausado pela Meta.
409template_category_requires_confirmationAprovado como MARKETING: só envia com accept_category.
409template_category_changedA categoria aprovada é diferente da enviada em accept_category ou, sem esse campo, diferente da pedida na criação.
409whatsapp_payment_method_requiredA conta do cliente na Meta não tem forma de pagamento. Avise o cliente para cadastrá-la; depois, repita com a mesma chave. A nova tentativa usa tudo que vier nela; o histórico registra template, idioma, categoria e destinatário, mas não armazena os valores das variáveis.
409template_send_not_reusableA chave pertence a um envio que falhou. Use outra chave.
409template_send_in_progressO envio dessa chave está em andamento em outra chamada. Repita com a mesma chave em alguns segundos; não use chave nova, ou o template seria cobrado duas vezes.
422template_variables_mismatchQuantidade de variáveis diferente da do template.
429template_daily_cap_reachedTeto de 200 templates por conexão numa janela móvel de 24 horas. Não se resolve em segundos: reagende. Contam os envios feitos, os em andamento e os de resultado incerto, porque podem ter sido cobrados. Não contam as recusas da Meta (502, whatsapp_payment_method_required), a Meta recusando por limite dela, nem falha de configuração do nosso lado. A nova tentativa de uma chave recusada por falta de pagamento conta no momento dela.
409whatsapp_connection_not_readyA conexão não está ativa ou não pode enviar.
502whatsapp_message_failedA Meta recusou o envio (número inválido ou bloqueado, por exemplo).
503whatsapp_message_temporarily_unavailableEm geral, resultado incerto: a mensagem pode ter saído e sido cobrada. Não repita com a mesma chave (responderia 409 template_send_not_reusable); confira na conversa e, se não saiu, envie com chave nova.

Criar um template próprio

Para um caso que o catálogo do seu produto não cobre:

POST/integrations/whatsapp/connections/{connection_id}/templates
{
  "name": "aviso_atraso_profissional",
  "language": "pt_BR",
  "category": "UTILITY",
  "body": "Olá {{1}}, seu atendimento das {{2}} vai atrasar cerca de {{3}} minutos. Responda esta mensagem se preferir remarcar.",
  "examples": ["Ana", "14:30", "15"]
}

Variáveis fora de sequência ({{2}} sem {{1}}) respondem 422 template_placeholders_not_sequential; exemplos a menos ou a mais respondem 422 template_examples_required. Já existindo um template com o mesmo nome e idioma, só é possível criar de novo se ele estiver REJECTED ou ABSENT; nos outros casos, 409 whatsapp_template_already_exists. Conexão não ativa responde 409 whatsapp_connection_not_ready. Falha do lado da Meta responde 502 whatsapp_template_create_failed ou 503 whatsapp_template_create_temporarily_unavailable.

Responde 201 com name, language, status (o que a Meta informou, normalmente PENDING), approved_category, requested_category e variables_count. Dica que funciona: termine o texto pedindo uma resposta. Se o contato responder, a janela de 24 horas abre e o resto da conversa não gera custo de template.

Corrigir o texto de um template

POST/integrations/whatsapp/connections/{connection_id}/templates/edit
{
  "name": "aviso_atraso_profissional",
  "language": "pt_BR",
  "body": "Olá {{1}}, seu atendimento das {{2}} vai atrasar uns {{3}} minutos. Responda se preferir remarcar.",
  "examples": ["Ana", "14:30", "15"]
}

Nome, idioma e categoria não mudam. A Meta reanalisa o texto; a resposta 200 com "status": "edit_submitted" confirma só o pedido. Limites da Meta: 1 edição por dia em template aprovado e 10 edições a cada 30 dias; a API confere a primeira regra antes de chamar a Meta, e a segunda, quando violada, volta como 502 whatsapp_template_edit_failed.

A quantidade de variáveis pode mudar na edição. Se mudar, ajuste os envios: com a quantidade antiga, eles passam a responder 422 template_variables_mismatch.

HTTPerrorSignificado
404whatsapp_template_not_foundO template não existe nessa conexão.
409whatsapp_template_not_syncedRode templates/sync antes de editar.
409whatsapp_template_not_editableSó é possível editar template aprovado, reprovado ou pausado.
409whatsapp_template_has_unsupported_componentsO template tem cabeçalho, rodapé ou botões, que esta rota não edita.
409whatsapp_connection_not_readyA conexão não está ativa.
429template_edit_too_soonTemplate aprovado já editado nas últimas 24 horas. Tente depois desse prazo.
502 / 503whatsapp_template_edit_failed, whatsapp_template_edit_temporarily_unavailableFalha do lado da Meta.

Manutenção

10. Boas práticas e checklist

Segurança

Robustez

Antes de liberar para os clientes

  1. Conexão completa de ponta a ponta com um número de teste, até "status": "active".
  2. Recebimento: uma mensagem enviada ao número aparece nas conversas.
  3. Resposta em texto dentro da janela de 24 horas.
  4. Envio de template aprovado para um número que ainda não conversou.
  5. Tela de erro para cancelled, failed (com o error do retorno), onboarding_expired e coexistence_not_confirmed.
  6. Explicação ao cliente sobre Coexistência antes de ele abrir a tela da Meta.

11. Referência rápida de rotas

Todas exigem Authorization: Bearer. Prefixo comum: /integrations/whatsapp. O retorno da tela da Meta (etapa 3) é uma rota da própria API, chamada pelo navegador, e não entra nesta lista.

MétodoRotaPara quê
POST/onboarding/startIniciar conexão
POST/onboarding/{onboarding_id}/exchangeConfirmar autorização
GET/onboarding/{onboarding_id}/assetsListar contas e números autorizados
POST/onboarding/{onboarding_id}/confirm-assetsConfirmar conta e número
POST/onboarding/{onboarding_id}/connectionCriar a conexão
POST/connections/{connection_id}/subscribeAtivar a conexão
POST/connections/{connection_id}/disconnectDesconectar número e limpar dados locais
POST/connections/{connection_id}/coexistence-syncRetomar sincronização
GET/connectionsListar conexões
GET/connections/{connection_id}/contactsContatos
GET/connections/{connection_id}/conversationsConversas
GET/connections/{connection_id}/conversations/{conversation_id}/messagesMensagens
POST/connections/{connection_id}/conversations/{conversation_id}/messagesEnviar texto
GET/connections/{connection_id}/outbound-queueFila de saída
GET/connections/{connection_id}/templatesListar templates
POST/connections/{connection_id}/templatesCriar template
POST/connections/{connection_id}/templates/editCorrigir texto de template
POST/connections/{connection_id}/templates/syncAtualizar lista de templates
POST/connections/{connection_id}/templates/provisionCriar templates que faltam
POST/connections/{connection_id}/template-messagesEnviar template

12. Suporte

Em caso de dúvida ou problema, fale com o contato da Inteligencialy que entregou a sua credencial. Para qualquer erro, envie o request_id, o horário aproximado e a rota chamada. Nunca envie a credencial por mensagem, e-mail ou chamado.