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
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 →
// 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.
| Header | Descrição |
|---|---|
X-HookCloud-Event | Tipo do evento. Após validar a assinatura, escolha o contrato correspondente; alguns eventos antigos não repetem o tipo no corpo. |
X-HookCloud-Delivery | ID da entrega para um endpoint; permanece o mesmo nas retentativas. |
X-HookCloud-Timestamp | Unix timestamp em segundos, gerado na tentativa de envio. |
X-HookCloud-Signature | sha256=HMAC_SHA256(webhook_secret, timestamp.rawBody), em hexadecimal. |
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 recebida | Origem | O que fazer |
|---|---|---|
Mensagens e status originais em object/entry/changes | Meta; assinatura X-Hub-Signature-256 | Trate 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álogo | HookCloud; assinatura X-HookCloud-Signature | Atualize 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 consultas | Resposta HTTP da Partner API à sua chamada GET | ok 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 - Qualidade, nome aprovado e portfólio do número
hookcloud.instance.updated - BAN, RED e restrição de novas conexões do cliente
hookcloud.customer.risk_updated - Aprovação, qualidade e conteúdo do template
hookcloud.meta.template.updated - Preferência de marketing do usuário
hookcloud.meta.user_preferences.updated - Instância criada e link de conexão inicial
instance.created - Novo link temporário de conexão
connect_session.created - Autorização da conexão concluída
instance.connected - Instância pausada
instance.paused - Instância inativa
instance.inactive - Instância desconectada ou encerrada
instance.canceled - Erro no estado técnico da instância
instance.error - Identidade Meta substituída com confirmação
instance.identity_replaced - Vínculo removido na Meta
instance.meta_link_removed
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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
id / event_id | UUID | Neste evento os dois identificam o mesmo fato. Guarde event_id para deduplicar; não o use como instance_id. |
event / schema_version | string | event 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_id | UUID | Identificam 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_generation | UUID | Identifica 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_at | data ISO 8601 | created_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_operate | enum / boolean | state: 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_status | enum | Estado 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_status | enum / enum | authorization_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_state | enum / enum | inventory_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 / reason | enum / string ou null | state: 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.status | enum | confirmed: 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_presence | enum | identified: 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_source | array de números / string ou null | issue_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_reason | datas ou null / boolean / string ou null | Datas 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 / source | enum / string ou null | verified, 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_reason | datas ou null / boolean / string ou null | Informam 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.evidence | objeto | schema_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 objetos | Cada 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_actions | array de strings | Açõ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.operation | objeto ou null | Quando 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_status | enum / enum | observation_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_profile | objeto | Observaçõ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 / tier | enum / string ou null | status=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 / source | string ou null | business_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_status | enum / string ou null | status=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_reason | data ISO ou null / string ou null / boolean | Cada 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
id / event_id / event / schema_version | UUID / string | event_id identifica o fato para deduplicação. event é hookcloud.instance.updated e schema_version é partner-instance-v2. |
partner_id / customer_id / instance_id / instance_key | UUID / string | Ví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_generation | data ISO / UUID | Criaçã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_status | string ou null | instance_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_rating | string ou null | Qualidade 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_name | string ou null | ID 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_id | string ou null | Identificadores 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
id / event_id / event / type / created_at | UUID / string / data | event_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_id | UUID | Identificam 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_id | string / UUID | Contrato 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 / limits | inteiros / objeto | Contagens 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 / reasons | boolean / enum / array | block_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_at | strings / inteiro / datas | Descrevem 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 / timezone | datas ou null / inteiro / string | Pró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_preserved | boolean | true 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_reason | data / boolean / string | Informam 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
id / type / created_at / partner_id | UUID / string / data | id 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 / language | string ou null | Conta 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 / category | string ou null | Status 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_change | objeto ou null | Quando 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_risky | booleans / string ou null | sendable é 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_schema | string ou null / array / objeto | Motivo 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 / components | inteiro / string / arrays | total_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 / source | data 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
id / type / created_at / partner_id | UUID / string / data | id 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_id | string / UUID | Delimitam a WABA, cliente e número da preferência; use esse escopo junto à identidade do usuário. |
data.user_preferences[].user_id / wa_id | string opcional | Pode 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 / timestamp | string | category é 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
event_id / id / event / created_at | UUID / string / data | event_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_id | UUID / string | Relacionam o parceiro, a nova instância e o ID estável do cliente no seu sistema. |
data.status / instance_key / instance_name | string | pending_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_url | URL sensível temporária ou null | URL 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
event_id / id / event / created_at / partner_id / instance_id | UUID / string / data | event_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_id | UUID | ID da tentativa temporária, diferente de instance_id. Uma instância pode ter novas sessões ao longo do tempo. |
data.connect_url / expires_at | URL sensível / data ISO | Link 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
event_id / id / event / created_at / partner_id / instance_id / external_customer_id | UUID / string / data | event_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.status | string | connected 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_operate | objeto / boolean, quando presentes | No 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_number | string ou null, quando presentes | Portfó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_status | string ou null, quando presentes | Dados 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_error | boolean / strings ou null, quando presentes | Inscriçã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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
event_id / id / event / created_at | UUID / string / data | Prefira event_id na deduplicação; event seleciona instance.paused. |
partner_id / instance_id / external_customer_id | UUID / string | Identidade da conexão e vínculo do cliente no seu sistema. |
data.status / reason | string / string ou null | Estado 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
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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
event_id / id / event / created_at | UUID / string / data | Prefira event_id para deduplicar; event seleciona instance.inactive. |
partner_id / instance_id / external_customer_id | UUID / string | Identificam a conexão e seu cliente. |
data.status / reason | string / string ou null | Estado 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
event_id / id / event / created_at | UUID / string / data | Prefira event_id para deduplicar; event seleciona instance.canceled. |
partner_id / instance_id / external_customer_id | UUID / string | Identificam a conexão e seu cliente. |
data.status / reason | string / string ou null | Estado 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_generation | UUID | Identificam 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_required | boolean | O 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_at | boolean / string / data | false 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_source | string | disconnect 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_required | string / string / boolean | disabled 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_scope | boolean / string | true + 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_status | string | unchanged 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
event_id / id / event / created_at | UUID / string / data | Prefira event_id para deduplicar; event seleciona instance.error. |
partner_id / instance_id / external_customer_id | UUID / string | Identificam a conexão e seu cliente. |
data.status / reason | string / string ou null | Estado 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
{
"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
| Campo | Tipo | Significado e como usar |
|---|---|---|
id / event_id / event / schema_version | UUID / string | event_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_key | UUID / string / null | Identificam 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_generation | UUID | Operaçã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_id | string | Estado técnico e IDs da identidade nova. connected não garante envio disponível; consulte a saúde atual. |
data.instance_key_preserved | boolean | true confirma que a chave lógica da instância foi preservada. |
data.replacement | object | Retrato 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 / reason | UUID / string | ID 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_generation | UUID | Geraçõ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_name | string ou null | IDs e rótulos da identidade anterior; nunca use o telefone exibido como prova de substituição. |
data.replacement.connected_at / superseded_at | data ISO 8601 ou null / data ISO 8601 | Momento 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.
Vínculo removido na Meta
instance.meta_link_removed
Confirma perda do vínculo da identidade atual, desabilita a autorização local e informa a liberação da vaga sem inventar uma limpeza remota.
Quando chega
Após evento account_update assinado e validado de remoção de parceiro, desinstalação do app ou exclusão da WABA, com escopo do app/parceiro e da WABA/número e data compatíveis com a geração atual. Erro Graph de objeto inacessível ou token inválido não produz este evento. Na coexistência, PARTNER_REMOVED confirmado para a solicitação de desconexão encerra a espera pelo titular: o campo operation pode desaparecer do GET porque foi arquivado. Confirme meta_link.source=signed_meta_webhook e connection_generation correspondente, status=canceled, occupies_slot=false e slot_state=released. Não espere remote_cleanup_confirmed=true: o evento confirma a desvinculação no aplicativo, não a execução de /deregister.
Como identificar o cliente e o número
Envelope event/data com instance_key e connection_generation na raiz. data.meta_link_state contém o vínculo confirmado; o GET da instância o apresenta resumido no campo meta_link.
Exemplo ilustrativo do POST recebido
{
"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.meta_link_removed",
"connection_generation": "55555555-5555-4555-8555-555555555555",
"data": {
"status": "canceled",
"meta_link_state": {
"state": "removed",
"reason": "app_uninstalled",
"confirmed_at": "2026-10-08T14:50:00Z",
"connection_generation": "55555555-5555-4555-8555-555555555555",
"evidence_source": "signed_meta_webhook",
"source_event_id": "88888888-8888-4888-8888-888888888888",
"meta_waba_id": "100000000000002",
"meta_phone_number_id": "100000000000001",
"app_id": "100000000000005",
"provider_business_id": null
},
"occupies_slot": false,
"slot_state": "released",
"capacity_release_reason": "signed_meta_link_removal",
"local_authorization": "disabled",
"remote_authorization": "unknown",
"remote_callback_status": "unavailable",
"remote_cleanup_confirmed": false,
"reconnect_required": true
}
}Significado dos campos do payload
| Campo | Tipo | Significado e como usar |
|---|---|---|
id / event_id / event / schema_version | UUID / string | Identificador estável para deduplicar; event=instance.meta_link_removed e schema_version=partner-instance-v2. |
partner_id / customer_id / external_customer_id / instance_id / instance_key / connection_generation | UUID / string / null | Identificam a instância, seu cliente e a geração encerrada. Um evento antigo não deve desconectar uma geração sucessora. |
data.status / meta_link_state.state / meta_link_state.reason | string | canceled, removed e um motivo confirmado: partner_removed, app_uninstalled ou waba_deleted. Pagamento ou RED não são motivos de remoção. |
data.meta_link_state.confirmed_at / connection_generation / evidence_source | data / UUID / string | Data do fato Meta, geração correspondente e signed_meta_webhook. No GET, a projeção usa meta_link.source, sem expor todo o registro interno. |
data.meta_link_state.source_event_id / meta_waba_id / meta_phone_number_id / app_id / provider_business_id | UUID / string / null | Referências da evidência assinada e do escopo validado. provider_business_id pode ser null; sua ausência neste caso não confirma perda de outro portfólio. |
data.occupies_slot / slot_state / capacity_release_reason | boolean / string | false, released e signed_meta_link_removal indicam a liberação local da vaga daquele vínculo, sem garantir a disponibilidade atual após operações posteriores. |
data.local_authorization / remote_authorization / remote_callback_status / remote_cleanup_confirmed | string / boolean | disabled, unknown, unavailable e false. A HookCloud não executou nem comprovou remoção do callback ou revogação do token global da WABA por esse evento. |
data.reconnect_required | boolean | true: retornar à operação exige nova jornada autorizada, com cobrança, risco e capacidade verificados. |
O que atualizar no seu sistema
- Na geração correspondente, apresente Desconectado — Vínculo removido na Meta, desative os sinais de envio e preserve os dados antigos como histórico. O sininho da HookCloud também recebe o aviso.
- Deduplicate e reconcilie por get-partner-instance. Não dispare remoção de callback, deregistro, nova conexão ou novo envio automaticamente em resposta ao evento.
- Se o GET mostrar meta_link.state=access_unconfirmed, apresente a falta de confirmação com cautela. Não converta falha de acesso em remoção, banimento ou liberação de vaga por conta própria.
- Quando o titular concluir no aplicativo, use instance.meta_link_removed para atualizar o cache e o painel. O HTTP 202 anterior tinha disconnect_status=awaiting_user, action_required=disconnect_in_whatsapp_business_app e meta_disconnect.status=action_required; status ainda refletia a conexão real. Não envie novas chamadas de desconexão para tentar acelerar a confirmação.
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
- Implemente GET e POST na URL nova.
- Teste com
validate-partner-webhook-endpoint. - Aplique com
update-partner-webhook-endpoint. - Acompanhe a migração.
- 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
