Skip to main content

Listar e inspecionar números

Liste todos os números de WhatsApp da sua conta ou obtenha o detalhe completo de um número — incluindo a saúde e, para números Meta, dados em tempo real do Meta Graph.
Use uma chave de API com escopo de tenant para gerenciar números em toda a sua conta. Uma chave com escopo de número também pode chamar esses endpoints. Autentique-se com o cabeçalho x-api-key: ps_....

GET /v1/numbers — Listar todos os números

Lista todas as instâncias de WhatsApp do tenant da chave de API como um array JSON.
  • Não retorna credenciais de sessão.
  • Cada número expõe seu state de conexão plano e uma flag isFullyConnected.
  • A listagem não força uma atualização em massa do estado de conexão — use GET /v1/numbers/{id}/status por número quando precisar de uma verificação em tempo real.
  • As entradas apiKeys de cada item incluem apenas name, keyPrefix e keyLast4 (sem o id da chave de API).
  • Itens Meta (Cloud API) também trazem um bloco coexistence — veja a seção de Coexistência abaixo.
  • Todo item traz extraChargeUnpaidSince e extraChargeDisconnectAt — veja Cobrança de número extra sem pagamento abaixo.

GET /v1/numbers/ — Detalhe completo de um número

Retorna o state/isFullyConnected plano, um bloco health (veja O bloco health) e — para números Meta — um bloco aninhado em camelCase meta com informações de phone, profile e waba obtidas do Meta Graph. Para números não-Meta, meta é null.
string
Adicione ?refresh=1 para forçar uma busca em tempo real no Meta Graph e persistir o snapshot. Por padrão, a resposta é armazenada em cache (último snapshot meta).
Segredos (tokens de acesso, app secret, configuração do provedor) nunca são retornados. Retorna 404 se o id não pertencer ao tenant do chamador.
Resposta (número Meta):
Para um número não-Meta (não oficial / QR), a mesma chamada retorna "provider": "PILOT_STATUS", "meta": null e "coexistence": null.

O bloco historyImport

O que aconteceu com o histórico do aparelho na conexão atual. Os dois campos vêm null quando nada aconteceu.
Os dois são definitivos. A Meta entrega o histórico de um número uma vez só, e apagar e reconectar o número não faz ela enviar de novo. Uma nova conexão do mesmo número começa com os dois null.

O bloco health

