# Guia de implementação — HookCloud Partner

**Versão do kit: 20 de outubro de 2026.** Guia para o assinante que integra a HookCloud ao seu próprio CRM, central de atendimento, chatbot, aplicação de disparos ou automação.

Este documento pode ser anexado a uma IA ou entregue à equipe de desenvolvimento. Os exemplos usam identificadores fictícios e marcadores; não contêm credenciais. A referência online da API continua sendo o contrato detalhado e deve ser consultada quando houver diferença de versão.

## 1. O que cada parte faz

| Parte | Responsabilidade |
|---|---|
| HookCloud Partner | Clientes, instâncias, autorização Connect, estado e saúde da conexão, capacidade, desligamento e acesso autorizado à credencial Meta. |
| Meta / WhatsApp Business Platform | Aprovação do número e do nome, políticas, templates, mensagens, qualidade, limites, cobrança Meta e eventos originais do WhatsApp. |
| Sistema do assinante | Login dos usuários, isolamento dos clientes, contatos, conversas, mensagens, bot/IA, filas de envio, campanhas, interface, retenção e política comercial própria. |

A Partner API não é um sistema de conversas completo. Criar uma instância não cria automaticamente uma caixa de atendimento no seu banco. Um chatbot que responde por outro serviço também não faz o histórico aparecer no seu painel: o sistema precisa persistir mensagens recebidas, suas próprias mensagens enviadas e os status.

Os identificadores `meta-send-text`, `meta-create-template` e semelhantes no catálogo são **nomes de exemplos Graph**, não rotas disponíveis em `?route=` na HookCloud.

Referências de entrada:

