Integração

Webhooks

Receba eventos da Meta e atualizações da HookCloud no webhook do seu sistema. Entenda cada payload para atualizar o painel do seu assinante.

Arquitetura de entrega

Metacampos operacionais elegíveis ao callback por número
→
Webhook do partnerpor phone_number_id
HookCloudtemplate.updated, lifecycle e saúde
→
Mesmo endpointassinado com HMAC

A URL salva no Partner Portal é aplicada às instâncias ativas. Quando a URL muda, a HookCloud migra os callbacks na Meta e mostra o progresso.

Referências: Webhooks Meta · Override de callback

Verificação GET e preparação inicial

O passo a passo e o código Express estão em Configuração inicial do webhook. O GET compara hub.verify_token e devolve hub.challenge como texto puro. Esse teste não salva o endpoint nem autentica os POSTs.

Esta página é a referência dos eventos POST: formato dos payloads, identificação do cliente, assinaturas, processamento e reconciliação.

Eventos originais da Meta: confira o campo e o destino

Campos operacionais elegíveis ao override de callback por número podem chegar diretamente da Meta ao endpoint aplicado à conexão. Campos administrativos enviados ao callback global do app são interpretados pela HookCloud somente quando há tratamento implementado e podem gerar os eventos normalizados específicos deste guia. Campos desconhecidos são ignorados pelo handler global; não há encaminhamento bruto genérico de todos os campos do app.

Uma assinatura habilitada no app, por si só, não comprova entrega ao parceiro nem disponibilidade do recurso. Preserve o payload original dos eventos que seu receptor efetivamente recebe e não transforme todos os campos em mensagens. O parser abaixo se aplica aos envelopes originais da Meta; os eventos HookCloud seguem seus próprios contratos.

Ver cada campo Meta: assinatura, payload, entrega e ação →

javascript
// Execute após verificar a origem/autenticidade do webhook.
// Persistir na fila deve terminar antes do HTTP 200.
export function parseMetaEvents(payload) {
  const events = [];
  for (const entry of payload.entry ?? []) {
    for (const change of entry.changes ?? []) {
      const value = change.value ?? {};
      const base = { wabaId: entry.id, phoneNumberId: value.metadata?.phone_number_id };
      if (change.field !== 'messages') {
        events.push({ ...base, kind: 'meta_event', field: change.field, value });
        continue;
      }
      for (const message of value.messages ?? []) {
        const contact = (value.contacts ?? []).find(c =>
          (message.from_user_id && c.user_id === message.from_user_id) ||
          (message.from && c.wa_id === message.from));
        events.push({ ...base, kind: 'inbound', message,
          userId: message.from_user_id ?? contact?.user_id ?? null,
          parentUserId: message.from_parent_user_id ?? contact?.parent_user_id ?? null,
          username: contact?.profile?.username ?? null,
          phone: message.from || contact?.wa_id || null });
      }
      for (const status of value.statuses ?? []) {
        events.push({ ...base, kind: 'status', status,
          userId: status.recipient_user_id ?? null,
          parentUserId: status.recipient_parent_user_id ?? null,
          phone: status.recipient_id || null });
      }
    }
  }
  return events;
}

O parser acima é um adaptador de dados. O receptor HTTP precisa validar a origem/assinatura conforme a integração, gravar os eventos de forma durável e então responder 200. Responder antes da gravação pode perder o evento se o processo cair. O hub.verify_token do desafio GET não valida a assinatura HMAC de um POST.

Para mensagens, deduplique pelo wamid dentro da instância. Para status, use também o estado e preserve a progressão sem regredir de read para sent se eventos chegarem fora de ordem. Não procure uma pessoa apenas pelo telefone; ele pode estar ausente.

Referência: Campos e exceções dos webhooks de identidade.

Eventos HookCloud e HMAC

Verifique a assinatura com o segredo do endpoint, usando o corpo bruto e o timestamp da tentativa. O exemplo rejeita entradas malformadas e não usa a Partner API Key ou o token de verificação GET.

HeaderDescrição
X-HookCloud-EventTipo do evento. Após validar a assinatura, escolha o contrato correspondente; alguns eventos antigos não repetem o tipo no corpo.
X-HookCloud-DeliveryID da entrega para um endpoint; permanece o mesmo nas retentativas.
X-HookCloud-TimestampUnix timestamp em segundos, gerado na tentativa de envio.
X-HookCloud-Signaturesha256=HMAC_SHA256(webhook_secret, timestamp.rawBody), em hexadecimal.
Node.js — servidorjavascript
import crypto from 'node:crypto';

export function verifyHookCloudWebhook({ rawBody, timestamp, signature, secret }) {
  if (typeof secret !== 'string' || !secret) return false;
  if (typeof rawBody !== 'string' && !Buffer.isBuffer(rawBody)) return false;
  if (typeof timestamp !== 'string' || !/^\d{1,12}$/.test(timestamp)) return false;
  if (typeof signature !== 'string' || !/^sha256=[a-f0-9]{64}$/i.test(signature)) return false;
  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest();
  const received = Buffer.from(signature.slice(7), 'hex');
  return expected.length === received.length && crypto.timingSafeEqual(expected, received);
}

Atualizar o painel do seu assinante por webhook

O seu sistema chama a Partner API para cadastrar e consultar; a HookCloud chama o webhook HTTPS do seu sistema por POST para avisar mudanças. Use esses eventos para atualizar o cadastro e o painel do seu assinante. Consulte a Partner API na primeira carga ou para reconciliar uma informação perdida, antiga ou incompleta.

Exemplo: a empresa passa a constar como verificada na Meta → a HookCloud confirma o estado → envia hookcloud.instance.health_updated por POST à URL cadastrada → o seu backend atualiza o cliente e o número correspondentes → o seu painel mostra a verificação.

Este POST é recebido pelo seu sistema. Ele não é uma rota para você chamar na HookCloud. Já a resposta de get-partner-instance é devolvida à sua própria consulta e pode ser usada para a primeira carga ou reconciliação.

Na aba Integração do Portal, procure Eventos recebidos no seu webhook. O catálogo abaixo usa a mesma fonte de exemplos e significados exibida no Portal.