health é o único lugar que responde este número consegue enviar agora, e se não, por quê. Ele é derivado dos sinais da Meta por um avaliador compartilhado, então a lista, o detalhe e o painel nunca discordam. Um bloqueio de envio fica fora de health: a cobrança de número extra sem pagamento. Ela é informada por extraChargeUnpaidSince, no nível de cima do número — veja Cobrança de número extra sem pagamento.
boolean
É o nosso veredito, não o da Meta. O state / isFullyConnected de um número Meta são derivados dela (sendable → "OPEN", caso contrário "DISCONNECTED").Leia como “a Pilot Status acredita, agora, que este número consegue enviar” — que é a soma dos campos abaixo, e não a mesma pergunta que “a Meta vai aceitar o próximo envio”. A segunda só a Meta responde, e responde por mensagem, na resposta àquele envio.
Ela governa o NOSSO envio, então um false errado não é cosmético. Quando vai a falso, a fila de envio do número é pausada e os jobs são adiados — as suas mensagens param de sair, e a Meta nunca as vê. Medido em 15/09/2026: um veredito derivado de um campo de estado do Graph manteve um número em sendable: false durante quase quatro horas enquanto a Meta aceitava os envios dele perfeitamente. A regra por trás disso foi retirada no mesmo dia.Então: alerte em sendable: false, sem dúvida — significa que o seu tráfego parou. Mas trate como “nós paramos de enviar, e o motivo é o code”, e confirme contra o erro de um envio real antes de dizer a um cliente que o número está bloqueado na Meta.
string | null
A nota de qualidade da Meta para o número, em maiúsculas e repassada: GREEN, YELLOW, RED, UNKNOWN. null quando a Meta não informou nenhuma. RED interrompe o envio do nosso lado (sendable: false).
string | null
O limite de mensagens do próprio número (messaging_limit_tier), por exemplo TIER_250, TIER_1K, TIER_2K, TIER_10K, TIER_100K, TIER_UNLIMITED. null quando a Meta não reporta nenhum para ele — o que já é informação: um número sem tier próprio não é um número Cloud API plenamente registrado, e chamadas não podem ser habilitadas nele.
string | null
O limite do Business Portfolio (whatsapp_business_manager_messaging_limit), mantido como campo próprio. A Meta o devolve mesmo quando o tier por número está ausente.Ele já substituiu o messagingLimitTier sempre que a Meta omitia o do número, e a substituição era invisível: o painel mostrava “2K/24h” para um número em TIER_250, e o portão de habilitar chamadas abria para 17 números que não tinham tier nenhum. Qual dos dois governa um envio é decisão da Meta, não nossa — leia os dois, e trate messagingLimitTier: null como “este número não tem limite próprio”, nunca como “ele tem o do portfólio”.
string | null
O status do número de telefone na Meta, em maiúsculas e repassado verbatim — não o enumeramos, porque a Meta acrescenta valores sem aviso. O que fixamos é a classificação de cada um:Um valor fora dos dois conjuntos não contribui para o veredito; a qualidade e a regra do número de teste abaixo ainda podem bloquear.
string | null
Causa legível por máquina, null quando saudável — com uma exceção, meta_phone_not_registered, que vem numa linha HEALTHY com sendable: true. Ramifique por aqui, não pelo reason.
string | null
A mensagem legível por humano da mesma causa, em português. String de diagnóstico, não contrato — o texto muda entre releases. Exiba; não faça parse.
As transições de saúde também são empurráveis. number.health_degraded, number.health_blocked, number.health_shadowban, number.recovered, number.inbound_stalled e number.inbound_recovered disparam na transição e estão no seletor de eventos — assine cada uma pelo nome, sem precisar do curinga "*".⚠️ Elas chegam na forma canónica { event, data }, não no envelope nativo da Meta. Se assinar saúde e conversa no mesmo webhook, o seu endpoint recebe dois formatos na mesma URL. Veja saúde do número na referência de eventos.

Cobrança de número extra sem pagamento

Um número extra — um número acima da capacidade que o seu workspace já pagou — é cobrado quando conecta pela primeira vez (Números extras). Se essa cobrança é recusada, o número continua conectado, mas o envio por ele fica bloqueado até o pagamento. Dois campos no nível de cima informam isso, em todo item de GET /v1/numbers (não oficiais e Meta) e em GET /v1/numbers/{id}:
string | null
Instante ISO 8601 em que a cobrança foi recusada pela primeira vez. O envio pelo número está bloqueado desde então: POST /v1/messages/send responde 409 NUMBER_BLOCKED com reason: "extra_charge_unpaid". null quando o número não está bloqueado.
string | null
Instante ISO 8601 em que o número será desconectado se continuar sem pagamento — 7 dias depois de extraChargeUnpaidSince. null quando o número não está bloqueado, e sempre null em número da Meta Cloud API: ele nunca é desconectado, fica bloqueado para envio até o pagamento.
Esses campos não fazem parte de health. O bloqueio é de cobrança, não de conexão: state, isFullyConnected e health.sendable continuam descrevendo só a conexão, e extra_charge_unpaid nunca aparece em health.code. Um número pode mostrar state: "OPEN" e health.sendable: true e ainda assim recusar todo envio. Confira também o extraChargeUnpaidSince.
Para remover o bloqueio, chame POST /v1/numbers/{id}/pay, recarregue a carteira ou salve um cartão. Depois do pagamento, os dois campos voltam a null.

Atualizando a partir do Meta Graph

