Da operação à entrega confirmada
O que guardar, como conferir cada etapa e quais testes executar antes de liberar a integração.
Escolher Cloud ou Marketing (link para esta seção)
| Uso | API de envio |
|---|---|
| Bilhete, reembolso e aviso transacional | cloud, com template UTILITY aprovado e compatível com a operação. |
| Código de verificação | cloud, com template AUTHENTICATION e otp_code. A validade do código é responsabilidade do emissor. |
| Promoção | cloud ou marketing, com template MARKETING aprovado. marketing exige ativação concluída na Meta. |
| Resposta livre na janela de atendimento | cloud. Confira a janela para o mesmo número remetente; ela não determina a cobrança. |
No painel, Nova campanha mostra o seletor API de envio quando o template é de Marketing. Na integração, informe delivery_api em POST /public/v1/messages; omitir usa cloud. A escolha não aceita termos da Meta e não comprova que a conta está habilitada. Não há fallback automático.
Marketing Messages API oferece otimizações próprias de marketing; isso não garante mais entregas, menor custo ou maior velocidade. Nenhuma rota contorna consentimento, bloqueios ou pausas. Consulte pré-requisitos e sincronização e como comparar campanhas.
Uma operação por mensagem (link para esta seção)
Crie a tarefa no outbox antes do POST. Um mesmo pedido pode gerar bilhete, devolução e aviso: cada mensagem tem referência e chave próprias. Uma campanha precisa de uma operação por destinatário. A Patrocinio não conhece o seu pedido; esse vínculo fica no seu banco.
| Guardar | Finalidade |
|---|---|
| Empresa e conexão | Isolar operações, credenciais e eventos de cada integração. |
| ID da operação e do pedido | Associar, por exemplo, outbox:8421 ao pedido interno. Não reutilize o client_ref em outra mensagem. |
| client_ref e Idempotency-Key | Referência para conciliação e chave para repetição segura, ambas persistidas antes do envio. |
| phone_number_id e destinatário solicitado | Registrar o remetente e o telefone internacional informado na operação. |
| Payload e delivery_api | Preservar a identidade da operação. Proteja os dados necessários; não grave OTP, tokens ou credenciais em logs. |
| message_id e data.to | Guardar o aceite e o destinatário canônico retornado. Não sobrescreva o telefone original. |
| delivery_api_confirmed | Guardar a evidência Meta recebida no status/webhook. null significa ausência de evidência. |
| Status, erro e eventos | Manter o histórico e o estado consolidado sem perder falhas nem regredir entregas. |
Idempotency-Key é opcional no contrato HTTP e recomendado em todo envio automatizado. Sem ele, repetir o POST pode enviar outra mensagem. client_ref também é opcional: o envio não impõe unicidade nem deduplica por esse campo. Garanta uma referência única por operação na empresa; duplicatas tornam a consulta ambígua (409 ambiguous_client_ref).Uma resposta repetida com idempotent_replay: true recupera o aceite anterior. O status: sent desse replay não é o estado atual: consulte o status ou processe o webhook sem rebaixar uma mensagem já entregue.
Consolidar os status sem regressão (link para esta seção)
A mesma regra vale para bilhetes e campanhas. Não compare apenas o número de status_rank: failed é um ramo próprio. Uma entrega confirmada prevalece sobre falha ou aceite atrasados.
function proximoStatus(atual, recebido) {
const entrega = { delivered: 3, read: 4, played: 5 };
if (Object.hasOwn(entrega, recebido)) {
return (entrega[atual] || 0) > entrega[recebido] ? atual : recebido;
}
// Uma falha nunca apaga uma entrega/leitura já comprovada.
if (recebido === "failed") return entrega[atual] ? atual : "failed";
// O aceite atrasado também não desfaz uma falha confirmada.
if (recebido === "sent") return entrega[atual] || atual === "failed" ? atual : "sent";
return atual; // Estado desconhecido: preserve e encaminhe para análise.
}Aplique a função dentro de uma transação, com bloqueio da operação ou atualização condicional. Grave cada evento para auditoria, mesmo quando não mudar o estado final. Contadores de campanha devem refletir a transição efetiva; repetir um evento não incrementa o funil. Recusa de promoções é uma dimensão separada: não apaga uma entrega anterior.
| Eventos recebidos nesta ordem | Estado final esperado |
|---|---|
| delivered → sent → failed | delivered |
| sent → failed → sent | failed |
| failed → delivered → read → delivered | read |
| read → failed → read (repetido) | read, sem contar outra leitura |
Quando o webhook chega primeiro (link para esta seção)
Valide a assinatura, descarte testes sem efeitos e grave eventos reais de forma durável antes do 2xx. Deduplique pelo identificador da conexão e delivery_id. Localize a operação por message_id ou pelo client_ref já salvo, conferindo a empresa e o remetente.
Se os identificadores apontarem para operações diferentes, preserve o evento para revisão. Sem correspondência, mantenha uma fila de conciliação com tentativas limitadas e alerta; não marque como processado nem associe a um pedido apenas pelo telefone. Envios feitos no painel podem não ter uma operação no seu sistema.
unknown_message no corpo do seu receptor confirma apenas que ele aceitou o webhook. A Patrocinio não interpreta esse texto para repetir o evento. O receptor precisa guardar o evento e conciliá-lo; veja o exemplo com fila durável.Erros que não devem disparar outra mensagem (link para esta seção)
| Código | Tratamento |
|---|---|
| delivery_api_template_mismatch · 422 | Corrigir a combinação de categoria e rota. Recusa desta tentativa; não repetir em laço nem trocar a rota automaticamente. |
| marketing_api_not_ready · 422 | Revisar ativação e permissões na Meta. Suspender novas tentativas até corrigir a configuração. |
| cloud_marketing_disabled · 422 | Marketing por Cloud indisponível para a conta. Rever a configuração sem fallback automático. |
| send_uncertain | Resultado sem confirmação, inclusive em destinatários de campanha. Preservar a operação e conciliar; não gerar novo envio. |
| idempotency_in_flight · 409 | Tentativa em andamento ou incerta. Consultar com a chave original; repetição limitada mantém chave e corpo. |
| ambiguous_client_ref · 409 | Referência reutilizada. Consultar pela Idempotency-Key original ou message_id; corrigir a unicidade do outbox. |
Consulte GET /messages/status com a chave no header ou a referência na query, uma opção por chamada. unconfirmed e 404 não comprovam falha nem autorizam reenvio. Confira os demais erros, limites e política de retry.
Roteiro de validação (link para esta seção)
| Teste | Critério de aceite |
|---|---|
| Contrato e catálogo | Ler OpenAPI, números e templates da conta. Validar categoria, idioma, variáveis, botões e mídia real do envio. |
| Idempotência local | Simular duas chamadas simultâneas, timeout e resposta repetida: uma só operação. Mudar rota com a mesma chave resulta em conflito. |
| Webhook antes do POST responder | Encontrar a operação pelo client_ref. Sem vínculo, preservar para conciliação sem dar baixa no evento. |
| Eventos duplicados e fora de ordem | Executar as sequências acima, inclusive com workers concorrentes; conferir status e contadores de campanha. |
| Assinatura e isolamento | Rejeitar assinatura inválida, respeitar a tolerância de horário e nunca vincular evento à operação de outra empresa. |
| Teste pelo painel | Validar os cinco eventos com data.test === true: 2xx após assinatura, sem alterar contatos, pedidos, consentimentos ou métricas. |
| Envio real autorizado | Registrar operação, aceite, destinatário canônico e delivered/read por webhook. Falha deve preservar o código e a causa. |
| Cloud versus Marketing | Registrar rota solicitada e confirmada. Uma entrega isolada valida o caminho; não prova ganho de desempenho ou economia. |
| Falha do receptor | Simular indisponibilidade, recuperar a fila e conferir deduplicação. Reentregar webhook não reenvia mensagem no WhatsApp. |
| Cota e consentimento | Leituras e escritas compartilham 600/min por chave. Respeitar Retry-After; marketing_allowed não comprova aceite, e inbound não prova consentimento para campanha. |
A mídia deve estar acessível à Meta sem login. Não reutilize a URL temporária de amostra do catálogo como arquivo de produção: use o documento ou imagem daquela operação, com validade suficiente para download. Um arquivo que abre no painel ainda pode ser recusado quando a Meta o buscar.
Registre separadamente testes locais, chamadas pelo serviço interno, HTTP autenticado da API pública e confirmação no telefone. Não trate teste sintético de webhook como envio real, nem 200 do receptor como prova de vínculo ao pedido.