Configurar o receptor e processar com segurança

  • Cadastre e valide a URL em Endpoints. O endpoint deve estar habilitado e subscribed_events deve incluir o nome exato do evento ou *. A seleção de eventos HookCloud é diferente dos campos assinados no app da Meta. A URL de destino pode ser a mesma, mas cada origem tem seu contrato e sua assinatura.
  • A requisição recebida no seu servidor é POST com Content-Type: application/json. Eventos originais da Meta usam object/entry/changes e X-Hub-Signature-256; eventos HookCloud usam os formatos abaixo e X-HookCloud-Event, X-HookCloud-Delivery, X-HookCloud-Timestamp e X-HookCloud-Signature. O desafio GET hub.verify_token serve somente para validar a URL.
  • O segredo HMAC do endpoint (webhook_secret, prefixo hc_whsec_) é diferente da Partner API Key, do token de verificação GET e do token Meta. Ele é provisionado com o endpoint; o GET de consulta do endpoint não devolve esse segredo. Se você não guardou a credencial fornecida no provisionamento, trate a configuração com o administrador antes de liberar o receptor.
  • Valide a assinatura HMAC SHA-256 sobre timestamp + ponto + corpo bruto, sem reserializar o JSON. Confira também o timestamp em segundos (por exemplo, tolerância de cinco minutos). Não escolha a origem apenas pelo nome de um campo; valide a assinatura apropriada antes de aceitar o conteúdo.
  • Persista ou enfileire o evento de forma durável antes de responder HTTP 2xx. Timeout e respostas fora de 200–299 provocam novas tentativas; redirects não são seguidos. A entrega tem tentativas limitadas e pode terminar em dead_letter. Não há garantia de entrega exatamente uma vez ou de ordem.
  • Para os eventos documentados, use payload.event_id quando presente; senão, payload.id. X-HookCloud-Delivery identifica a entrega para um endpoint e permanece o mesmo nas retentativas; não representa o cliente nem o número. Guarde o ID do evento para impedir processamento duplicado. Nos eventos antigos de ciclo de vida, id e event_id podem ser diferentes: prefira event_id.
  • Cada evento tem seu próprio envelope. Alguns usam event e campos na raiz; outros usam type e data. Campos ausentes ou null não são prova de ausência de pagamento, empresa não verificada ou conexão desligada. Preserve campos desconhecidos e ignore tipos ainda não implementados após registrar a ocorrência.
  • Também existem avisos de operações de cadastro, como hookcloud.partner.customer.updated, hookcloud.partner.customer.merged, hookcloud.partner.instance.moved, hookcloud.partner.customer.erasure_started, hookcloud.partner.customer.erasure_failed e hookcloud.partner.customer.erased. Esses contratos antigos enviam o payload da operação sem envelope universal: podem não ter id, event_id, event, type, partner_id ou created_at no body. Identifique o tipo pelo X-HookCloud-Event validado, deduplique pelo X-HookCloud-Delivery quando não houver ID de evento e reconcilie o cliente/instância pela Partner API. Não aplique o parser dos eventos de saúde a esses corpos.
  • Compare connection_generation quando existir (é um UUID opaco, não um contador ordenável): um evento de uma autorização anterior não deve substituir a conexão atual. Use observed_at, checked_at ou evaluated_at do fato e não apenas a hora de chegada. Se houver geração diferente, empate ou dúvida de ordem, consulte get-partner-instance/get-partner-customer para reconciliar.
  • GET get-partner-instance e list-partner-instances consultam o estado armazenado na HookCloud; não fazem uma nova leitura Graph a cada chamada. Use page e page_size nas listagens e percorra as páginas retornadas. Para uma conferência explícita use refresh-partner-instance-meta, com controle de frequência; não faça polling contínuo à Meta.
  • A sincronização é eventual: a Meta pode levar tempo para propagar a mudança e a HookCloud usa fila e recuo após erros. Nem todo GET gera evento: saúde e perfil são notificados quando os dados relevantes mudam. O POST confirma uma observação, não uma garantia permanente de operação.
  • Os exemplos abaixo são ilustrativos. IDs e datas são fictícios; connect_url contém somente um marcador. Não copie esses valores para produção nem registre credenciais, links de conexão ou conteúdo pessoal desnecessário em logs.
  • O perfil opcional meta_profile possui cache de até 6 horas, separado da validade de 45 minutos da saúde. Uma nova observação relevante pode antecipar a leitura, respeitando fila e backoff. Mudanças materiais de limite/username seguem em hookcloud.instance.health_updated; apenas a passagem do tempo ou mudança de checked_at não cria um evento repetido. GETs locais e o refresh normal podem reutilizar esse cache: refreshed_at geral não substitui checked_at de cada campo.
  • A rotina normal de reconciliação usa 30 minutos mais uma distribuição de 0 a 60 segundos, além do tempo de cron/fila. Evento não garante leitura imediata: somente o domínio pertinente pode ser reavaliado, respeitando piso de 15 minutos, orçamento de tentativas, cache e backoff. Dados empresariais têm cache de 24 horas; pagamento, 6 horas ou 24 horas quando permission_error. Atualização manual não contorna esses limites.
  • instance.identity_replaced informa uma troca confirmada de IDs Meta, automática na reconexão do mesmo número ou explícita pelo contrato avançado. A instance_key permanece. Telefones enviados pelo navegador não autorizam substituição; a HookCloud exige prova da Meta e encerramento anterior.
  • instance.meta_link_removed é emitido após confirmação assinada da perda de vínculo. O evento usa data.meta_link_state com evidence_source; o GET expõe a projeção meta_link com source. Não confunda esses nomes nem use erros Graph 100/33/190 como prova de remoção.
Informação recebidaOrigemO que fazer
Mensagens e status originais em object/entry/changesMeta; assinatura X-Hub-Signature-256Trate o evento original no seu sistema. Para alimentar a Saúde de Entregas da HookCloud, reporte status em report-partner-message-status.
Saúde, perfil, risco, templates e ciclo de vida deste catálogoHookCloud; assinatura X-HookCloud-SignatureAtualize o cache e o painel conforme a identidade e os campos de cada evento. Não reenvie esses eventos à rota de status de mensagens.
ok, items, total, page, page_size nas consultasResposta HTTP da Partner API à sua chamada GETok indica sucesso da consulta, não saúde Meta; items é a página retornada. Percorra as páginas conforme o contrato da operação, sem confundir paginação com eventos de webhook.

As novas tentativas de entrega têm recuo crescente (1, 2, 4, 8 minutos, limitado a 60 minutos), respeitando o limite de tentativas da entrega. O padrão é cinco tentativas. Um X-HookCloud-Delivery repetido não exige repetir sua operação de negócio. Eventos sem conclusão devem ser investigados na gestão de entregas; não existe garantia de recepção se o seu endpoint permanecer indisponível.

Eventos HookCloud e significado de cada payload

Abra o evento que seu servidor recebeu. Os formatos são diferentes por contrato; não suponha que todos tenham um objeto data ou um campo event.

Saúde, pagamento Meta e verificação da empresa
hookcloud.instance.health_updated

Atualiza o diagnóstico do assinante: conexão técnica, mensagens, pagamento e verificação empresarial. Também transporta observações opcionais de limite Meta e username comercial, sem usá-las para declarar saúde positiva ou negativa.

Quando chega

Quando a HookCloud registra mudança relevante de saúde, pagamento, verificação empresarial ou perfil opcional (limite e username). Atualizar somente checked_at ou a marca stale do perfil opcional não gera evento periódico. Uma consulta sem mudança material não garante outra notificação.

Como identificar o cliente e o número