?refresh=1 é a única coisa que lê a Meta. Nada atualiza o meta por agendamento para você: sem ele, meta.fetchedAt fica preso a quando o snapshot foi gravado pela última vez — tipicamente o momento do pareamento — não importa quanto tráfego o número tenha tratado desde então. O que custa um refresh: quatro leituras sequenciais na Graph (o nó do telefone, o perfil comercial, a WABA e as configurações de chamada), cada uma com try/catch independente, mais uma escrita para persistir o snapshot. Não há limite de taxa nosso na chamada; o teto que você vai encontrar é o throttling da própria Graph, compartilhado com toda outra chamada feita com o token daquele número. Como o refresh persiste, a atualidade não é propriedade do endpoint: um GET /v1/numbers posterior lê o mesmo snapshot que o refresh acabou de gravar. A lista não é permanentemente “mais fraca” que o detalhe — é o mesmo cache.
Por que coexistence.evidence pode diferir entre duas chamadas no mesmo instante. As duas rotas rodam o mesmo predicado, e ele prefere a evidência mais forte presente no snapshot:
  1. meta.phone.isOnBizApp → meta_is_on_biz_app
  2. senão metaIsCoexistence (gravado do que a Meta respondeu no onboarding) → history_sync
  3. senão a impressão digital → phone_fields (só aparece com LIKELY)
  4. senão UNKNOWN
is_on_biz_app é um campo não documentado da Graph, então ele viaja na lista rica de campos; se essa requisição falha, caímos para um conjunto mínimo que não o inclui. Um snapshot sem ele responde então por history_sync — mesmo status, evidência mais fraca. Um ?refresh=1 bem-sucedido recoloca o isOnBizApp no snapshot, e a partir daí as duas rotas reportam meta_is_on_biz_app.
UNKNOWN não é problema de tempo, então esperar não resolve. Ele significa que não temos evidência nenhuma: sem isOnBizApp no snapshot, sem resposta de onboarding e sem casamento da impressão digital. Chame ?refresh=1 imediatamente — é ele que busca o is_on_biz_app, e normalmente resolve para CONFIRMED ou NO nessa mesma chamada.Se continuar UNKNOWN depois de um refresh bem-sucedido, a leitura rica do telefone está falhando. Olhe meta.errors / meta.graphErrors na mesma resposta para o motivo (token expirado e app removido aparecem os dois lá) — repetir num timer não vai ajudar.

Coexistência — e por que o platformType não responde isso

Coexistência significa que o número continua ativo no app WhatsApp Business de um aparelho e, ao mesmo tempo, está conectado à Cloud API.
Não leia meta.phone.platformType para responder isso. Um número em coexistência reporta CLOUD_API — porque ele está na Cloud API. É isso que coexistência significa. O conjunto de valores da Meta para esse campo não tem COEXISTENCE, então um número em coexistência e um número Cloud API “puro” são indistinguíveis por ali.
Leia o bloco coexistence. Ele vem nos números Meta tanto em GET /v1/numbers quanto em GET /v1/numbers/{id}, e é null para números não oficiais (pareados por QR).
string
CONFIRMED — o número está em coexistência. NO — não está; é um número Cloud API puro (indisponível no app do celular). LIKELY — inferido, não respondido: a Meta deixa marcas num número em coexistência (ela recusa o /register, então ele fica NOT_VERIFIED, com nameStatus NON_EXISTS e sem PIN). UNKNOWN — não é possível responder com o que temos.
string
De onde veio a resposta, para você separar resposta de palpite: meta_is_on_biz_app (campo da própria Meta, também espelhado em meta.phone.isOnBizApp), history_sync (o que a Meta respondeu quando pedimos o histórico do aparelho) ou phone_fields (a inferência acima — só aparece com LIKELY). É null quando o status é UNKNOWN.
UNKNOWN não quer dizer “não”. O sinal por trás do LIKELY é de mão única: quando ele aparece, vale confiar; a ausência dele não prova nada. Trate UNKNOWN como “pergunte de novo depois” — chamar ?refresh=1 no número busca a resposta da própria Meta e normalmente promove o status para CONFIRMED ou NO.

O que a coexistência muda na prática

  • A Meta recusa o /register do número (“not available for SMB businesses”), então ele nunca vira um número Cloud API plenamente registrado.
  • Chamadas não podem ser habilitadas enquanto o número seguir em coexistência.
  • Mensagens funcionam normalmente — envio e recebimento não são afetados.
  • Para usar chamadas, o número precisa ser migrado para fora do app WhatsApp Business. Essa decisão é de quem é dono do número.