- [Documentação do desenvolvedor HookCloud](https://developers.hookcloud.app/)
- [Referência da Partner API](https://partner-portal.hookcloud.app/docs/api-reference.html)
- [Documentação oficial WhatsApp Business Platform](https://developers.facebook.com/documentation/business-messaging/whatsapp/)

## 2. Arquitetura mínima

Use um backend autenticado entre o navegador e os serviços externos. Ele deve:

1. Resolver a empresa/cliente do usuário pelo login, sem confiar em `tenant_id` ou `partner_id` enviados pelo navegador.
2. Validar a permissão para a ação e a propriedade da instância.
3. Consultar o estado persistido, criar uma intenção durável quando houver escrita externa e executar a chamada apropriada.
4. Persistir resultado, identificadores e diagnóstico seguro.
5. Retornar ao frontend apenas os campos necessários.

O receptor webhook deve aceitar eventos independentemente de haver alguém com a tela aberta. Uma fila ou tarefa de servidor precisa continuar após o usuário fechar o navegador. Não use apenas timers de frontend ou promises em segundo plano sem garantia de execução.

Para um **CRM**, priorize cadastro, vínculos, conexão e histórico. Para **atendimento**, acrescente inbox, responsáveis, não lidas, transferência ao humano e estados de entrega. Para **bot/IA**, acrescente pausa por conversa, prevenção de loops e decisão antes de enviar. Para **disparos**, acrescente opt-in, seleção de destinatários, campanha persistida, limites e cancelamento de itens ainda não enviados.

## 3. Credenciais e ambientes

| Credencial | Uso correto |
|---|---|
| Partner API Key | Bearer das chamadas autorizadas à HookCloud. O escopo `*.write` não implica automaticamente `*.read`. |
| JWT do Portal | Sessão de um usuário do Portal. Não é substituto automático da API Key e não deve ser copiado do navegador para integrações. |
| Token Meta | Bearer das chamadas diretas à Graph, obtido para a instância autorizada. |
| Token de verificação GET | Compara `hub.verify_token` no desafio de validação do webhook. Não autentica eventos POST. |
| Segredo HMAC do endpoint HookCloud | Verifica eventos normalizados HookCloud. É diferente de todos os anteriores. |
| Token `hc_connect_…` | Capacidade temporária de uma sessão Connect. Não é uma API Key nem autorização permanente do número. |

Guarde segredos no gerenciador do backend, com criptografia quando persistidos. Não use variáveis públicas de frontend, código distribuído ao navegador, analytics, URLs de consulta ou logs com tokens. Restrinja revelação de credenciais a rotas internas e operadores autorizados.

A base pública de produção é:

```text
https://api.hookcloud.app/functions/v1/swift-worker
```

Uma chamada de consulta tem a forma:

```http
GET /functions/v1/swift-worker?route=get-partner-instance&instance_id=INSTANCE_ID
Authorization: Bearer PARTNER_API_KEY_DO_BACKEND
Accept: application/json
```

`INSTANCE_ID` e `PARTNER_API_KEY_DO_BACKEND` são marcadores, não valores para copiar. Use o ambiente disponibilizado para sua conta; não suponha que a mesma chave, cliente ou instância exista na homologação. Defina URLs de retorno próprias por ambiente. Nunca siga redirecionamento HTTP para outro domínio carregando Bearer.

## 4. Onboarding do parceiro e pré-requisitos

O proprietário contrata o pacote pelo checkout, informa cartão e recebe o acesso conforme a confirmação do fluxo de cobrança. Uma página de “sucesso” no navegador não substitui a confirmação do pagamento/trial no servidor.

No Portal, o proprietário conclui o cadastro obrigatório:

- Pessoa física: nome completo, celular e CPF.
- Pessoa jurídica: número do CNPJ e dados empresariais solicitados no formulário.

A validação local do formato e dos dígitos verificadores **não comprova identidade nem situação cadastral na Receita**. Não prometa consulta cadastral oficial quando ela não estiver contratada. Esse cadastro pertence ao proprietário do Partner; não envie esses documentos em `upsert-partner-customer`.

Depois, o parceiro cadastra o webhook global. O sistema só libera as operações dependentes quando o GET estiver validado **e a configuração tiver sido salva**. Ter uma URL preenchida ou executar uma validação avulsa não comprova onboarding concluído.

Consulte `get-partner-onboarding-status` com `webhook.read`. Se houver impedimento, apresente a etapa concreta: cadastro, configuração do webhook ou cobrança. `billing_sync_required` pede reconciliação pelo Portal; não confirma cartão recusado nem banimento Meta.

A cobrança do parceiro com a HookCloud e a cobrança do cliente final com seu SaaS são contratos distintos. Seu sistema controla sua assinatura e, após um cancelamento confirmado e autorizado, solicita o desligamento do cliente correspondente na HookCloud.

## 5. Webhook global: GET e POST

### 5.1 Desafio GET

Publique uma rota HTTPS, por exemplo `https://sistema.example.com/webhooks/whatsapp`, que compare o token configurado no servidor e devolva apenas o challenge:

```javascript
app.get('/webhooks/whatsapp', (req, res) => {
  const mode = req.query['hub.mode'];
  const token = req.query['hub.verify_token'];
  const challenge = req.query['hub.challenge'];

  if (
    mode === 'subscribe' &&
    typeof token === 'string' &&
    token === process.env.META_VERIFY_TOKEN &&
    typeof challenge === 'string'
  ) {
    return res.status(200).type('text/plain').send(challenge);
  }
  return res.sendStatus(403);
});
```

Esse é um exemplo adaptado para Express. A [documentação oficial de configuração de webhooks](https://developers.facebook.com/docs/graph-api/webhooks/getting-started/) descreve o desafio; não é necessário que a página oficial tenha exatamente o mesmo código.

A validação da HookCloud usa `hub.challenge=1234`. Não fixe a resposta em `1234`: devolva o challenge recebido após conferir token e modo. A Meta pode enviar outro valor.

- `validate-partner-webhook-endpoint`: testa a URL e o token.
- `update-partner-webhook-endpoint`: salva a configuração; envie `webhook_url`, `meta_verify_token` e um `reason` explicativo.
- `get-partner-webhook-endpoint`: consulta o estado salvo.
- `get-partner-webhook-migration`: acompanha mudanças de callback já solicitadas.

Uma troca de webhook pode exigir migração das instâncias existentes. Acompanhe o resultado; não presuma que salvar a URL trocou todos os destinos instantaneamente.

### 5.2 Autenticação do POST

O mesmo endereço pode receber duas origens, com contratos distintos:

| Origem | Corpo típico | Autenticação |
|---|---|---|
| Meta | `object`, `entry[]`, `changes[]` | `X-Hub-Signature-256`, calculada com o App Secret do app emissor. |
| HookCloud | Eventos de conexão, saúde, risco, clientes ou templates | `X-HookCloud-Event`, `X-HookCloud-Delivery`, `X-HookCloud-Timestamp` e `X-HookCloud-Signature`, conforme o contrato do endpoint. |

O App Secret do provedor não é fornecido por `reveal-partner-instance-meta-token`. O token Meta também não permite calcular essa assinatura. **Não substitua o segredo por verify token, Partner API Key ou token de acesso.**

Para eventos HookCloud, o cálculo é HMAC SHA-256 sobre `timestamp + "." + corpo_bruto`, usando o segredo do endpoint. Confira assinatura em tempo constante e uma tolerância de timestamp. Só depois interprete o JSON.

**Pré-requisito de implantação:** confirme com a HookCloud como a autenticação dos POSTs será provisionada para sua integração. O GET de consulta e a atualização comum de endpoint não são uma API de obtenção de App Secret/segredo HMAC. Se o segredo ou um mecanismo autenticado de entrega ainda não estiver disponível, configure o receptor para falhar de forma segura e deixe essa etapa pendente. Não publique um receptor que aceita qualquer POST para “fazer funcionar”. Também não copie um relay privado de outro produto.

Os eventos originais da Meta podem chegar diretamente ao callback do número; não suponha que todos serão reenviados com uma assinatura HookCloud. Um endpoint secundário de aplicação exige provisionamento e autenticação compatíveis; não use sua criação como atalho para resolver a validação do POST.

### 5.3 Persistência e confirmação

Antes do HTTP 2xx:

1. Valide assinatura, tamanho do corpo e formato.
2. Localize o vínculo autorizado pelos IDs do evento.
3. Grave o evento ou uma tarefa durável, com chave única.
4. Só então confirme o recebimento.

Não aguarde uma resposta da IA, disparo ou download demorado para confirmar o webhook. O worker posterior processa a fila. Em indisponibilidade de persistência, devolva falha para permitir a política de reentrega, em vez de descartar silenciosamente.

Eventos podem repetir, chegar fora de ordem ou esgotar tentativas. A Meta e a HookCloud têm políticas de reentrega próprias; sua aplicação deve ter reconciliação e fila de falhas.

## 6. Modelo de dados e isolamento

Mantenha entidades separadas:

| Entidade | Identidade e dados essenciais |
|---|---|
| Cliente do SaaS | ID local, `partner_id`, `partner_customer_id`, `external_customer_id`, status cadastral. |
| Instância | ID local, cliente proprietário, `instance_id`, `instance_key`, IDs Meta, `connection_generation`, status e datas. |
| Contato WhatsApp | ID local, contexto empresarial, telefone quando disponível, BSUID completo e origem da evidência. |
| Conversa | Cliente/instância/contato, responsável, estado, não lidas, última mensagem recebida e pausa do bot. |
| Mensagem | Direção, conteúdo permitido, `wamid`, estado, timestamps, erro seguro, instância/geração de envio. |
| Tentativa de envio | ID do pedido local, hash do conteúdo, estado, claim/lease, correlação e resultado. |
| Campanha | Seleção congelada de destinatários, consentimento, conteúdo, itens, progresso e cancelamento. |
| Template | WABA, ID, nome, idioma, categoria, componentes, aprovação, qualidade, datas. |
| Evento recebido | Origem autenticada, chave de deduplicação, corpo permitido, horário do fato e do recebimento. |

Crie índices únicos para identidades estáveis e operações idempotentes. Não mantenha histórico de mensagens crescente dentro de um único JSON do workspace. Use tabelas/coleções paginadas e controle de concorrência.

Cheque a propriedade em todas as leituras e escritas, inclusive anexos, busca, templates, campanhas e endpoints internos. Uma API Key de parceiro não dispensa o seu isolamento entre os clientes daquele parceiro.

O telefone visível pode mudar. `phone_number_id`, WABA, portfólio e `connection_generation` também podem mudar em uma nova autorização. Capture a identidade usada em cada envio; uma reconexão não deve reativar uma fila antiga para outro vínculo.

## 7. Clientes e instâncias

### 7.1 Fluxo recomendado de criação

Crie ou atualize o cliente:

```http
POST ?route=upsert-partner-customer
Content-Type: application/json
Authorization: Bearer PARTNER_API_KEY_DO_BACKEND
```

```json
{
  "external_customer_id": "crm-cliente-001",
  "customer_name": "Comércio Exemplo",
  "customer_email": "contato@example.com"
}
```

Guarde `partner_customer_id` e a chave externa. Omitir campos opcionais preserva seu valor; enviar `null` pode limpar o campo conforme o contrato. `metadata` é opcional e não seleciona ambiente de teste.

Depois crie o número para o cliente existente:

```http
POST ?route=create-customer-instance
Idempotency-Key: IDENTIFICADOR_UNICO_DESTA_CRIACAO
```

```json
{
  "external_customer_id": "crm-cliente-001",
  "instance_name": "WhatsApp principal",
  "create_connect_session": true,
  "ttl_hours": 24,
  "return_url": "https://sistema.example.com/conexoes"
}
```

**Não preencha uma nova `instance_key`.** A HookCloud gera a chave; guarde o valor retornado. O nome inicial é provisório e pode ser atualizado pelo nome aprovado na Meta.

`create-partner-instance` combina o cadastro/atualização do cliente com a criação da instância. É útil em uma jornada única sem cadastro prévio. Não é um segundo passo obrigatório após `create-customer-instance` nem deve ser chamado em sequência para o mesmo número. Para mudanças pontuais, use as rotas específicas.

### 7.2 Idempotência

Nas duas rotas de criação, use `Idempotency-Key` de 8 a 200 caracteres, estável para **aquela operação**, com o mesmo endpoint e o mesmo corpo. Guarde a intenção antes da chamada. Uma repetição legítima retorna o resultado original; corpo diferente pode retornar `409 idempotency_key_reused`.

Uma nova chave representa outra operação. Depois de timeout, trocar a chave ou usar a outra rota de criação pode duplicar uma instância. O replay não renova a expiração de um link.

Não assuma que a Graph ou todas as rotas HookCloud aceitam esse cabeçalho com o mesmo efeito. Mantenha idempotência local e conheça o contrato de cada escrita.

### 7.3 Inventário existente

Use `list-partner-customers` e `list-partner-instances`, com `page`/`page_size`; o limite aceito de página é 200. Percorra `total`/`pages` sem truncar silenciosamente. Se o inventário mudar entre páginas, reconcilie ou reinicie a leitura controladamente.

- Clientes usam `items[].id`; as instâncias listadas usam `items[].id` e `partner_customer_id`.
- Preserve IDs locais quando já houver `instance_id` ou um mapeamento confiável do cliente.
- Não agrupe por nome igual ou telefone parecido.
- Mantenha arquivados, inativos, mesclados e dados eliminados identificados como tais.
- `include_merged=true` e `include_erased=true` permitem uma reconciliação completa quando necessária; não restaure dados eliminados de cópias antigas.
- Valide todo o lote e salve com transação/controle de revisão. Importar não deve disparar `create-*`.

Consultas úteis: `get-partner-customer`, `get-partner-customer-history`, `get-partner-instance`, `get-partner-instance-history`. Atualizações usam `update-partner-customer` e `update-partner-instance-label`. Operações como mesclar clientes ou mover instâncias precisam de intenção explícita, IDs exatos e preservação de histórico.

## 8. Connect, retorno e reconexão

A resposta de criação pode fornecer `connect_url`, `connect_session_id` e `connect_expires_at`. Abra o link em uma ação do usuário. Trate-o como segredo temporário.

O padrão configurado da sessão tem fallback de 24 horas. `ttl_hours` pode ser explicitamente definido entre 1 minuto e 168 horas. O prazo confiável é o timestamp da resposta, não um contador fixo na interface.

Para link vencido, tentativa abandonada ou reconexão, chame `create-connect-session` usando a instância persistida:

```json
{
  "instance_id": "33333333-3333-4333-8333-333333333333",
  "mode": "reconnect",
  "ttl_hours": 24,
  "return_url": "https://sistema.example.com/conexoes"
}
```

Essa rota retorna `expires_at`. Não presuma idempotência de criação de sessão: após resposta ambígua, consulte o estado e as sessões conhecidas antes de repetir.

O retorno ao seu site, fechamento do popup ou `postMessage` isolado não autoriza marcar a conexão como ativa. A confirmação vem do backend HookCloud, por evento autenticado e consulta da instância. Use progresso, mensagens claras e uma opção de atualizar a sessão. Não encerre definitivamente o fluxo por um timeout visual curto enquanto a autorização continua na Meta.

Use a página Connect fornecida pela HookCloud. Não é necessário copiar o SDK Meta e reproduzir o Embedded Signup dentro do seu SaaS para esse fluxo. Rotas internas como `complete-meta-onboarding` não são um substituto para a experiência oficial do Connect.

Após nova autorização do mesmo número, a HookCloud pode atualizar os IDs Meta preservando `instance_id`/`instance_key`. `connection_generation` é um UUID opaco: compare igualdade, nunca “maior que”.

## 9. Saúde do número e WhatsApp Manager

Use os campos já calculados pela HookCloud:

- `connection_status`, `authorization_status`, `callback_status` e `inventory_state`.
- `meta_account_state`, `messaging_capability`, `business_verification` e `meta_payment`.
- `issues`, `allowed_actions`, `operation`, `evidence`, `observation_scope` e `panel_category`.
- `meta_profile.messaging_limit`, `meta_profile.username`, `checked_at`, `stale`, fonte e motivo de indisponibilidade.

`unknown` ou `null` não significa “não tem cartão”, “não é verificado” ou “está banido”. `no_issue_detected` significa ausência de pendência detectada naquela observação; não confirma um cartão específico. Nome aprovado e empresa verificada são fatos diferentes. Qualidade RED não é sinônimo de banimento.

O `get-partner-instance` fornece `whatsapp_manager_url`; use-o em um botão “Abrir WhatsApp Manager”. Valide origem HTTPS `business.facebook.com` antes de mostrar um link retornado ou construir um equivalente com IDs confirmados. Não acrescente tokens.

Mostre estado saudável em verde quando houver evidência atual; pendência em laranja; restrição confirmada conforme o diagnóstico; desconhecido/histórico em tom neutro. Exiba primeira conexão e última observação separadamente. Um número desligado não deve conservar checks verdes de evidências antigas.

Não faça nova consulta Graph para desenhar cada card. Os GETs da Partner API consultam o estado armazenado. `refresh-partner-instance-meta` é uma solicitação controlada de conferência, sujeita a cache, fila e recuo; não garante dado instantâneo.

## 10. Receber mensagens e eventos

Nos eventos Meta, percorra todos os `entry[]` e `changes[]`; não processe apenas o primeiro item.

- `metadata.phone_number_id` identifica o número comercial que recebeu o evento.
- `entry.id` identifica a WABA.
- `messages[].id` é o `wamid` recebido.
- `messages[].from_user_id` e `contacts[].user_id` podem fornecer BSUID.
- `statuses[].id` correlaciona a mensagem enviada; `recipient_user_id` pode identificar o destinatário.
- `smb_message_echoes` e histórico não devem disparar uma segunda resposta automática como se fossem uma mensagem nova do consumidor.

Só associe o evento a uma conversa depois de confirmar o vínculo da instância e do cliente. Durante uma mudança de callback, um evento de um vínculo conhecido ainda em ativação deve ser recuperável; não o confirme e descarte por falta temporária de configuração.

Nos eventos HookCloud, não existe um envelope único para todos os tipos. Alguns usam `event` na raiz, outros `type`/`data`, e eventos antigos de cadastro podem depender do cabeçalho autenticado.

Consulte os envelopes completos em `WEBHOOK-EVENTS.json`, incluído no kit, ou no [catálogo de eventos](https://developers.hookcloud.app/webhooks.html#event-catalog). `META-WEBHOOK-FIELDS.json` explica os campos originais da Meta, sua entrega e o tratamento atual no Partner. Consulte somente os eventos necessários por etapa para respeitar o limite de contexto da IA.

| Evento | Uso no SaaS |
|---|---|
| `instance.created` | Registrar criação; o link pode estar ausente no evento. |
| `hookcloud.instance.updated` | Atualizar dados cadastrais e observações do número. |
| `hookcloud.instance.health_updated` | Atualizar saúde, empresa, pagamento e perfil opcional. |
| `hookcloud.customer.risk_updated` | Mostrar restrições de novas conexões daquele cliente. |
| `instance.identity_replaced` | Atualizar IDs confirmados, preservando instância e histórico. |
| `instance.meta_link_removed` | Tratar remoção confirmada do vínculo na geração informada. |
| `hookcloud.meta.template.updated` | Atualizar aprovação, categoria, qualidade e conteúdo conforme o envelope. |
| `hookcloud.meta.user_preferences.updated` | Aplicar `marketing_messages=stop/resume` à identidade e ao número exatos. Não espalhar a preferência por toda a WABA nem interpretar retomada como consentimento para qualquer finalidade. |
| Eventos de cliente/movimentação | Reconciliar cadastro ou proprietário pelos IDs e pelo contrato específico. |

Deduplique preferencialmente por `event_id`, depois `id`; quando o evento antigo não tiver ID no corpo, use o identificador de entrega autenticado. Para mensagens Meta, use uma chave composta pela instância/número e `wamid`. Para status, inclua estado e timestamp do fato.

Não deixe `sent` atrasado substituir `read` ou `delivered`. Eventos de geração anterior não podem sobrescrever a conexão vigente. Em divergência, consulte o GET local da HookCloud, com cache e frequência controlada.

## 11. Enviar mensagens pela Graph

1. Confirme a instância conectada, a identidade e a permissão do usuário.
2. Obtenha a credencial no backend com `reveal-partner-instance-meta-token`, exigindo `instances.credentials.read`.
3. Confira se `instance_id`, número, WABA e contexto retornados correspondem ao envio.
4. Use `graph_version` e o token Meta na Graph, nunca a Partner API Key.
5. Guarde `messages[].id` e acompanhe a entrega pelo webhook.

Exemplo de texto para BSUID:

```http
POST https://graph.facebook.com/GRAPH_VERSION/PHONE_NUMBER_ID/messages
Authorization: Bearer META_ACCESS_TOKEN_DO_BACKEND
Content-Type: application/json
```

```json
{
  "messaging_product": "whatsapp",
  "recipient": "BR.BSUID_COMPLETO_RECEBIDO",
  "type": "text",
  "text": { "body": "Olá, recebemos sua solicitação." }
}
```

O BSUID é opaco e associado ao portfólio empresarial. Preserve prefixo, caracteres e origem; não derive do telefone. `user_id` é nome de campo no webhook, enquanto o envio usa `recipient`. Para telefone, use `to` com o formato aceito pela Meta. Não envie ambos, porque `to` tem precedência.

Username comercial reservado identifica a empresa, não um consumidor. Ele não substitui BSUID, telefone do destinatário ou `PHONE_NUMBER_ID`. A disponibilidade de username e certas modalidades de autenticação depende de regras específicas; não prometa que todo recurso Meta funciona com todo identificador.

Referências: [mensagens de texto](https://developers.facebook.com/documentation/business-messaging/whatsapp/messages/text-messages/) e [BSUID](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids/).

## 12. Janela de atendimento, FEP e cobrança

**A janela de 24 horas e o benefício FEP são controles diferentes.**

A mensagem recebida real do usuário abre/renova a janela de atendimento. Calcule o prazo a partir do timestamp validado desse evento, não de um clique no painel, envio do bot, criação do contato, eco ou status de entrega.

Antes de enviar texto ou mídia sem template, revalide a janela no backend. Fora dela, ofereça um template aprovado e mantenha o envio livre bloqueado. Uma campanha criada durante a janela pode chegar ao worker depois de expirar.

Na consulta oficial de 20/10/2026, a Meta descreve uma FEP de **até 7 dias**, condicionada à entrada elegível e à resposta da empresa conforme suas regras. Isso não autoriza texto livre por sete dias. Não converta automaticamente toda conversa originada de anúncio em uma FEP ativa; use evidências e validade compatíveis com a documentação/evento disponível. Sem evidência confiável, mostre “benefício não confirmado”.

Permissão para enviar e preço são dimensões separadas. Não rotule toda mensagem dentro de 24 horas como gratuita. Consulte as condições vigentes por categoria, mercado e eventuais franquias; evite valores monetários fixos no código ou no kit.

Fonte oficial verificada: [Preços e janelas da WhatsApp Business Platform](https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing/). A Meta pode atualizar condições; revalide antes de publicar mudanças comerciais.

## 13. Templates, arquivos e username

### Templates

O catálogo HookCloud oferece consulta de templates armazenados (`list-partner-meta-templates`, `get-partner-meta-template`) e sincronização controlada (`refresh-partner-meta-templates`, `get-partner-meta-template-sync`). Isso não equivale a CRUD Graph.

| Ação Graph | Recurso |
|---|---|
| Listar/criar | `GET` / `POST /{WABA_ID}/message_templates` |
| Consultar/editar | `GET` / `POST /{TEMPLATE_ID}` |
| Excluir uma tradução | `DELETE /{WABA_ID}/message_templates?name=…&hsm_id=…` |
| Enviar um template | `POST /{PHONE_NUMBER_ID}/messages` com `type=template` |

Use nome, idioma, categoria e componentes conforme o contrato vigente. Preencha exemplos de variáveis na criação. Na edição, preserve componentes não editados; não descarte botões, cabeçalhos ou estruturas desconhecidas silenciosamente.

Aguarde aprovação explícita da WABA/idioma exatos. HTTP 200 de criação/edição não significa template aprovado. Reclassificação para marketing não é automaticamente rejeição ou banimento. Para exclusão específica, não remova `hsm_id` por conveniência: isso pode ampliar o alcance às traduções do mesmo nome.

Uma edição/exclusão com resultado ambíguo deve ser reconciliada antes de repetir. Não envie um template apenas para testar aprovação sem autorização do destinatário e do operador.

### Arquivos

Upload usa `POST /{PHONE_NUMBER_ID}/media` com multipart e arquivo binário; não envie URL ou Base64 no campo `file` como se fossem bytes. O retorno é um `MEDIA_ID`, não um `wamid`. Envie a mensagem de mídia posteriormente por `/messages`.

O GET de mídia fornece metadados e URL temporária protegida. Faça o download pelo backend autorizado e entregue ao cliente usando o seu controle de acesso. Valide tamanho, MIME real e destino; proteja downloads contra acesso a redes internas e redirecionamento de credenciais.

Não confunda mídia de mensagem com handles de upload usados em cabeçalhos de templates. Formatos, limites e codecs são específicos; consulte a [documentação oficial de mídia](https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media/).

### Username

Use primeiro o perfil armazenado em `meta_profile.username`. Uma reserva não prova que o nome já está visível. Operações diretas de consulta/reserva/remoção só devem ser implementadas se fizerem parte do produto e estiverem disponíveis para o ativo. Não force a transferência de username de outro número para ocultar um conflito.

## 14. Inbox, bot e campanhas

O histórico deve refletir fatos:

- Mensagem recebida: gravada após evento autenticado.
- Mensagem enviada: gravada como tentativa antes da chamada; passa a aceita após resposta válida.
- `sent`, `delivered`, `read` e `failed`: atualizados pelo status correlacionado.
- Mensagem de outro sistema sem conteúdo no webhook: indique conteúdo não recebido; não invente texto.

Separe `queued`, `sending`, `accepted`, `sent`, `delivered`, `read`, `failed`, `uncertain` e `canceled`. Um HTTP 2xx sem identificador esperado ou um timeout após POST não prova envio nem falha definitiva.

Use ID de pedido e hash do conteúdo na sua fila, claim atômico e lease. A mesma intenção não pode ser enviada por dois workers. Após falha ambígua, preserve a tentativa e reconcilie; não gere automaticamente outra chave.

Para bot, responda só a inbound elegível, com ID determinístico baseado no evento. Não responda a ecos, histórico ou à própria mensagem. Uma transferência ao humano deve interromper respostas automáticas ainda pendentes. Textos do consumidor são dados, não instruções que autorizam acessar segredos ou operar outra empresa.

Para campanhas:

1. Exija seleção e confirmação de consentimento apropriado.
2. Capture os destinatários e o conteúdo da campanha em um conjunto estável.
3. Revalide consentimento, opt-out, instância, geração, template e janela na execução.
4. Processe lotes limitados, com progresso persistido e limite por número/destinatário.
5. Cancelar campanha impede itens pendentes; não desfaz mensagens já aceitas pela Meta.

Seu cliente cadastrado na HookCloud é a empresa usuária do SaaS; o destinatário de uma campanha é um contato WhatsApp. Não trate toda empresa importada como contato com consentimento de marketing.

## 15. Desconexão e cancelamento

`deactivate-partner-instance` encerra uma instância específica. Mostre “processando” enquanto a operação não tiver confirmação. Não marque sucesso apenas porque a requisição foi recebida.

Leia `operation`, `disconnect_cleanup`, `allowed_actions`, geração, ocupação e resultado técnico. A retirada da rota do número e a desativação do acesso local podem ser confirmadas enquanto uma autorização Meta compartilhada é preservada. Não afirme que um token global foi revogado ou que a parceria comercial foi removida sem a evidência correspondente.

Ao desligar:

- Interrompa envios/bots e jobs da instância.
- Invalide o uso local de tokens copiados, inclusive caches.
- Preserve o histórico com o estado desligado.
- Emita sua notificação quando o encerramento for confirmado.
- Libere capacidade apenas conforme o estado confirmado da HookCloud.

Para encerrar **o cliente inteiro**, use `deactivate-partner-customer` após autenticar o cancelamento real da assinatura no seu sistema. Acompanhe `get-customer-offboarding`; o processo pode ser assíncrono. A HookCloud avalia números e parcerias no alcance seguro; relações compartilhadas podem permanecer. Reativar o cadastro não reconecta automaticamente os números.

Não use o cancelamento da assinatura do parceiro na HookCloud como API para cancelar um cliente final. São níveis diferentes.

## 16. Erros e alertas compreensíveis

Armazene código, subcódigo, HTTP status, identificador de rastreamento e detalhe sanitizado. Não salve resposta inteira contendo tokens ou conteúdo pessoal desnecessário.

| Sinal | Como apresentar |
|---|---|
| `131037` | Número fornecido pela Meta precisa de aprovação do nome de exibição para enviar. Conferir WhatsApp Manager. |
| `131047` | Janela de atendimento encerrada; usar template aprovado. |
| `131042` | Pendência de pagamento informada pela Meta; conferir a conta. |
| `131056` | Limite temporário de envios para o mesmo destinatário. |
| `130429` | Limite de velocidade de envio da conta. |
| `131016` | Indisponibilidade temporária informada pela Meta. |
| `131000` | A operação não foi processada; diagnosticar pelo detalhe e rastreamento. |
| `100` / `131009` | Parâmetro/valor recusado; conferir o campo indicado, sem concluir banimento. |
| `190` | Falha de autorização; conferir o vínculo e a credencial, sem assumir remoção comercial. |
| `PIN_RESET_SUCCESS` | Evento de sucesso na redefinição de PIN; não é uma falha de envio por si só. |

Essas descrições não substituem a classificação de saúde da HookCloud nem a [referência oficial de erros](https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/). Preserve um código desconhecido com explicação honesta e opção de suporte; não converta todo evento desconhecido em alerta vermelho.

Quando o destinatário recebe a mensagem mas seu painel não mostra, investigue persistência, roteamento, assinatura e correlação. Reenviar não resolve perda do histórico e pode duplicar a mensagem.

## 17. Cache, escala e entregas

Use GETs locais da HookCloud para a primeira carga e reconciliação; mantenha cache por parceiro, cliente, instância e WABA. Priorize eventos para invalidar apenas o domínio alterado. Um evento de username não exige recarregar todas as empresas.

Para Graph: cache obrigatório quando aplicável, paginação, concorrência limitada, backoff com jitter e respeito a `Retry-After` quando presente. Pare loops de erro de permissão. Evite todos os tenants sincronizando ao mesmo segundo. Leituras de mídia temporária e operações críticas precisam de política própria.

GETs de templates HookCloud usam cache; refresh explícito tem controle de frequência. Saúde, perfil opcional, dados empresariais e pagamento podem ter caches diferentes. `checked_at` de um campo não é substituído pela hora em que você abriu a tela.

O painel “Saúde de entregas” do Partner depende dos eventos/reportes observados. Quando os status chegam ao seu webhook direto, use `report-partner-message-status` para reportar os fatos suportados e `report-partner-message-delivery-failure` para falhas conforme a documentação. **Essas rotas não enviam mensagens.**

Não envie `delivered` só porque recebeu HTTP 200 da Graph. Use `wamid`, horário do fato e status real. Consulte `get-partner-delivery-health` e `search-partner-delivery-events` para o resumo do Partner.

## 18. Testes e critérios de entrega

Comece por fixtures locais; use uma conta e destinatários autorizados para os testes externos.

- Login, permissões e isolamento: outro cliente não consegue consultar/enviar/anexar dados alheios.
- Onboarding: cadastro faltante, GET inválido, validação não salva, scopes insuficientes e cobrança pendente.
- Inventário: repetição sem duplicar; várias páginas; arquivados; conflito de propriedade; falha no meio sem importação parcial.
- Criação: clique duplo, timeout com mesmo `Idempotency-Key`, resposta ambígua e ausência de `instance_key` personalizada.
- Connect: demora na Meta, cancelamento, link expirado, sucesso tardio, reconexão com nova geração.
- Webhook: assinatura errada, replay, corpo alterado, duplicata, evento fora de ordem, geração antiga, fila indisponível.
- Mensagens: entrada real, texto dentro da janela, bloqueio fora dela, template aprovado, mídia, aceitação sem entrega, falha assíncrona.
- Concorrência: dois workers disputam o mesmo item e produzem uma única tentativa.
- Campanha: opt-out antes da execução, janela expirada após agendamento, cancelamento e desconexão durante a fila.
- Bot: pausa, transferência humana, ecos e mensagens duplicadas.
- Templates: criação, consulta, rejeição/reclassificação, edição e exclusão somente de registros temporários autorizados.
- Desligamento: status transitório, geração nova, autorização compartilhada preservada e histórico sem falsos checks.
- Segurança: segredos ausentes do bundle/logs, limites de requisição, arquivos autorizados e falhas sem respostas sensíveis.

Separe no relatório: teste local, API real aceita, webhook confirmado e entrega confirmada pelo destinatário. Esses níveis não são equivalentes.

## 19. Limitações que devem aparecer no projeto

Este kit não fornece credenciais, acesso ao app Meta, assinatura do receptor POST, serviço de IA, infraestrutura de filas ou política comercial pronta. Esses recursos dependem da conta e da stack.

A Meta pode exigir ações do titular, aprovação, permissões ou disponibilidade de recursos. Uma conexão tecnicamente concluída não garante envio irrestrito. Nenhuma implementação pode prometer ausência de bloqueios, aprovação instantânea de templates ou recebimento de todo evento sem possibilidade de reconciliação.

Não execute ações reais só por ter copiado o kit. Implemente o escopo solicitado e use a autorização concreta do responsável para envios, criação de recursos de teste, exclusões, alteração de cobrança e publicação.

## 20. Como usar a skill opcional

`hookcloud-partner/SKILL.md` e suas referências formam uma skill portátil para ferramentas compatíveis com esse formato. O processo de instalação varia por ferramenta. Nenhum arquivo precisa ser instalado no computador para usar o prompt e o guia como anexos.

A skill orienta decisões e testes; não instala SDK, plugin, segredo ou conta e não concede permissão a serviços externos. Se sua ferramenta não reconhece skills, cole o prompt e anexe este guia.