Use partner_id + instance_id para localizar o número; customer_id é o UUID do cliente na HookCloud. Os dados ficam em connection_health na raiz, não em data. connection_generation identifica a autorização vigente.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111111",
  "event_id": "11111111-1111-4111-8111-111111111111",
  "created_at": "2026-10-02T15:00:02Z",
  "schema_version": "partner-health-v2",
  "event": "hookcloud.instance.health_updated",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "customer_id": "44444444-4444-4444-8444-444444444444",
  "connection_generation": "55555555-5555-4555-8555-555555555555",
  "observed_at": "2026-10-02T15:00:00Z",
  "connection_health": {
    "state": "ready",
    "ready_to_operate": true,
    "checked_at": "2026-10-02T15:00:00Z",
    "connection_status": "connected",
    "authorization_status": "available",
    "callback_status": "active",
    "inventory_state": "active",
    "meta_account_state": "no_explicit_restriction_observed",
    "messaging_capability": {
      "state": "available",
      "reason": null
    },
    "meta_payment": {
      "status": "no_issue_detected",
      "method_presence": "unknown",
      "issue_codes": [],
      "source": "graph_health",
      "method_source": null,
      "checked_at": "2026-10-02T15:00:00Z",
      "observed_at": "2026-10-02T15:00:00Z",
      "stale": false,
      "unavailable_reason": "permission_error"
    },
    "business_verification": {
      "status": "verified",
      "source": "graph_business",
      "checked_at": "2026-10-02T15:00:00Z",
      "observed_at": "2026-10-02T15:00:00Z",
      "stale": false,
      "unavailable_reason": null
    },
    "evidence": {
      "schema_version": "partner-health-v2",
      "complete": true,
      "stale": false,
      "truncated": false,
      "checked_at": "2026-10-02T15:00:00Z",
      "source": "graph",
      "identity_match": true
    },
    "allowed_actions": [
      "view_history",
      "refresh_health",
      "disconnect"
    ],
    "operation": null,
    "observation_scope": "current",
    "verification_status": "verified",
    "issues": [],
    "meta_profile": {
      "messaging_limit": {
        "status": "available",
        "source": "graph_phone_portfolio_limit",
        "checked_at": "2026-10-02T15:00:00Z",
        "unavailable_reason": null,
        "stale": false,
        "tier": "TIER_2K",
        "scope": "business_portfolio"
      },
      "username": {
        "status": "available",
        "source": "graph_phone_username",
        "checked_at": "2026-10-02T15:00:00Z",
        "unavailable_reason": null,
        "stale": false,
        "value": "empresa.exemplo",
        "lifecycle_status": "reserved"
      }
    }
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
id / event_idUUIDNeste evento os dois identificam o mesmo fato. Guarde event_id para deduplicar; não o use como instance_id.
event / schema_versionstringevent seleciona o tratamento hookcloud.instance.health_updated. partner-health-v2 identifica a versão do contrato; não é a versão da Graph API.
partner_id / customer_id / instance_idUUIDIdentificam parceiro, cliente e número dentro da HookCloud. customer_id não é o external_customer_id do seu sistema. Mantenha esse vínculo ao cadastrar o cliente.
connection_generationUUIDIdentifica a geração da conexão. Se diferir da geração salva, confirme o estado atual pelo GET antes de aplicar a mudança; não reative dados de uma conexão anterior.
created_at / observed_atdata ISO 8601created_at é a criação do evento; observed_at é a observação que motivou a atualização. A hora de recebimento no seu servidor pode ser posterior por fila ou retentativa.
connection_health.state / ready_to_operateenum / booleanstate: ready (disponível segundo a observação), limited (atenção/limitação), blocked (operação impedida) ou unknown (não confirmado). ready_to_operate é a conclusão operacional da HookCloud; não a recalcule apenas pelo pagamento ou por status=connected.
connection_health.connection_statusenumEstado técnico: created, pending_signup, connected, paused, inactive, canceled, error ou unknown. connected significa autorização concluída, não garantia de envio.
connection_health.authorization_status / callback_statusenum / enumauthorization_status: available, unavailable ou unknown. callback_status descreve o destino dos eventos: active, configured, pending, error, failed, removed, removing, inactive ou unknown. São dimensões separadas da qualidade e do pagamento.
connection_health.inventory_state / meta_account_stateenum / enuminventory_state: active, attention, restricted, inactive ou unknown. meta_account_state: disabled, restricted, no_explicit_restriction_observed ou unknown. Pendência de pagamento isolada não deve virar rótulo de banimento.
connection_health.messaging_capability.state / reasonenum / string ou nullstate: available, limited, blocked ou unknown. reason explica o diagnóstico, por exemplo payment_setup, meta_restriction, connection_not_operational ou evidence_unavailable. Exiba a razão concreta e as issues; não prometa entrega de uma mensagem com base só neste campo.
connection_health.meta_payment.statusenumconfirmed: fonte de cobrança identificada na WABA exata; não confirma cartão específico nem sucesso futuro. no_issue_detected: leitura de saúde completa sem a pendência 141006, sem confirmar método. action_required: pendência explícita que requer conferência. unknown: falta de evidência atual. Não transforme no_issue_detected em cartão confirmado.
connection_health.meta_payment.method_presenceenumidentified: a leitura financeira identificou uma fonte de cobrança. not_confirmed: essa leitura não confirmou uma fonte. unknown: não foi possível determinar. Não descreve bandeira, validade, saldo ou dados de cartão.
connection_health.meta_payment.issue_codes / source / method_sourcearray de números / string ou nullissue_codes preserva 141006 quando observado. source pode ser graph_health, graph_funding ou null; method_source é graph_funding ou null. Ausência de permissão financeira não comprova ausência de cartão.
connection_health.meta_payment.checked_at / observed_at / stale / unavailable_reasondatas ou null / boolean / string ou nullDatas da evidência e sinal de desatualização. unavailable_reason explica a indisponibilidade, como permission_error ou identity_mismatch. Exemplo acima: a saúde não tem pendência, mas a permissão financeira não permite confirmar o método.
connection_health.business_verification.status / sourceenum / string ou nullverified, not_verified, pending ou unknown; source é graph_business, graph_waba ou null. Trata da empresa/portfólio, não da aprovação do nome do número e não de selo comercial.
connection_health.business_verification.checked_at / observed_at / stale / unavailable_reasondatas ou null / boolean / string ou nullInformam quando o estado empresarial foi consultado/observado e se a evidência ainda é atual; não são a data exata em que a Meta aprovou a empresa. unknown não equivale a empresa reprovada.
connection_health.evidenceobjetoschema_version indica o contrato; complete informa a completude; stale marca evidência antiga; truncated marca leitura parcial; checked_at e source (graph/stored) informam observação e origem; identity_match confirma correspondência à identidade atual.
connection_health.issues[]array de objetosCada item contém code, severity (info/warning/error/critical), message e action. domain e scope podem acompanhar a classificação, mas não são garantidos em avisos sintetizados. message explica o problema; action orienta a correção. Não converta qualquer warning em banimento.
connection_health.allowed_actionsarray de stringsAções autorizadas pelo diagnóstico: view_history, refresh_health, disconnect, connect ou contact_support conforme o caso. Respeite também a autorização e as validações da API no momento da execução.
connection_health.operationobjeto ou nullQuando presente: id, kind, status, generation, started_at, finished_at, lease_until, result_code, steps, remote_cleanup_confirmed e capacity_retained. processing não é conclusão; failed/needs_review/stale pedem conferência. Limpeza remota e liberação de capacidade são estados separados.
connection_health.observation_scope / verification_statusenum / enumobservation_scope é current ou historical: não use uma fotografia histórica para declarar conexão atual ativa. verification_status é verified ou pending e trata da conferência do diagnóstico, não da verificação empresarial.
connection_health.meta_profileobjetoObservações opcionais de messaging_limit e username. São independentes da saúde operacional. Sua ausência em um payload antigo ou indisponibilidade não significa limite zero, ausência de username, bloqueio ou disponibilidade confirmada.
connection_health.meta_profile.messaging_limit.status / tierenum / string ou nullstatus=available informa que há uma faixa válida e atual observada; unavailable indica que não foi possível confirmar. tier pode ser TIER_250, TIER_2K, TIER_10K, TIER_100K ou TIER_UNLIMITED, entre valores que a Meta venha a retornar. Não é saldo restante, consumo, quantidade de mensagens do dia ou franquia paga na HookCloud. Null não significa zero nem ilimitado.
connection_health.meta_profile.messaging_limit.scope / sourcestring ou nullbusiness_portfolio e graph_phone_portfolio_limit vêm do campo moderno whatsapp_business_manager_messaging_limit: limite compartilhado pelos números do portfólio, para usuários únicos fora da janela de atendimento em 24 horas móveis. phone_legacy e graph_phone_legacy_limit identificam fallback antigo messaging_limit_tier; não some como capacidade extra nem trate esse fallback como nova confirmação do portfólio. TIER_UNLIMITED não elimina rate limits e outras políticas.
connection_health.meta_profile.username.status / value / lifecycle_statusenum / string ou nullstatus=available confirma que o texto do username foi observado, não que esteja ativo. value é o username comercial; lifecycle_status é approved, reserved ou null quando não confirmado. reserved significa reservado, não aprovado/ativo. available com lifecycle_status=null exige rótulo neutro de estado não confirmado. Username comercial não é BSUID nem destinatário para o envio de mensagens.
connection_health.meta_profile.messaging_limit e username: checked_at / source / stale / unavailable_reasondata ISO ou null / string ou null / booleanCada observação tem seu próprio horário, origem, validade e motivo de indisponibilidade. O cache opcional tem até 6 horas e respeita a identidade e geração da conexão. Expiração ou identidade diferente neutraliza tier/value/status de ciclo de vida; não reaproveite metadata antigo. permission_error, field_unavailable, rate_limited, timeout, identity_mismatch e stale explicam limitações sem afirmar ausência do dado. source de username é graph_phone_username ou null.

O que atualizar no seu sistema

  • Atualize apenas a instância identificada, conservando a data e a geração da observação.
  • Mostre pagamento Meta, empresa verificada, conexão e capacidade de envio em indicadores separados.
  • No exemplo, exiba Empresa verificada e Sem pendência detectada; não exiba Cartão confirmado.
  • Se a observação for antiga, parcial ou de geração desconhecida, reconcilie pelo GET antes de substituir o estado atual.
  • Use o limite Meta como faixa informativa do portfólio, sem multiplicá-lo pelo número de instâncias e sem apresentar um saldo de mensagens que este payload não calcula.
  • Exiba username reservado e situação não confirmada separadamente de username aprovado. Atualize seu cache pelos eventos materiais; não dispare GETs diretos à Meta para cada card.

Chamadas relacionadas

get-partner-instance · list-partner-instances · get-partner-instance-history · refresh-partner-instance-meta

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Qualidade, nome aprovado e portfólio do número
hookcloud.instance.updated

Mantém o card do número atualizado com o nome e a qualidade informados pela Meta e os nomes do portfólio empresarial e da conta WhatsApp.

Quando chega

Quando mudam instance_name, verified_name, name_status, quality_rating, meta_business_name ou meta_waba_name. A mudança de um desses campos gera uma fotografia do perfil abaixo.

Como identificar o cliente e o número

Campos na raiz, com event (não type/data). Use partner_id + instance_id; customer_id localiza o cliente interno. Compare connection_generation antes de atualizar uma reconexão.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111112",
  "event_id": "11111111-1111-4111-8111-111111111112",
  "schema_version": "partner-instance-v2",
  "event": "hookcloud.instance.updated",
  "created_at": "2026-10-02T15:01:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "customer_id": "44444444-4444-4444-8444-444444444444",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "instance_key": "hc_inst_EXEMPLO",
  "connection_generation": "55555555-5555-4555-8555-555555555555",
  "instance_name": "Empresa Exemplo Atendimento",
  "verified_name": "Empresa Exemplo Atendimento",
  "name_status": "APPROVED",
  "quality_rating": "GREEN",
  "meta_business_id": "BUSINESS_ID",
  "meta_business_name": "Portfólio Empresa Exemplo",
  "meta_waba_id": "WABA_ID",
  "meta_waba_name": "WhatsApp Empresa Exemplo",
  "meta_phone_number_id": "PHONE_NUMBER_ID"
}

Significado dos campos do payload

CampoTipoSignificado e como usar
id / event_id / event / schema_versionUUID / stringevent_id identifica o fato para deduplicação. event é hookcloud.instance.updated e schema_version é partner-instance-v2.
partner_id / customer_id / instance_id / instance_keyUUID / stringVínculos internos e chave imutável da instância. Não confunda instance_key com um segredo nem com o telefone; guarde o relacionamento com seu external_customer_id.
created_at / connection_generationdata ISO / UUIDCriação do evento e geração da autorização. Este payload não garante observed_at próprio; confirme pelo GET se não conseguir determinar se a atualização é mais recente.
instance_name / verified_name / name_statusstring ou nullinstance_name é o nome apresentado no Partner; ele acompanha o verified_name quando a aprovação é confirmada. name_status traz o estado Meta, por exemplo APPROVED, AVAILABLE_WITHOUT_REVIEW ou PENDING_REVIEW. Nome pendente não prova bloqueio de mensagens nem empresa não verificada.
quality_ratingstring ou nullQualidade informada pela Meta: normalmente GREEN, YELLOW, RED ou UNKNOWN. RED é um sinal de qualidade; não é sinônimo de banimento. Para o bloqueio de novas conexões use customer_risk.block_new_connections.
meta_business_id / meta_business_namestring ou nullID e nome do portfólio empresarial associado, quando disponíveis. A presença de um nome não comprova verificação da empresa; leia business_verification no diagnóstico.
meta_waba_id / meta_waba_name / meta_phone_number_idstring ou nullIdentificadores da conta WhatsApp e do número na Meta e nome da conta. phone_number_id é um ID técnico, não o telefone visível e não o BSUID do destinatário.

O que atualizar no seu sistema

  • Atualize nome, qualidade e associação ao portfólio no card desse número.
  • Guarde o nome retornado pela HookCloud; não mantenha um nome provisório diferente depois da aprovação.
  • Não derive pagamento, verificação empresarial ou banimento apenas de qualidade/name_status. Use o evento de saúde e o de risco para esses indicadores.

Chamadas relacionadas

get-partner-instance · list-partner-instances

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

BAN, RED e restrição de novas conexões do cliente
hookcloud.customer.risk_updated

Atualiza a política individual de risco do cliente do parceiro, preservando os números já conectados.

Quando chega

Quando muda a política calculada com as evidências retidas: contagens, bloqueio, cobertura ou prazos da janela. A proteção é aplicada no servidor mesmo que seu sistema não processe o webhook.

Como identificar o cliente e o número

Este evento tem event e type iguais; customer_id fica na raiz e data.partner_customer_id repete o cliente. Não é uma restrição de todo o parceiro: o escopo é esse cliente.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111113",
  "event_id": "11111111-1111-4111-8111-111111111113",
  "event": "hookcloud.customer.risk_updated",
  "type": "hookcloud.customer.risk_updated",
  "created_at": "2026-10-02T15:02:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "customer_id": "44444444-4444-4444-8444-444444444444",
  "data": {
    "partner_customer_id": "44444444-4444-4444-8444-444444444444",
    "customer_risk": {
      "schema_version": "partner-customer-risk-v2",
      "scope": "customer",
      "partner_id": "22222222-2222-4222-8222-222222222222",
      "partner_customer_id": "44444444-4444-4444-8444-444444444444",
      "policy_basis": "grouped_incidents_rolling_window",
      "grouping": "exact_identity_between_explicit_restorations",
      "window_days": 30,
      "window_start": "2026-09-02T15:02:00Z",
      "evaluated_at": "2026-10-02T15:02:00Z",
      "ban_count_30d": 0,
      "red_count_30d": 5,
      "limits": {
        "ban": 20,
        "red": 5
      },
      "block_new_connections": true,
      "manual_blocked": false,
      "risk_level": "enforced",
      "reasons": [
        "red_window_limit"
      ],
      "next_exit_at": "2026-10-03T10:00:00Z",
      "next_exit_quantity": 1,
      "next_unlock_at": "2026-10-03T10:00:00Z",
      "timezone": "UTC",
      "existing_connections_preserved": true,
      "history_started_at": "2026-09-01T00:00:00Z",
      "history_complete": true,
      "coverage_reason": "Counts use retained verified evidence; events before retained history are not reconstructed."
    }
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
id / event_id / event / type / created_atUUID / string / dataevent_id deduplica o fato. event e type contêm hookcloud.customer.risk_updated. created_at é a criação do evento; a política tem sua própria evaluated_at.
partner_id / customer_id / data.partner_customer_idUUIDIdentificam o parceiro e o cliente interno afetado. A política segue a identidade do cliente, não todos os assinantes do parceiro.
data.customer_risk.schema_version / scope / partner_id / partner_customer_idstring / UUIDContrato partner-customer-risk-v2, escopo customer e identidade do titular da política. Guarde esses dados junto com as contagens.
data.customer_risk.ban_count_30d / red_count_30d / limitsinteiros / objetoContagens de incidentes agrupados nos últimos 30 dias e limites {ban:20,red:5}. Não conte cada reentrega do mesmo webhook como um novo BAN/RED.
data.customer_risk.block_new_connections / manual_blocked / risk_level / reasonsboolean / enum / arrayblock_new_connections é a decisão efetiva. manual_blocked indica análise manual. risk_level é enforced, warning ou none. reasons pode conter ban_window_limit, red_window_limit e manual_review. Mesmo com vagas pagas, a HookCloud recusa novas conexões desse cliente enquanto bloqueado.
data.customer_risk.policy_basis / grouping / window_days / window_start / evaluated_atstrings / inteiro / datasDescrevem o agrupamento por identidade entre recuperações explícitas, a janela móvel de 30 dias e quando ela foi calculada. Use evaluated_at para evitar regressão por evento atrasado.
data.customer_risk.next_exit_at / next_exit_quantity / next_unlock_at / timezonedatas ou null / inteiro / stringPróxima saída de incidente da janela, quantidade nessa saída e estimativa de desbloqueio conforme as evidências atuais. UTC é o fuso dos valores; formate para o usuário. Novos incidentes podem alterar o prazo. Bloqueio manual deixa next_unlock_at null.
data.customer_risk.existing_connections_preservedbooleantrue informa que a regra restringe novas conexões; ela não manda desconectar os números existentes.
data.customer_risk.history_started_at / history_complete / coverage_reasondata / boolean / stringInformam a cobertura do histórico utilizado. history_complete=false exige mostrar a limitação de cobertura; não significa que o bloqueio deixou de valer.

O que atualizar no seu sistema

  • Mostre as contagens, limites, razão e previsão ao lado do cliente e dos seus números.
  • Desabilite a ação de adicionar número quando block_new_connections=true; mantenha a validação do servidor como decisão final.
  • Não desconecte números existentes, não transfira a restrição a outros clientes e não recalcule incidentes somando webhooks.

Chamadas relacionadas

get-partner-customer · get-partner-instance · list-partner-instances

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Aprovação, qualidade e conteúdo do template
hookcloud.meta.template.updated

Atualiza o catálogo de templates da WABA para o seu sistema mostrar status, categoria, parâmetros e disponibilidade observada.

Quando chega

Quando a HookCloud detecta uma mudança no catálogo ou no estado do template por evento Meta ou sincronização. Deduplicação do produtor evita repetir a mesma transição como uma alteração nova.

Como identificar o cliente e o número

Envelope com type e data. Use partner_id + data.meta_waba_id + data.template_id; nome e idioma ajudam a localizar o modelo. Não há instance_id obrigatório: uma WABA pode estar associada a mais de um número.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111114",
  "type": "hookcloud.meta.template.updated",
  "created_at": "2026-10-02T15:03:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "data": {
    "meta_waba_id": "WABA_ID",
    "template_id": "TEMPLATE_ID",
    "name": "confirmacao_agendamento",
    "language": "pt_BR",
    "status": "APPROVED",
    "quality_status": "GREEN",
    "category": "UTILITY",
    "pending_category_change": null,
    "sendable": true,
    "missing_from_graph": false,
    "sync_status": "synced",
    "is_risky": false,
    "rejection_reason": null,
    "components": [
      {
        "type": "BODY",
        "text": "Seu agendamento foi confirmado."
      }
    ],
    "parameter_schema": {
      "total_parameters": 0,
      "parameter_format": "POSITIONAL",
      "parameters": [],
      "buttons": [],
      "components": [
        {
          "type": "BODY",
          "format": null,
          "text": "Seu agendamento foi confirmado.",
          "parameters": [],
          "example": null,
          "buttons": []
        }
      ]
    },
    "last_graph_sync_at": "2026-10-02T15:03:00Z",
    "source": "graph_sync"
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
id / type / created_at / partner_idUUID / string / dataid deduplica este evento; type é hookcloud.meta.template.updated. Este envelope não garante event_id, event, customer_id, instance_id nem previous_status. created_at é a criação do evento.
data.meta_waba_id / template_id / name / languagestring ou nullConta WhatsApp, ID Meta, nome e idioma do template. Armazene por WABA e identidade do template, sem sobrescrever o catálogo de outra conta ou tradução.
data.status / quality_status / categorystring ou nullStatus e qualidade conhecidos na Meta e categoria atual. status pode indicar aprovação, revisão, pausa ou rejeição; preserve valores novos. A categoria é distinta da qualidade.
data.pending_category_changeobjeto ou nullQuando presente, category é a categoria futura e effective_at é a data prevista. Não aplique como categoria atual antes de reconciliar sua vigência.
data.sendable / missing_from_graph / sync_status / is_riskybooleans / string ou nullsendable é a elegibilidade observada, não garantia de entrega. missing_from_graph indica ausência na leitura de catálogo; sync_status informa a sincronização. is_risky sinaliza cuidado operacional. Não envie ignorando status, regras da Meta ou falha posterior.
data.rejection_reason / components / parameter_schemastring ou null / array / objetoMotivo conhecido de rejeição, componentes Meta e esquema dos parâmetros. Use-os para montar os campos do formulário do seu sistema; não envie o payload do webhook inteiro como body de mensagem.
data.parameter_schema.total_parameters / parameter_format / parameters / buttons / componentsinteiro / string / arraystotal_parameters conta os parâmetros identificados; parameter_format informa o formato, como POSITIONAL. parameters descreve component, index/name, kind e required quando aplicáveis; buttons e components organizam os campos por botão/componente. Um modelo sem variáveis tem total_parameters=0 e parameters=[]. Use o catálogo de detalhes para montar os componentes de envio da Meta.
data.last_graph_sync_at / sourcedata ou null / stringÚltima sincronização Graph conhecida e origem desta atualização. A criação do evento e a leitura Graph podem ocorrer em horários diferentes.

O que atualizar no seu sistema

  • Atualize o modelo e o idioma no catálogo da WABA correspondente.
  • Recarregue os detalhes/parâmetros necessários pela Partner API se o cache local estiver incompleto.
  • Ao enviar, use o contrato de mensagens da Meta. Um sendable=true não substitui o acompanhamento de sent/delivered/read/failed.

Chamadas relacionadas

list-partner-meta-templates · get-partner-meta-template

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Preferência de marketing do usuário
hookcloud.meta.user_preferences.updated

Informa opt-out ou retomada de mensagens de marketing para o usuário do número exato.

Quando chega

Quando um evento válido de user_preferences da Meta é associado de forma inequívoca ao número e à WABA da instância.

Como identificar o cliente e o número

Envelope type/data. Use data.partner_instance_id + data.meta_phone_number_id e o user_id/wa_id de cada preferência; não espalhe a escolha por todos os números de uma WABA.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111115",
  "type": "hookcloud.meta.user_preferences.updated",
  "created_at": "2026-10-02T15:04:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "data": {
    "meta_waba_id": "WABA_ID",
    "partner_customer_id": "44444444-4444-4444-8444-444444444444",
    "partner_instance_id": "33333333-3333-4333-8333-333333333333",
    "meta_phone_number_id": "PHONE_NUMBER_ID",
    "user_preferences": [
      {
        "user_id": "BR.BSUID_EXEMPLO",
        "category": "marketing_messages",
        "value": "stop",
        "timestamp": "1790953440"
      }
    ]
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
id / type / created_at / partner_idUUID / string / dataid deduplica o evento. type seleciona a preferência. created_at é quando a HookCloud recebeu o evento Meta de origem; o horário da escolha individual é timestamp em cada preferência. Não há event_id/event garantidos.
data.meta_waba_id / partner_customer_id / partner_instance_id / meta_phone_number_idstring / UUIDDelimitam a WABA, cliente e número da preferência; use esse escopo junto à identidade do usuário.
data.user_preferences[].user_id / wa_idstring opcionalPode haver BSUID (user_id), identidade legada wa_id ou ambos. Preserve o BSUID completo, inclusive prefixo e letras; não exija telefone para aplicar a preferência.
data.user_preferences[].category / value / timestampstringcategory é marketing_messages; value é stop ou resume. timestamp é Unix em segundos como string e ordena a preferência do usuário. Não deixe um stop antigo sobrescrever um resume mais recente ou o inverso.

O que atualizar no seu sistema

  • Em stop, interrompa marketing nesse escopo no seu sistema; em resume, registre a retomada respeitando as demais regras e permissões da Meta.
  • Persista a preferência no seu backend. O GET de instância não é uma consulta do consentimento individual do usuário.

Este evento deve ser persistido no seu sistema; não há GET de preferências individuais nesta API.

Instância criada e link de conexão inicial
instance.created

Informa que o cadastro técnico foi criado. Pode acompanhar um link temporário para iniciar a autorização Meta.

Quando chega

Registrado junto com cada instância nova criada por create-customer-instance ou create-partner-instance. Reaproveitar a instância ou repetir a mesma operação idempotente não cria outro evento. A entrega é assíncrona e pode ser repetida; deduplique por event_id.

Como identificar o cliente e o número

Envelope event/data com instance_id na raiz. Guarde event_id para deduplicar, pois pode ser diferente de id.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111116",
  "event_id": "11111111-1111-4111-8111-111111111117",
  "event": "instance.created",
  "created_at": "2026-10-02T15:05:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "external_customer_id": "cliente-001",
  "data": {
    "status": "pending_signup",
    "instance_key": "hc_inst_EXEMPLO",
    "instance_name": "Nome provisório",
    "connect_url": "https://partner-connect.hookcloud.app/connect?token=TOKEN_TEMPORARIO_EXEMPLO"
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
event_id / id / event / created_atUUID / string / dataevent_id é o identificador persistido da ocorrência, preferido para deduplicar; id pode ser outro UUID do envelope. event é instance.created.
partner_id / instance_id / external_customer_idUUID / stringRelacionam o parceiro, a nova instância e o ID estável do cliente no seu sistema.
data.status / instance_key / instance_namestringpending_signup significa que a autorização ainda não terminou. A chave é gerada pela HookCloud; o nome pode ser provisório até a aprovação pela Meta.
data.connect_urlURL sensível temporária ou nullURL sensível e temporária ou null. A sessão inicial tenta preencher este campo antes da entrega. O valor pode ser null quando create_connect_session=false, a geração da sessão falha ou o evento é entregue antes de o link ficar disponível. Use connect_url e connect_expires_at da resposta HTTP; se não houver link, consulte a instância e gere depois com create-connect-session. Receber este evento não confirma WhatsApp conectado. Não publique o link em logs.

O que atualizar no seu sistema

  • Use instance_id, instance_key e o link retornados pela resposta HTTP da criação nas duas rotas. Não espere o webhook para continuar; o registro é durável, mas a entrega depende da disponibilidade do seu receptor.
  • Guarde os identificadores e apresente o link ao cliente somente quando connect_url estiver presente; senão, gere a sessão quando necessário.
  • Espere a conclusão e consulte a saúde antes de indicar que o número está operacional.

Chamadas relacionadas

get-partner-instance · create-connect-session

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Novo link temporário de conexão
connect_session.created

Avisa que foi gerada uma nova sessão para conectar ou reconectar a instância.

Quando chega

Emitido pela chamada explícita create-connect-session. A sessão inicial criada internamente junto com uma instância não gera obrigatoriamente este evento.

Como identificar o cliente e o número

Envelope event/data com partner_id e instance_id; external_customer_id e connection_generation não são garantidos neste evento.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111118",
  "event_id": "11111111-1111-4111-8111-111111111119",
  "event": "connect_session.created",
  "created_at": "2026-10-02T15:06:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "data": {
    "connect_session_id": "66666666-6666-4666-8666-666666666666",
    "connect_url": "https://partner-connect.hookcloud.app/connect?token=TOKEN_TEMPORARIO_EXEMPLO",
    "expires_at": "2026-10-03T15:06:00Z"
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
event_id / id / event / created_at / partner_id / instance_idUUID / string / dataevent_id deduplica a ocorrência; instance_id identifica a conexão. event é connect_session.created, não uma confirmação de conexão.
data.connect_session_idUUIDID da tentativa temporária, diferente de instance_id. Uma instância pode ter novas sessões ao longo do tempo.
data.connect_url / expires_atURL sensível / data ISOLink autorizado e prazo dessa sessão. Use expires_at retornado como fonte do prazo; a configuração padrão atual é 24 horas. A expiração do link não desconecta um número já autorizado.

O que atualizar no seu sistema

  • Use connect_session_id, connect_url e expires_at da resposta HTTP da chamada; não dependa da chegada do webhook para apresentar o link.
  • Disponibilize o link somente ao cliente correto.
  • Se expirado, crie nova sessão para a mesma instância, preservando a identidade e as regras de capacidade.

Chamadas relacionadas

get-partner-instance · create-connect-session

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Autorização da conexão concluída
instance.connected

Informa a conclusão técnica; a liberação para operar depende do diagnóstico de saúde enviado ou consultado.

Quando chega

Ao concluir o fluxo autorizado da conexão. Alterar apenas o status administrativo para connected é rejeitado pela API: é necessária a prova da conexão.

Como identificar o cliente e o número

Envelope event/data; instance_id fica na raiz. Este contrato de ciclo de vida não garante connection_generation na raiz. Consulte o GET se precisar confirmar a geração vigente.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111120",
  "event_id": "11111111-1111-4111-8111-111111111121",
  "event": "instance.connected",
  "created_at": "2026-10-02T15:07:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "external_customer_id": "cliente-001",
  "data": {
    "status": "connected",
    "connection_health": {
      "state": "ready",
      "ready_to_operate": true,
      "checked_at": "2026-10-02T15:00:00Z",
      "connection_status": "connected",
      "authorization_status": "available",
      "callback_status": "active",
      "inventory_state": "active",
      "meta_account_state": "no_explicit_restriction_observed",
      "messaging_capability": {
        "state": "available",
        "reason": null
      },
      "meta_payment": {
        "status": "no_issue_detected",
        "method_presence": "unknown",
        "issue_codes": [],
        "source": "graph_health",
        "method_source": null,
        "checked_at": "2026-10-02T15:00:00Z",
        "observed_at": "2026-10-02T15:00:00Z",
        "stale": false,
        "unavailable_reason": "permission_error"
      },
      "business_verification": {
        "status": "verified",
        "source": "graph_business",
        "checked_at": "2026-10-02T15:00:00Z",
        "observed_at": "2026-10-02T15:00:00Z",
        "stale": false,
        "unavailable_reason": null
      },
      "evidence": {
        "schema_version": "partner-health-v2",
        "complete": true,
        "stale": false,
        "truncated": false,
        "checked_at": "2026-10-02T15:00:00Z",
        "source": "graph",
        "identity_match": true
      },
      "allowed_actions": [
        "view_history",
        "refresh_health",
        "disconnect"
      ],
      "operation": null,
      "observation_scope": "current",
      "verification_status": "verified",
      "issues": [],
      "meta_profile": {
        "messaging_limit": {
          "status": "available",
          "source": "graph_phone_portfolio_limit",
          "checked_at": "2026-10-02T15:00:00Z",
          "unavailable_reason": null,
          "stale": false,
          "tier": "TIER_2K",
          "scope": "business_portfolio"
        },
        "username": {
          "status": "available",
          "source": "graph_phone_username",
          "checked_at": "2026-10-02T15:00:00Z",
          "unavailable_reason": null,
          "stale": false,
          "value": "empresa.exemplo",
          "lifecycle_status": "reserved"
        }
      }
    },
    "ready_to_operate": true,
    "meta_business_id": "BUSINESS_ID",
    "waba_id": "WABA_ID",
    "phone_number_id": "PHONE_NUMBER_ID",
    "display_phone_number": "+55 11 99999-0000",
    "verified_name": "Empresa Exemplo Atendimento",
    "quality_rating": "GREEN",
    "account_mode": "LIVE",
    "code_verification_status": "VERIFIED",
    "app_subscribed": true,
    "remote_callback_scope": "phone_number",
    "remote_callback_status": "active",
    "remote_callback_uri": "https://seusistema.com/webhooks/whatsapp",
    "last_error": null
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
event_id / id / event / created_at / partner_id / instance_id / external_customer_idUUID / string / dataevent_id é a chave preferida de deduplicação; instance_id e external_customer_id relacionam a conexão ao cliente. O prefixo deste evento é instance., sem hookcloud..
data.statusstringconnected informa a conclusão técnica do fluxo. Não implica ausência de pendências Meta nem garantia de envio.
data.connection_health / ready_to_operateobjeto / boolean, quando presentesNo retorno do fluxo de conexão, o diagnóstico tem o significado explicado em hookcloud.instance.health_updated. Se ausentes, faça get-partner-instance. Conectado não significa pagamento confirmado ou mensagens liberadas.
data.meta_business_id / waba_id / phone_number_id / display_phone_numberstring ou null, quando presentesPortfólio, conta WhatsApp, ID técnico do número e telefone visível. Observe que aqui os campos são waba_id/phone_number_id, sem prefixo meta_.
data.verified_name / quality_rating / account_mode / code_verification_statusstring ou null, quando presentesDados da observação Meta no fluxo concluído. Nome, qualidade, modo e verificação técnica são diferentes de verificação empresarial e pagamento.
data.app_subscribed / remote_callback_scope / remote_callback_status / remote_callback_uri / last_errorboolean / strings ou null, quando presentesInscrição do app, escopo, estado e destino do callback e último erro. Use-os para diagnóstico; não confunda ausência de erro com saúde integral.

O que atualizar no seu sistema

  • Atualize o estado técnico e consulte o diagnóstico completo quando não vier no evento.
  • Só mostre disponibilidade operacional conforme connection_health; preserve pendências e orientações Meta.

Chamadas relacionadas

get-partner-instance · get-partner-instance-history

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Instância pausada
instance.paused

Notifica a alteração do estado técnico para pausado.

Quando chega

Quando uma alteração de status gera o evento de pausa.

Como identificar o cliente e o número

Envelope event/data. Use partner_id e instance_id; os campos de saúde e geração não são obrigatórios.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111122",
  "event_id": "11111111-1111-4111-8111-111111111123",
  "event": "instance.paused",
  "created_at": "2026-10-02T15:08:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "external_customer_id": "cliente-001",
  "data": {
    "status": "paused",
    "reason": "Pausa solicitada"
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
event_id / id / event / created_atUUID / string / dataPrefira event_id na deduplicação; event seleciona instance.paused.
partner_id / instance_id / external_customer_idUUID / stringIdentidade da conexão e vínculo do cliente no seu sistema.
data.status / reasonstring / string ou nullEstado paused e motivo da alteração. Não comprova desregistro na Meta nem liberação de vaga.

O que atualizar no seu sistema

  • Mostre a pausa e reconcilie saúde e capacidade pela Partner API.

Chamadas relacionadas

get-partner-instance

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Instância inativa
instance.inactive

Notifica a alteração do estado técnico para inativo.

Quando chega

Quando uma alteração de status gera o evento de inativação.

Como identificar o cliente e o número

Envelope event/data. Não existe um evento universal instance.disconnected neste catálogo: use o tipo efetivamente recebido e consulte a operação.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111124",
  "event_id": "11111111-1111-4111-8111-111111111125",
  "event": "instance.inactive",
  "created_at": "2026-10-02T15:09:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "external_customer_id": "cliente-001",
  "data": {
    "status": "inactive",
    "reason": "Inativação solicitada"
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
event_id / id / event / created_atUUID / string / dataPrefira event_id para deduplicar; event seleciona instance.inactive.
partner_id / instance_id / external_customer_idUUID / stringIdentificam a conexão e seu cliente.
data.status / reasonstring / string ou nullEstado inactive e motivo. A confirmação da limpeza remota e a capacidade ficam no diagnóstico/operation; não são inferidas apenas deste status.

O que atualizar no seu sistema

  • Atualize o estado técnico e confira operation.remote_cleanup_confirmed e operation.capacity_retained no GET antes de concluir que a limpeza terminou e liberou vaga.

Chamadas relacionadas

get-partner-instance · get-partner-instance-history

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Instância desconectada ou encerrada
instance.canceled

Notifica o encerramento técnico. Conclusões confirmadas de desconexão trazem a evidência adicional abaixo.

Quando chega

Depois da conclusão da desconexão pela HookCloud, iniciada pelo Portal, API ou Admin. O evento e o sininho são gravados na mesma transação. proof_source=phone_callback_removed_local_access_disabled + remote_cleanup_scope=hookcloud_integration confirma callback removido e credenciais locais desativadas, sem exigir revogação de uma autorização compartilhada. grant_revoked_readback + app_authorization permanece como prova distinta de revogação efetiva. Eventos legados sem prova não autorizam inventar limpeza remota.

Como identificar o cliente e o número

Envelope event/data identificado por partner_id e instance_id.

Exemplo ilustrativo do POST recebido

json
{
  "id": "44444444-4444-4444-8444-444444444444",
  "event_id": "44444444-4444-4444-8444-444444444444",
  "event": "instance.canceled",
  "created_at": "2026-10-02T15:10:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "external_customer_id": "cliente-001",
  "data": {
    "status": "canceled",
    "reason": "Cancelamento solicitado",
    "deactivation_status": "completed",
    "operation_id": "44444444-4444-4444-8444-444444444444",
    "operation_kind": "disconnect",
    "connection_generation": "55555555-5555-4555-8555-555555555555",
    "actor_source": "partner_api_key",
    "occupies_slot": false,
    "slot_state": "released",
    "remote_callback_status": "removed",
    "remote_cleanup_confirmed": false,
    "remote_cleanup_required": true,
    "local_authorization": "disabled",
    "remote_authorization": "preserved_shared",
    "reconnect_required": true,
    "completed_at": "2026-10-02T15:10:00Z",
    "technical_disconnect_confirmed": true,
    "proof_source": "phone_callback_removed_local_access_disabled",
    "remote_cleanup_scope": "hookcloud_integration",
    "commercial_relationship": "unchanged",
    "phone_deregister_status": "not_attempted"
  },
  "operation_id": "44444444-4444-4444-8444-444444444444",
  "connection_generation": "55555555-5555-4555-8555-555555555555"
}

Significado dos campos do payload

CampoTipoSignificado e como usar
event_id / id / event / created_atUUID / string / dataPrefira event_id para deduplicar; event seleciona instance.canceled.
partner_id / instance_id / external_customer_idUUID / stringIdentificam a conexão e seu cliente.
data.status / reasonstring / string ou nullEstado canceled e motivo. Não é o status da assinatura Stripe do parceiro e não comprova por si só a limpeza remota.
operation_id / connection_generationUUIDIdentificam a conclusão e a geração da conexão. Ignore uma conclusão de geração antiga depois de reconectar. event_id permanece estável na repetição da entrega.
data.remote_cleanup_confirmed / remote_cleanup_requiredbooleanO contrato legado confirmed=true exige callback e deregistro. Em hookcloud_integration e app_authorization, confirmed=false é intencional: use technical_disconnect_confirmed, proof_source e remote_cleanup_scope para distinguir desativação local de revogação efetiva. Não invente deregistro nem retirada comercial.
data.occupies_slot / slot_state / completed_atboolean / string / datafalse e released descrevem a vaga liberada na conclusão, com data. Releia o estado atual se a conexão já tiver outra geração.
data.operation_kind / actor_sourcestringdisconnect ou billing_suspend; origem autenticada partner_api_key, partner_member_jwt, admin_jwt, system ou billing_worker. Origem não altera a validação.
data.local_authorization / remote_authorization / reconnect_requiredstring / string / booleandisabled impede uso da instância pela HookCloud. preserved_shared preserva a autorização Meta para outros números; unknown não confirma revogação. revoked exige grant_revoked_readback. Não invalide outras conexões ao receber este evento.
data.technical_disconnect_confirmed / proof_source / remote_cleanup_scopeboolean / stringtrue + phone_callback_removed_local_access_disabled + hookcloud_integration confirma a retirada da rota e desativação local. true + grant_revoked_readback + app_authorization confirma revogação efetiva. Ambas exigem a geração exata; não provam exclusão da conta nem retirada comercial.
data.commercial_relationship / phone_deregister_statusstringunchanged preserva a relação comercial. not_attempted informa que o número coexistente não foi deregistrado pela API. A empresa pode continuar listada como parceira no portfólio Meta.

O que atualizar no seu sistema

  • Atualize a instância da mesma geração para Desconectado, guarde event_id e data da conclusão, e desative indicadores atuais de envio. Preserve informações Meta antigas como histórico.
  • Se faltarem os campos de confirmação, consulte get-partner-instance: status canceled legado por si só não é prova remota.
  • Não repita a desconexão ao receber o webhook. Retentativas de entrega são deduplicadas por event_id e X-HookCloud-Delivery.
  • Na prova grant_revoked_readback, confira a mesma geração, status=canceled, occupies_slot=false e slot_state=released. Não espere remote_cleanup_confirmed=true, nem esconda uma parceria comercial somente porque a desconexão técnica terminou.
  • Com proof_source=phone_callback_removed_local_access_disabled, mostre Desconectado da HookCloud. Se remote_authorization=preserved_shared, explique: Credenciais desativadas na HookCloud; autorização Meta compartilhada preservada para manter outros números.

Chamadas relacionadas

get-partner-instance · get-partner-instance-history

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Erro no estado técnico da instância
instance.error

Avisa que uma alteração colocou a instância em estado de erro.

Quando chega

Quando o fluxo de status publica error.

Como identificar o cliente e o número

Envelope event/data; não trate erro genérico como BAN ou como ausência de cartão.

Exemplo ilustrativo do POST recebido

json
{
  "id": "11111111-1111-4111-8111-111111111128",
  "event_id": "11111111-1111-4111-8111-111111111129",
  "event": "instance.error",
  "created_at": "2026-10-02T15:11:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "external_customer_id": "cliente-001",
  "data": {
    "status": "error",
    "reason": "Operação precisa de conferência"
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
event_id / id / event / created_atUUID / string / dataPrefira event_id para deduplicar; event seleciona instance.error.
partner_id / instance_id / external_customer_idUUID / stringIdentificam a conexão e seu cliente.
data.status / reasonstring / string ou nullEstado error e explicação, quando disponível. Reconcilie issues, evidence e operation para orientar a ação correta.

O que atualizar no seu sistema

  • Mostre atenção no número e consulte o diagnóstico; não repita automaticamente conexão, desconexão ou cobrança.

Chamadas relacionadas

get-partner-instance · get-partner-instance-history

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Identidade Meta substituída com confirmação
instance.identity_replaced

Confirma o novo vínculo Meta da mesma instância lógica, preservando a instance_key e arquivando a identidade anterior.

Quando chega

Após confirmar novos IDs Meta na reconexão normal do mesmo número, com nova autorização, consulta à Graph e vínculo anterior encerrado, ou após uma sessão avançada mode=replace autorizada. O novo snapshot, o histórico, o evento e o aviso no sininho são gravados juntos. Gerar o link ou selecionar um telefone igual não confirma substituição.

Como identificar o cliente e o número

Envelope event/data com partner_id, customer_id, external_customer_id, instance_id e instance_key. connection_generation na raiz é a geração sucessora; data.replacement.connection_generation identifica a anterior.

Exemplo ilustrativo do POST recebido

json
{
  "id": "44444444-4444-4444-8444-444444444444",
  "event_id": "44444444-4444-4444-8444-444444444444",
  "schema_version": "partner-instance-v2",
  "created_at": "2026-10-08T15:00:00Z",
  "partner_id": "22222222-2222-4222-8222-222222222222",
  "customer_id": "11111111-1111-4111-8111-111111111111",
  "external_customer_id": "cliente-001",
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "instance_key": "hc_inst_exemplo",
  "event": "instance.identity_replaced",
  "operation_id": "77777777-7777-4777-8777-777777777777",
  "connection_generation": "66666666-6666-4666-8666-666666666666",
  "data": {
    "status": "connected",
    "replacement": {
      "id": "44444444-4444-4444-8444-444444444444",
      "status": "superseded",
      "subtype": "meta_identity_replacement",
      "connection_generation": "55555555-5555-4555-8555-555555555555",
      "successor_generation": "66666666-6666-4666-8666-666666666666",
      "phone_number_id": "100000000000001",
      "waba_id": "100000000000002",
      "business_id": "100000000000004",
      "display_phone_number": "+55 00 00000-0000",
      "verified_name": "Empresa de exemplo",
      "connected_at": "2026-10-01T15:00:00Z",
      "superseded_at": "2026-10-08T15:00:00Z",
      "reason": "explicit_meta_identity_replacement"
    },
    "phone_number_id": "100000000000003",
    "waba_id": "100000000000002",
    "instance_key_preserved": true
  }
}

Significado dos campos do payload

CampoTipoSignificado e como usar
id / event_id / event / schema_versionUUID / stringevent_id deduplica; event=instance.identity_replaced e schema_version=partner-instance-v2 selecionam este contrato.
partner_id / customer_id / external_customer_id / instance_id / instance_keyUUID / string / nullIdentificam o parceiro, o cliente e a mesma instância lógica. Não crie uma segunda instância pelo simples recebimento do evento.
operation_id / connection_generationUUIDOperação de conexão concluída e sua geração sucessora. UUIDs são opacos; não ordene lexicalmente para decidir qual é atual.
data.status / phone_number_id / waba_idstringEstado técnico e IDs da identidade nova. connected não garante envio disponível; consulte a saúde atual.
data.instance_key_preservedbooleantrue confirma que a chave lógica da instância foi preservada.
data.replacementobjectRetrato da identidade anterior, com o mesmo formato de cada item de connection_history. Não é a identidade de envio atual.
data.replacement.id / status / subtype / reasonUUID / stringID da substituição, status=superseded e subtype=meta_identity_replacement. reason=automatic_same_phone_identity_replacement identifica atualização automática dos IDs do mesmo número; explicit_meta_identity_replacement identifica o modo avançado legado.
data.replacement.connection_generation / successor_generationUUIDGerações anterior e sucessora. A sucessora corresponde à connection_generation na raiz do evento.
data.replacement.phone_number_id / waba_id / business_id / display_phone_number / verified_namestring ou nullIDs e rótulos da identidade anterior; nunca use o telefone exibido como prova de substituição.
data.replacement.connected_at / superseded_atdata ISO 8601 ou null / data ISO 8601Momento confirmado do vínculo anterior, quando disponível, e momento da substituição. connected_at não é a primeira conexão de toda a vida do número.

O que atualizar no seu sistema

  • Deduplicate pelo event_id e reconcilie a mesma instância no GET local; atualize seus IDs Meta atuais somente após conferir a geração.
  • Exiba a identidade anterior em Substituídos, com o subtipo no card e sem ações de envio, reconexão ou desconexão. Não some esse histórico à capacidade nem aos números ativos.
  • Se o evento chegar depois de outra reconexão, não restaure a geração antiga. O GET é a autoridade para a identidade atual. Não reinicie o fluxo Embedded automaticamente.

Chamadas relacionadas

get-partner-instance · list-partner-instances · create-connect-session

Os GETs consultam o cache da HookCloud. refresh-partner-instance-meta, quando listado, solicita uma conferência explícita à Meta; create-connect-session gera outra sessão temporária. Não são consultas automáticas para executar a cada evento.

Evento de template

O contrato completo, o exemplo atualizado e o significado dos campos estão em hookcloud.meta.template.updated. O evento usa type e data; não garante um campo previous_status.

Troca segura da URL

  1. Implemente GET e POST na URL nova.
  2. Teste com validate-partner-webhook-endpoint.
  3. Aplique com update-partner-webhook-endpoint.
  4. Acompanhe a migração.
  5. Reprocesse somente as falhas.

Boas práticas de produção

  • Responder HTTP 2xx em poucos segundos
  • Enfileirar processamento pesado
  • Deduplicar mensagens pelo wamid; eventos HookCloud por event_id ou id e, quando ausentes, pelo header de entrega
  • Não seguir redirects no seu validador
  • Registrar falhas sem conteúdo sensível
  • Monitorar taxa de HTTP 4xx/5xx
  • Rotacionar segredos com processo controlado
Esta página ajudou?Use o Partner Portal para suporte e compartilhe o link desta seção.