# Patrocinio Tech: API pública > Referência para integrar envio de mensagens e templates do WhatsApp, recebimento de respostas e confirmação de entrega por webhook. Base da API: https://api.patrociniotech.com/public/v1. Autenticação: Bearer, somente no backend. Comece pelo guia de integração com IA; consulte apenas os assuntos necessários. O OpenAPI descreve os campos e rotas públicas. Os exemplos não são credenciais nem dados reais. Antes de enviar, consulte o catálogo autenticado do seu número: nomes, variáveis e exigências de mídia dependem dos templates aprovados daquela conta. Um aceite de envio não confirma entrega ao destinatário. Idempotency-Key é opcional no HTTP e recomendado em todo envio automatizado; client_ref não é único por imposição do servidor. Garanta uma operação por mensagem no seu outbox, antes do POST. Em campanhas e bilhetes, failed atrasado não apaga delivered/read e sent atrasado não desfaz failed. Eventos sem vínculo ficam em conciliação, não processados. Leia validacao.md para o roteiro completo. POST /public/v1/messages mantém a mesma URL e aceita delivery_api: cloud (padrão) ou marketing (somente template MARKETING e conta ativada na Meta). Política STRICT, sem fallback entre rotas. A escolha faz parte da idempotência; não troque rota ou chave para contornar resultado incerto. delivery_api_confirmed diferencia a evidência recebida da Meta da seleção local; null não indica falha. Veja mensagens.md, webhook.md, erros.md e campanhas.md para contrato, configuração e comparação de resultados. ## Começar - [Visão geral](https://patrociniotech.com/documentacao-api/index.md): Autenticação e primeira consulta. - [Integrar com IA](https://patrociniotech.com/documentacao-api/ia.md): Instrução copiável, Markdown, OpenAPI e MCP. - [Números conectados](https://patrociniotech.com/documentacao-api/numeros.md): Remetentes, qualidade e limite compartilhado do portfólio. - [Conceitos do WhatsApp](https://patrociniotech.com/documentacao-api/conceitos.md): Janela de 24h, template, opt-in, qualidade e capacidade. ## Enviar - [Janela de 24h](https://patrociniotech.com/documentacao-api/janela.md): Confira se o contato pode receber uma mensagem livre. - [Mensagens](https://patrociniotech.com/documentacao-api/mensagens.md): Texto, template, Cloud/Marketing API e idempotência. - [Consentimento](https://patrociniotech.com/documentacao-api/consentimento.md): Consulta em lote, aceite registrado e saída de promoções. - [Resultados de campanhas](https://patrociniotech.com/documentacao-api/campanhas.md): Aceite, entrega, falhas e destinatários do painel. - [Templates](https://patrociniotech.com/documentacao-api/templates.md): Aprovação, variáveis e campos de envio. - [Exemplo completo](https://patrociniotech.com/documentacao-api/exemplo.md): Do envio à confirmação de entrega. ## Receber - [Webhook](https://patrociniotech.com/documentacao-api/webhook.md): Status de envio, resposta do contato e assinatura. - [Entregas](https://patrociniotech.com/documentacao-api/entregas.md): Consultar e reenviar eventos do webhook. ## Referência - [Validar a integração](https://patrociniotech.com/documentacao-api/validacao.md): Operações, conciliação e roteiro de testes Cloud/Marketing. - [Erros e limites](https://patrociniotech.com/documentacao-api/erros.md): Códigos de erro, limites e novas tentativas. - [Mudanças da API](https://patrociniotech.com/documentacao-api/mudancas.md): O que mudou e a regra de compatibilidade. ## Contrato - [OpenAPI JSON](https://patrociniotech.com/documentacao-api/openapi.json): Contrato público gerado pelas rotas do backend; inclui autenticação, validações e respostas. ## MCP - [MCP de documentação](https://patrociniotech.com/documentacao-api/mcp): Servidor HTTP de leitura pública. Ferramentas search_docs, read_doc e get_openapi. Não precisa de chave e não acessa contas nem envia mensagens. ## Optional - [Documentação completa em texto](https://patrociniotech.com/documentacao-api/llms-full.txt): Todos os guias, tabelas e exemplos completos em uma consulta. Prefira os arquivos por assunto para economizar contexto. --- # Integre seu sistema ao WhatsApp Envie pelo número conectado à sua conta e acompanhe a entrega por webhook. Comece com uma consulta e avance para o primeiro envio. - [Enviar mensagens](https://patrociniotech.com/documentacao-api/mensagens.md): Texto, template, PDF e código de autenticação. - [Escolher um template](https://patrociniotech.com/documentacao-api/templates.md): Confira aprovação, variáveis e anexos exigidos. - [Receber confirmações](https://patrociniotech.com/documentacao-api/webhook.md): Valide a assinatura e acompanhe entregue, lida ou falhou. ## 1. Prepare o acesso Crie uma chave no painel, em **Integrações**. Copie o valor completo quando ele aparecer e guarde-o em uma variável de ambiente no seu servidor. | Configuração | Valor | | --- | --- | | Base da API | `https://api.patrociniotech.com` | | Autenticação | `Authorization: Bearer SUA_CHAVE` | | Corpo de requisições | `Content-Type: application/json` | > Atenção: A chave dá acesso à conta e permite enviar mensagens. Use-a somente no servidor. Se houver exposição, revogue a chave em Integrações e atualize a credencial da sua aplicação. A conta precisa estar ativa ou em avaliação. Contas suspensas ou desativadas não podem usar a API, mesmo com uma chave ainda não revogada. ## 2. Escolha o número e o template Consulte [GET /public/v1/numbers](https://patrociniotech.com/documentacao-api/numeros.md) para descobrir os remetentes conectados, percorrendo as páginas. Escolha explicitamente o `phone_number_id` da operação. Substitua a chave e o ID de um número conectado. Esta consulta lista os modelos aprovados da conta WhatsApp desse número. cURL · primeira consulta ```bash curl "https://api.patrociniotech.com/public/v1/templates?phone_number_id=SEU_NUMERO_ID" \ -H "Authorization: Bearer SUA_CHAVE" ``` Para enviar um modelo, confira `sendable: true` e `api_supported: true`. A resposta informa as variáveis, o cabeçalho e os botões que você precisa preencher. > Texto livre exige uma [janela de atendimento aberta](https://patrociniotech.com/documentacao-api/janela.md). Fora dela, use um template aprovado. A janela define o formato permitido; a cobrança é informada separadamente em Faturamento. ## 3. Envie e acompanhe | Etapa | Como fazer | | --- | --- | | Enviar | Faça [POST /public/v1/messages](https://patrociniotech.com/documentacao-api/mensagens.md) com uma `Idempotency-Key` por operação e uma referência em `client_ref`. | | Registrar o aceite | Guarde `message_id`, número de envio, referência e chave. HTTP 200 com `status: sent` confirma o aceite da Meta. | | Confirmar a entrega | Receba `message.status` em um [webhook assinado](https://patrociniotech.com/documentacao-api/webhook.md). É esse evento que informa entrega, leitura ou falha. | Veja o [fluxo completo de integração](https://patrociniotech.com/documentacao-api/exemplo.md) para organizar envio, armazenamento e processamento dos eventos. ## Como ler uma resposta Verifique o status HTTP e o campo `success`. Em uma recusa, trate `error.code` no código da sua aplicação; `error.message` é a explicação para a pessoa. JSON · envio aceito ```json { "success": true, "data": { "message_id": "wamid.EXEMPLO", "status": "sent", "to": "5521999999999", "client_ref": "pedido-8421" } } ``` Em falha de conexão ou 5xx, preserve o corpo e a chave da operação. Consulte [Erros e limites](https://patrociniotech.com/documentacao-api/erros.md) antes de implementar novas tentativas. ## Importar a especificação Use este endereço no Postman, Insomnia ou gerador de clientes. O arquivo contém as rotas públicas, os campos de envio e a autenticação Bearer. URL · OpenAPI ```text https://patrociniotech.com/documentacao-api/openapi.json ``` ### Endereço direto da API A mesma especificação está disponível no endereço abaixo. Se sua ferramenta não conseguir acessá-lo, use o endereço da documentação acima. URL · origem da especificação ```text https://api.patrociniotech.com/public/v1/openapi.json ``` ## Integrar com IA O guia [Integrar com IA](https://patrociniotech.com/documentacao-api/ia.md) reúne uma instrução pronta para copiar, a ordem de leitura e o endereço do MCP de documentação. Entregue o endereço abaixo à IA que está implementando sua integração. O índice aponta para versões em Markdown por assunto e para o contrato OpenAPI, sem exigir leitura da interface. URL · índice para IA ```text https://patrociniotech.com/documentacao-api/llms.txt ``` As versões em texto são geradas do mesmo conteúdo destas páginas, incluindo tabelas e exemplos completos. Se preferir consultar tudo de uma vez, use a [documentação completa em texto](https://patrociniotech.com/documentacao-api/llms-full.txt). Nenhuma dessas referências contém sua chave ou o catálogo privado da conta. ## Referência rápida | Item | Regra | | --- | --- | | Versão | `/public/v1/` | | Dados | JSON. Datas de eventos em UTC, no formato ISO 8601. | | Limite da API | 600 requisições por minuto, por chave. Veja [limites e novas tentativas](https://patrociniotech.com/documentacao-api/erros.md). | | Mudanças | [Histórico e compatibilidade da API](https://patrociniotech.com/documentacao-api/mudancas.md) | --- Fonte: https://patrociniotech.com/documentacao-api Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Comece pela referência certa Um índice curto para encontrar o assunto, Markdown para ler e OpenAPI para validar os campos. O mesmo conteúdo serve à equipe e aos assistentes de código. - [Índice para IA](https://patrociniotech.com/documentacao-api/llms.txt): Links por assunto. Leia somente o que precisa. - [Contrato OpenAPI](https://patrociniotech.com/documentacao-api/openapi.json): Rotas, autenticação, modelos e respostas da API. - [Referência completa](https://patrociniotech.com/documentacao-api/llms-full.txt): Todos os guias e exemplos em um arquivo de texto. ## Copiar para a IA da sua equipe Use a instrução abaixo no assistente que vai trabalhar no seu repositório. Ela não contém credenciais. Informe separadamente as regras do seu negócio e disponibilize segredos pelo ambiente seguro do backend. Instrução de integração · copiar ```text Implemente a integração do nosso backend com a API pública da Patrocinio Tech. Leia primeiro https://patrociniotech.com/documentacao-api/llms.txt e o contrato https://patrociniotech.com/documentacao-api/openapi.json. Consulte os Markdown por assunto; não dependa do HTML. Se sua ferramenta aceitar MCP remoto, a documentação está em https://patrociniotech.com/documentacao-api/mcp (somente leitura, sem chave). 1. Compare a implementação existente com o contrato atual. Não invente rotas, campos, nomes de templates ou códigos de erro. 2. Guarde a chave Bearer somente no backend. Consulte GET /public/v1/numbers, percorra a paginação e escolha explicitamente phone_number_id. Consulte /templates para esse número e confira sendable, api_supported, idioma, variáveis, cabeçalho e botões. Reaproveite as consultas; não as repita antes de cada envio. 3. Telefones de envio devem incluir DDI. Use POST /public/v1/validate com country para cadastros locais; formato válido não prova que o telefone tem WhatsApp. Consulte GET /public/v1/window/{to} com o mesmo phone_number_id escolhido para texto livre. Aceite pendente, recusa e bloqueio são situações diferentes; não transforme importação de lista em consentimento. 4. Persista a operação antes de enviar: Idempotency-Key única por operação, client_ref, número escolhido e payload. Uma nova tentativa da mesma operação usa a mesma chave e o mesmo conteúdo. Se faltar message_id, consulte GET /public/v1/messages/status com Idempotency-Key no header ou client_ref na query. Não confunda unconfirmed/404 com autorização para reenviar; não gere outra chave. 5. Templates de autenticação usam otp_code e botão OTP. Gere, expire e valide o código no sistema emissor; nunca registre o código ou a chave em logs. Documentos e imagens devem cumprir o cabeçalho exigido pelo catálogo. 6. No POST /messages, HTTP 200/status sent significa aceite, não entrega. Persista message_id e acompanhe message.status. Valide X-PT-Signature (sha256=...) com HMAC-SHA256 sobre X-PT-Timestamp + ponto + corpo bruto, confira a tolerância de horário de 300 segundos do exemplo e use comparação em tempo constante. Após validar a assinatura, se data.test === true, responda 2xx sem persistir nem alterar cadastros, para qualquer tipo de evento. Para eventos reais, elimine duplicatas pelo identificador da entrega e trate eventos fora de ordem. Confirme 2xx após persistência durável; processe em fila. 7. Trate success, error.code, error.details, campos inválidos, opt-in, falhas da Meta, 429/Retry-After e tentativas limitadas. A cota de 600 requisições/minuto por chave soma consultas e envios; use os headers X-RateLimit-Remaining/Reset como fotografia, não reserva. Confira health.reasons e health.stale; LIMITED não é sinônimo de limite250. Não some limites de números do mesmo portfólio nem os trate como saldo. 8. Teste isolamento de empresas, duplicidade, timeout, assinatura inválida, entrega fora de ordem e rejeições antes de publicar. Use dados fictícios e apenas um telefone de teste expressamente autorizado. Não dispare campanhas, crie chaves nem altere endpoints reais automaticamente. 9. Consulte consentimento em lote por POST /public/v1/contacts/consent/batch. marketing_allowed indica ausência de bloqueio local, não comprova aceite. accepted com origem inbound registra conversa recebida, não autorização para campanhas; não conte esses contatos como consentimentos comprovados. Registre consentimentos reais por PUT /public/v1/contacts/{to}/consent com event_id, data, origem e referência da prova; não declare aceite de contatos importados sem autorização. 10. Para monitorar promoções e modelos, prepare contact.marketing_opt_out, template.status e template.quality e peça ao administrador para habilitar as assinaturas. POST /public/v1/templates submete modelos à Meta; consulte status=ALL antes de usar. Campanhas do painel são consultadas por GET /public/v1/campaigns e /campaigns/{id}/results, com paginação e totais separados de aceite, entrega, falha e incerteza. counts.read não entra em counts.delivered; use delivered_including_read. opted_out sobrepõe os estados e não deve ser somado ao total de entrega. 11. Em campanhas, dispatch_failed é um contador histórico de ocorrências: pode incluir exclusões e tentativas sem confirmação. Decida pelo status e error_code de cada destinatário. uncertain/send_uncertain nunca autoriza reenvio automático, mesmo sem message_id. Não reenviar sent/delivered/read nem contatos com saída de promoções. Antes de preparar outra campanha, atualize os resultados, revise a causa e revalide consentimento; a exportação é só uma fotografia. Reentregar webhook não reenvia mensagem no WhatsApp. A previsão do painel é para terminar o processamento da fila, não para garantir entrega ao cliente. 12. Campanha paused suspende novas tentativas, mas uma requisição em andamento pode terminar. Webhooks PAUSED/DISABLED/REJECTED do template pausam campanhas no mesmo tenant, WABA, modelo e idioma; qualidade RED apenas avisa. APPROVED nunca retoma sozinho. A retomada pelo painel exige validação atual e inclui somente destinatários ainda sem tentativa; não repete aceitos, falhas tentadas ou uncertain. A API pública de campanhas é somente consulta. Não presuma prazo fixo de liberação da Meta. 13. cancelled encerra a fila por ação do usuário no painel e não permite retomada dessa campanha. Não apaga histórico nem cancela mensagens já aceitas na Meta. Destinatários sem tentativa ficam not_sent; tentativas em andamento sem confirmação ficam uncertain. Uma pausa Meta nunca cancela a campanha automaticamente. 14. pause_reason template_catalog_unavailable ou template_category_unavailable significa que a validação do catálogo/categoria não pôde ser concluída. A fila é preservada, não há confirmação de rejeição ou falha de entrega, e a retomada exige ação no painel após uma consulta bem-sucedida. 15. POST /public/v1/messages aceita delivery_api: cloud (padrão) ou marketing. A URL base e a autenticação da Patrocinio não mudam. marketing é somente para template MARKETING aprovado e conta com ativação concluída na Meta; texto livre, UTILITY e AUTHENTICATION continuam cloud. A rota Meta marketing_messages usa product_policy STRICT, sem fallback silencioso para Cloud API. Não aceite termos nem altere message_activity_sharing automaticamente. Qualidade, limites, bloqueios e opt-out continuam valendo; não prometa mais entrega nem menor preço. 16. Persista delivery_api com a operação. Omitir e informar cloud explicitamente são equivalentes para a Idempotency-Key; mudar cloud/marketing com a mesma chave retorna 409 idempotency_conflict. Não troque rota nem chave para contornar um envio incerto. Leia delivery_api na resposta/status e campaign.delivery_api nos resultados de campanhas. delivery_api_confirmed em status, message.status e destinatários registra apenas a evidência da classificação Meta: marketing_lite confirma marketing, marketing confirma cloud. Histórico sem dado permanece null; seleção ou confirmação da rota não prova entrega. Para comparar rotas, use públicos equivalentes sem sobreposição e o mesmo tempo de observação; read é subconjunto das entregas reais, e delivered_including_read já inclui essas leituras. 17. Trate 422 delivery_api_template_mismatch como incompatibilidade de categoria, marketing_api_not_ready como configuração/permissão pendente e cloud_marketing_disabled como recusa Meta 131063 para marketing pela Cloud API; delivery_api inválido retorna 422 invalid_request. Não repita esses erros em laço nem implemente fallback automático. Na Marketing Messages API, templates novos e modelos sem uso por mais de sete dias precisam de sincronização: leia mensagens.md e a orientação Meta antes do tráfego, sem envio automático de aquecimento nem promessa de entrega. 18. Idempotency-Key é opcional no contrato HTTP e recomendado em todo envio automatizado. client_ref também é opcional: o POST não impõe unicidade nem deduplica por esse campo. Imponha unicidade por empresa no outbox, com uma tarefa por mensagem (ex.: outbox:8421). Bilhete, reembolso e aviso do mesmo pedido são tarefas separadas. Vincule o pedido localmente; cada destinatário de campanha tem sua própria operação. 19. Guarde data.to (destinatário canônico), delivery_api, delivery_api_confirmed e message_id sem apagar o telefone solicitado. O retorno idempotent_replay recupera um aceite antigo; status sent nesse retorno não deve rebaixar o estado atual. Em bilhetes e campanhas, failed substitui sent, nunca delivered/read/played; sent atrasado não desfaz failed. Aplique transições e contadores sob transação, deduplicando eventos por conexão e delivery_id. 20. Se o evento não corresponder à operação, mantenha uma fila de conciliação e alerta, sem marcá-lo processado. Confira empresa, remetente e conflitos entre message_id/client_ref. Não associe pelo telefone. HTTP 200 com unknown_message ainda confirma recepção à Patrocinio: a responsabilidade de reprocessar o evento retido é do receptor. 21. Execute o roteiro https://patrociniotech.com/documentacao-api/validacao.md. Não descarte uma integração existente que já atende os critérios: teste os casos de regressão e implemente somente as lacunas. Falhas de configuração da rota exigem correção, sem fallback; send_uncertain exige conciliação, nunca novo envio automático. Entregue o que mudou, testes executados, configuração necessária e pendências reais. Diferencie teste local/serviço interno de chamada HTTP autenticada em produção. Se faltar credencial ou definição de negócio, explique sem inventar sucesso. ``` ## Conectar a documentação por MCP Em um cliente compatível com MCP remoto por HTTP, adicione o endereço abaixo. Não exige login nem chave da API: este servidor consulta somente a documentação pública. Envios e consultas da sua conta continuam na API autenticada. URL · servidor MCP ```text https://patrociniotech.com/documentacao-api/mcp ``` | Recurso | Como usar | | --- | --- | | search\_docs | Busque por assunto ou campo. Retorna até cinco trechos com o endereço da página. | | read\_doc | Leia o Markdown completo de um assunto pelo nome do arquivo, como webhook.md. | | get\_openapi | Obtenha o contrato público atual. Falha explicitamente se a origem estiver indisponível. | | Resources | Índice, páginas em Markdown e especificação OpenAPI também podem ser lidos como recursos MCP. | O transporte é HTTP sem sessão persistente, com compatibilidade com clientes Streamable HTTP de 2025 e com o protocolo de 2026. O formato de configuração varia por ferramenta. Se o cliente não aceitar MCP remoto, use os links Markdown e OpenAPI: o conteúdo é o mesmo. > O MCP não envia mensagens, acessa contatos, consulta o banco da sua conta, cria chaves ou altera configurações. Não envie segredos no texto das ferramentas de documentação. ## Ordem de leitura | Objetivo | Referência | | --- | --- | | Preparar acesso e escolher o remetente | [Autenticação](https://patrociniotech.com/documentacao-api/index.md) e [números conectados](https://patrociniotech.com/documentacao-api/numeros.md). | | Enviar e evitar duplicidade | [Templates](https://patrociniotech.com/documentacao-api/templates.md), [mensagens](https://patrociniotech.com/documentacao-api/mensagens.md) e [erros e limites](https://patrociniotech.com/documentacao-api/erros.md). | | Escolher Cloud ou Marketing Messages API | [delivery\_api e idempotência](https://patrociniotech.com/documentacao-api/mensagens.md); [comparação de campanhas](https://patrociniotech.com/documentacao-api/campanhas.md) com públicos e períodos equivalentes. | | Receber e conciliar | [Webhook](https://patrociniotech.com/documentacao-api/webhook.md), [fila de eventos](https://patrociniotech.com/documentacao-api/entregas.md) e consulta de status do envio. | | Revisar uma integração existente | [Mudanças da API](https://patrociniotech.com/documentacao-api/mudancas.md) e [exemplo completo](https://patrociniotech.com/documentacao-api/exemplo.md). | | Validar antes de liberar | [Operações, unicidade, status fora de ordem e roteiro de testes Cloud/Marketing.](https://patrociniotech.com/documentacao-api/validacao.md) | ## Antes de liberar envios reais Use o [roteiro de validação](https://patrociniotech.com/documentacao-api/validacao.md) como critério de entrega. Ele diferencia campos opcionais do contrato, garantias que ficam no seu outbox e evidências de aceite, entrega e processamento do webhook. Confira o número remetente, os templates disponíveis, a política de consentimento e o tratamento de erros. Teste duplicidade, falha de rede e webhook fora de ordem. Valide a entrega no telefone autorizado: aceite da API não equivale a mensagem recebida. As páginas e seus arquivos Markdown usam a mesma fonte. O OpenAPI é gerado pelas rotas públicas do backend; não inclui rotas do painel. Nenhuma especificação substitui a consulta autenticada do catálogo de cada empresa. --- Fonte: https://patrociniotech.com/documentacao-api/ia Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Escolha o número de envio Liste os números conectados à sua empresa, confira a qualidade conhecida e o limite compartilhado do portfólio. ### GET /public/v1/numbers A chave Bearer limita a consulta à sua empresa. Números desconectados ou removidos não entram na lista. Use o `phone_number_id` escolhido no catálogo de templates e no envio; ele é um identificador da Meta, não o telefone do contato. cURL · listar números ```bash curl "https://api.patrociniotech.com/public/v1/numbers?limit=20" \ -H "Authorization: Bearer SUA_CHAVE" ``` ## Paginação | Parâmetro | Regra | | --- | --- | | limit | De 1 a 50. Padrão: 20 números por página. | | after\_id | Copie next\_after\_id da resposta anterior. Omita na primeira página; não incremente nem interprete o cursor. | | has\_more / next\_after\_id | Enquanto has\_more for true, consulte a próxima página. next\_after\_id é null quando a lista termina. | A ordem é lexical por ID. A listagem não congela o cadastro: uma conexão nova que entre antes do cursor será vista em uma nova varredura. Guarde o catálogo localmente e renove periodicamente, em vez de fazer esta consulta antes de cada mensagem. JSON · resposta ilustrativa ```json { "success": true, "data": { "items": [ { "phone_number_id": "123456789012345", "waba_id": "234567890123456", "business_portfolio_id": "345678901234567", "label": "Avisos", "display_phone_number": "+55 11 90000-0000", "verified_name": "Empresa Exemplo", "quality": { "rating": "GREEN", "label": "Alta", "source": "meta", "checked_at": "2026-11-06T12:00:00Z" }, "messaging_limit": { "value": 10000, "tier": "TIER_10K", "unlimited": false, "known": true, "label": "10.000", "scope": "business_portfolio", "window_hours": 24, "source": "whatsapp_business_manager_messaging_limit", "checked_at": "2026-11-06T12:00:00Z" }, "health": { "state": "AVAILABLE", "source": "monitoring", "checked_at": "2026-11-06T11:55:00Z", "reasons": [], "stale": false }, "meta_available": true } ], "has_more": false, "next_after_id": null, "checked_at": "2026-11-06T12:00:01Z" } } ``` ## O que cada leitura significa | Campo | Interpretação | | --- | --- | | quality.rating | GREEN, YELLOW, RED, UNKNOWN ou null. UNKNOWN/null não são qualidade baixa; representam ausência de nota ou indisponibilidade. | | messaging\_limit.value | Destinatários únicos em uma janela móvel de 24 h, compartilhados pelo portfólio. Não é mensagens por minuto, saldo restante ou limite individual de cada número. | | messaging\_limit.known / unlimited | known=false: não foi possível conhecer o limite. unlimited=true: a Meta informou ilimitado. Nos dois casos value pode ser null; não confunda desconhecido com ilimitado. | | business\_portfolio\_id | Agrupa números que compartilham a capacidade. Não some o limite desses números. Pode ser null quando a conexão não informa o portfólio. | | health.state | Última verificação persistida: AVAILABLE, LIMITED, BLOCKED ou UNKNOWN. Não garante que uma nova mensagem será aceita ou entregue. | | health.reasons | Lista de restrições: code, title, action e entity. 141010 é uma pendência de verificação da empresa; não reduz por si só o limite para 250. Erros de SIP/chamadas são excluídos. | | health.stale | true quando não há leitura ou ela tem mais de 30 minutos. Confira a hora antes de tomar decisões com o estado persistido. | | checked\_at | A resposta, a qualidade, o limite e a saúde têm horários próprios. A data da consulta não indica quando a Meta alterou o valor. | ## Falha parcial e atualização `BLOCKED` informa impedimento de envio: suspenda a campanha e confira os motivos. `LIMITED` exige avaliar `reasons`; não equivale a bloqueio total. Uma pendência de verificação pode coexistir com qualidade GREEN e limite TIER\_10K. Mesmo AVAILABLE não reserva capacidade: acompanhe recusas, entrega e qualidade durante o envio. Para UNKNOWN ou leitura antiga, não conclua que o número está liberado. Uma falha em um número não apaga os outros. A página retorna HTTP 200 com `meta_available: false` nesse item, qualidade e limite desconhecidos, preservando identidade e a última saúde registrada. Mostre indisponível; não converta esse caso em bloqueado, qualidade baixa ou custo zero. Qualidade e limite podem usar uma consulta Meta feita há até 120 segundos. O monitoramento de saúde é separado, distribuído em lotes e com novas tentativas espaçadas em falhas. Em fila acumulada ou indisponibilidade, seu horário pode ser mais antigo. Use os horários na interface para deixar isso claro. > A rota consome a cota compartilhada de 600 requisições por minuto por chave. Ela não oferece reserva de capacidade nem verifica se um destinatário possui WhatsApp. Uma lista vazia é válida; erros de autenticação, paginação e limite continuam usando o envelope de erros da API. --- Fonte: https://patrociniotech.com/documentacao-api/numeros Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Conceitos do WhatsApp Entenda a janela de atendimento, os templates e os limites que afetam sua integração. > A autorização de envio e a cobrança são controles diferentes. Uma janela aberta permite texto livre, mas não garante gratuidade. Consulte a tela de Faturamento e os valores informados pela Meta. ## 1. A janela de 24 horas Uma mensagem do contato abre a janela de **24 horas** para o número da empresa que a recebeu. Nesta API, você pode responder com texto livre ou template. Fora da janela, use um template aprovado. O prazo conta a partir da **última mensagem recebida do contato**. Uma nova mensagem dele renova a janela; respostas da empresa não alteram o prazo. > Atenção: A janela pode vencer entre a consulta e o envio. Trate `outside_24h_window`mesmo quando uma consulta anterior indicava janela aberta. ### Como saber se está aberta No painel, o cabeçalho da conversa mostra "24h aberta" ou "24h fechada". Pela API, use [GET /public/v1/window/{to}](https://patrociniotech.com/documentacao-api/janela.md). A consulta usa as mensagens recebidas que foram registradas pela plataforma. A Meta confirma a permissão quando a mensagem é enviada, então trate também o erro de janela vencida. ## 2. Template aprovado Template é um modelo de mensagem aprovado pela Meta. Crie o modelo no painel, em Templates, e aguarde a aprovação. Para enviá-lo pela API, informe o nome, o idioma e os campos exigidos pelo cadastro. As partes variáveis podem ser posicionais, como `{{1}}`, ou nomeadas, como `{{pedido}}`. Alterações no conteúdo dependem das regras de edição e revisão da Meta. ### As três categorias | Categoria | Para que serve | Cuidado | | --- | --- | --- | | Marketing | Promoção, novidade, reativação. | A mensagem precisa ser esperada pelo destinatário. Confira o consentimento e as regras vigentes. | | Utilidade | Confirmação de pedido, aviso de entrega, cobrança, agendamento. | Deve estar relacionado a uma solicitação ou transação do destinatário. Conteúdo promocional pode ser reclassificado. | | Autenticação | Código de verificação de acesso ou de uma operação. | Use o formato próprio de autenticação e valide o código no seu sistema. | > A Meta pode reclassificar um template. Confira a categoria retornada pela API e não misture promoções com avisos de utilidade. Códigos de verificação devem usar autenticação. ### Se o modelo for rejeitado Consulte o motivo informado pela Meta no painel de Templates. Revise a categoria, o texto e os exemplos das variáveis antes de solicitar uma nova análise. ## 3. Opt-in (consentimento) Antes de iniciar mensagens pelo WhatsApp, respeite as regras de permissão da Meta e a preferência do destinatário. O registro pode vir de um formulário no seu site, de uma caixa marcada no checkout, de uma conversa em que a pessoa pediu para ser avisada, ou do próprio WhatsApp. Mantenha a data, a origem e o contexto da autorização. O painel permite registrar o consentimento por contato; seu sistema também deve conservar as evidências da operação. > Atenção: Mensagens não solicitadas podem gerar bloqueios e denúncias. Envie apenas para contatos que esperam a comunicação e respeite pedidos de cancelamento. Mantenha uma lista de contatos que não desejam receber mensagens e consulte-a antes de enviar, inclusive nas campanhas por etiqueta. ## 4. Qualidade do número A Meta informa a qualidade do número com base em sinais recentes dos destinatários, incluindo bloqueios e denúncias. Acompanhe o status e evite insistir em mensagens que a pessoa não espera. | Nota | O que significa | | --- | --- | | Verde (alta) | Qualidade alta nos sinais informados pela Meta. | | Amarelo (média) | Revise os destinatários, a frequência e o conteúdo das mensagens. | | Vermelho (baixa) | Revise as campanhas e acompanhe possíveis restrições de envio. | A qualidade pode afetar a capacidade de envio. Confira a situação do número e eventuais restrições no painel e no WhatsApp Manager. ## 5. Limite diário O limite de mensagens iniciadas pela empresa considera destinatários únicos em uma janela móvel de 24 horas. Ele é diferente do limite de requisições da API e da capacidade de envio por segundo. Consulte o limite atribuído ao seu portfólio no WhatsApp Manager. A elegibilidade e o aumento de capacidade dependem dos critérios atuais da Meta. Não fixe um limite universal no código da integração. > Dimensione sua fila conforme os limites da conta e acompanhe falhas e qualidade ao aumentar o volume. ## 6. Coexistência com o aplicativo Na coexistência, o mesmo número pode operar no aplicativo WhatsApp Business e na API. O painel exibe as mensagens do aplicativo que a Meta sincronizou. Isso não significa que o webhook público encaminhe essas saídas como mensagens recebidas do contato. Mensagens enviadas pelo aplicativo **não têm as mesmas confirmações disponíveis no painel** que uma mensagem enviada pela API. A disponibilidade de histórico e mídia também depende do que a Meta sincronizou. Não conte ausência de confirmação como falha de entrega. ## Como isso vira erro na API | Conceito | Erro que aparece | | --- | --- | | Janela de 24h | `outside_24h_window` | | Template | `invalid_template` | | Qualidade e limite diário | `delivery_limited` | | Conta em revisão | `account_restricted` | | Destinatário não pôde receber | `recipient_unreachable` | A lista completa, com o que fazer e quais valem repetir, está em [Erros e limites](https://patrociniotech.com/documentacao-api/erros.md). --- Fonte: https://patrociniotech.com/documentacao-api/conceitos Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Janela de 24h Confira se o contato pode receber texto livre pelo número que fará o envio. A janela define se você pode enviar texto livre. Fora dela, use um template aprovado. Estar dentro da janela não significa que a mensagem será gratuita: a cobrança depende das regras da Meta. ### GET /public/v1/window/{to} ## O fluxo Consulte o mesmo número que fará o envio antes de escolher o formato da mensagem. A Meta confirma a permissão no momento do envio, porque a janela pode vencer entre as duas chamadas. fluxo ```text janela aberta -> POST /messages com "text" ou "template" janela fechada -> POST /messages com "template" aprovado ``` curl ```bash curl https://api.patrociniotech.com/public/v1/window/5521999999999 \ -H "Authorization: Bearer SUA_CHAVE" ``` ## Resposta 200 ```text { "success": true, "data": { "to": "5521999999999", "open": true, "can_send_text": true, "seconds_left": 84141, "expires_at": "2026-08-03T11:14:22.000Z", "last_inbound_at": "2026-08-02T11:14:22.000Z", "opt_in": "accepted", "opt_in_at": "2026-08-02T11:14:22.000Z", "opt_in_source": "inbound", "opt_in_scope": "phone_number", "marketing_allowed": true, "marketing_block_reason": null } } ``` | Campo | O que é | | --- | --- | | open | A janela está aberta agora. | | can\_send\_text | Mesmo valor de `open`. Indica se o envio com `text` está permitido. | | seconds\_left | Quantos segundos faltam para fechar. Zero quando está fechada. | | expires\_at | Quando fecha, em UTC. Nulo se o contato nunca escreveu. | | last\_inbound\_at | Última vez que o contato escreveu para o número consultado. | | opt\_in | accepted, pending, declined ou nulo, conforme o cadastro no número consultado. Não define se a janela está aberta. | | opt\_in\_at / opt\_in\_source | Data e origem do estado local. accepted com origem inbound indica que a pessoa escreveu; não comprova autorização para promoções. null significa informação não registrada. | | opt\_in\_scope | phone\_number: estado do cadastro no número selecionado. Uma recusa em outro número da empresa também impede marketing. | | marketing\_allowed | Ausência de bloqueio registrado para marketing. True não comprova consentimento, aprovação do template ou entrega. | | marketing\_block\_reason | opted\_out\_or\_blocked quando existe recusa de promoções na empresa ou bloqueio no número consultado; nulo sem essa exclusão. | ## Janela aberta não é autorização de marketing O contato pode ter `open: true` e `marketing_allowed: false`. A primeira informação permite responder ao atendimento com texto; a segunda impede novos templates promocionais pela API. `opt_in` descreve o cadastro deste número. Uma saída de promoções registrada em outro número da mesma empresa também impede marketing: por isso, consulte `marketing_allowed`, não apenas `opt_in`. > Para um contato pendente ou desconhecido, `marketing_allowed: true` significa apenas que não encontramos exclusão registrada. Não converta esse resultado em `accepted`. A origem e o consentimento da sua lista continuam sob responsabilidade da integração. ## O que abre a janela A mensagem do **contato** renova a janela por 24 horas a partir do horário da mensagem. Envio, entrega e leitura de mensagens da empresa não estendem o prazo. > Se não houver mensagem recebida registrada, a resposta será `open: false`. Para iniciar a conversa, use um template aprovado e respeite a permissão do destinatário. ## Vários números Se a sua conta tem mais de um número conectado, a janela é por número. Passe `phone_number_id` para consultar a de um específico; sem ele, consultamos o número padrão, seguindo a mesma seleção usada no envio. Não combinamos janelas de números diferentes. O telefone em `to` deve conter DDI e DDD. Para cadastros locais, [normalize o telefone informando o país](https://patrociniotech.com/documentacao-api/mensagens.md); esta rota não deduz o país nem acrescenta 55. curl ```bash curl "https://api.patrociniotech.com/public/v1/window/5521999999999?phone_number_id=1234567890" \ -H "Authorization: Bearer SUA_CHAVE" ``` ## Usar a janela recebida no webhook A consulta é opcional. O evento [message.received](https://patrociniotech.com/documentacao-api/webhook.md) já informa `window_expires_at`. Mantenha a maior validade por contato e número da empresa para escolher entre texto e template sem consultar a cada resposta. > Atenção: Perto do vencimento, confirme a janela com esta consulta. Trate o erro `outside_24h_window` caso o prazo termine antes do envio. --- Fonte: https://patrociniotech.com/documentacao-api/janela Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Enviar mensagens Envie texto livre na janela de atendimento ou use um template aprovado, com os anexos e botões previstos no modelo. ### POST /public/v1/messages > Escolha `text` ou `template`, nunca os dois. Os modelos dos exemplos são ilustrativos: consulte [os templates do seu número](https://patrociniotech.com/documentacao-api/templates.md) e use os nomes e campos retornados. ## Enviar um template Este exemplo usa um modelo com duas variáveis numeradas: nome da pessoa e número do pedido. Pode ser enviado dentro ou fora da janela de atendimento. cURL · enviar template ```bash curl -X POST https://api.patrociniotech.com/public/v1/messages \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: aviso-pedido-8421-v1" \ -d '{ "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "template": "aviso_entrega", "language": "pt_BR", "variables": [ "Maria", "8421" ], "client_ref": "pedido-8421" }' ``` ## Escolher a API de entrega O endereço da Patrocinio continua `POST /public/v1/messages`. O campo opcional `delivery_api` escolhe a API da Meta para esta mensagem; não muda o número remetente, a chave de acesso nem a URL base da integração. | delivery\_api | Quando usar | | --- | --- | | cloud | Padrão quando o campo é omitido. WhatsApp Cloud API: texto livre permitido pela janela e templates das categorias suportadas, incluindo MARKETING. | | marketing | Marketing Messages API: somente templates aprovados da categoria MARKETING. Exige que a conta conclua a ativação correspondente na Meta. | cURL · template de marketing pela Marketing Messages API ```bash curl -X POST https://api.patrociniotech.com/public/v1/messages \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: oferta-novembro-contato-8421-marketing-v1" \ -d '{ "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "template": "oferta_agencias", "language": "pt_BR", "delivery_api": "marketing", "variables": [ "Maria" ], "client_ref": "oferta-novembro-contato-8421" }' ``` Confirme as variáveis e os anexos no catálogo do seu número antes de usar o exemplo. Texto livre, avisos de utilidade e códigos de autenticação continuam em `cloud`; escolher `marketing` para esses conteúdos retorna `422 delivery_api_template_mismatch`, sem converter a categoria. Valores diferentes de `cloud` e `marketing` retornam `422 invalid_request`. Ao escolher `marketing`, a Patrocinio encaminha o envio ao endpoint Meta `/PHONE_NUMBER_ID/marketing_messages` com `product_policy: STRICT`. Não existe troca automática para Cloud API quando essa rota está indisponível ou recusa o envio. O integrador não deve implementar essa troca como retry. > A seleção não aceita termos nem ativa a conta na Meta, e não altera `message_activity_sharing`. Aprovação do template, qualidade, limites, bloqueios e saída de promoções continuam válidos. A opção não garante mais entregas nem preço menor. Na Marketing Messages API, um template novo pode levar até dez minutos para sincronizar. Um modelo sem uso por mais de sete dias também precisa de sincronização após o primeiro uso; a Meta orienta aguardar dez minutos antes de iniciar o tráfego de marketing. Isso não é prazo de aprovação nem garantia de entrega. Não envie mensagens de aquecimento automaticamente. Se a Meta recusar marketing pela Cloud API com o código `131063`, a integração retorna `422 cloud_marketing_disabled`. Falta de ativação ou permissão da Marketing Messages API retorna `422 marketing_api_not_ready`. Confira a configuração e a [orientação do erro](https://patrociniotech.com/documentacao-api/erros.md); nenhuma dessas respostas troca a rota por conta própria. Referências Meta: [ativação da Marketing Messages API](https://developers.facebook.com/documentation/business-messaging/whatsapp/marketing-messages/get-started/) e [envio de marketing](https://developers.facebook.com/documentation/business-messaging/whatsapp/marketing-messages/send-marketing-messages/). ## Resposta de envio aceito JSON · HTTP 200 ```json { "success": true, "data": { "message_id": "wamid.EXEMPLO", "status": "sent", "delivery_api": "cloud", "to": "5521999999999", "client_ref": "pedido-8421" } } ``` `sent` confirma que a Meta aceitou o envio. Guarde `message_id` e acompanhe `message.status` no [webhook](https://patrociniotech.com/documentacao-api/webhook.md) para confirmar entrega, leitura ou falha. `delivery_api` registra a API selecionada para a operação. Esse campo não é confirmação de entrega; mantenha-o junto à referência e ao payload para consultar e comparar os resultados. Guarde também o `to` da resposta: ele identifica o destinatário reconhecido pela Meta. Mantenha o status no seu sistema a partir dos eventos; para conferir uma mensagem já identificada, use a consulta abaixo. ## Consultar um envio sem reenviar ### GET /public/v1/messages/{message_id}/status Informe o `message_id` devolvido no envio. A consulta lê o estado registrado na Patrocinio Tech, não envia uma mensagem nem consulta o telefone na Meta. Somente mensagens de saída da empresa autenticada são acessíveis. cURL · consultar status ```bash curl "https://api.patrociniotech.com/public/v1/messages/wamid.EXEMPLO/status" \ -H "Authorization: Bearer SUA_CHAVE" ``` JSON · estado registrado ```json { "success": true, "data": { "message_id": "wamid.EXEMPLO", "status": "delivered", "delivery_api": "cloud", "delivery_api_confirmed": null, "client_ref": "pedido-8421", "phone_number_id": "SEU_NUMERO_ID", "to": "5521999999999", "created_at": "2026-11-06T12:00:00Z", "message_at": "2026-11-06T12:00:00Z", "checked_at": "2026-11-06T12:01:00Z", "error": null } } ``` | Campo | Interpretação | | --- | --- | | status | sent, delivered, read, played, failed ou unknown. unknown indica ausência de um estado reconhecido; não é confirmação de falha. | | delivery\_api | API selecionada para o envio: cloud ou marketing. Pode ser null em históricos sem esse registro. Não representa entrega nem uma mudança de categoria do template. | | delivery\_api\_confirmed | API comprovada pela classificação do webhook Meta: marketing\_lite confirma marketing; marketing confirma cloud. null significa que essa evidência ainda não foi registrada, não uma falha de envio. | | error | Somente em failed: code numérico da Meta, quando preservado, reason estável da integração e message em português. O texto bruto não é exposto; code pode ser nulo. | | created\_at / message\_at | Horários UTC do registro e da mensagem/última tentativa. Não são o horário de entrega ou leitura; esses horários não são disponibilizados nesta consulta. | | checked\_at | Horário desta consulta. Não comprova que chegou um novo evento da Meta nesse instante. | > O webhook continua sendo o caminho principal. Use esta consulta para conciliar estados pendentes, com intervalo e tentativas limitados; ela também consome a cota de 600 requisições por minuto por chave. Codifique o identificador no caminho da URL com `encodeURIComponent(message_id)`. `404 message_not_found` pode significar identificador incorreto, registro ainda não persistido, removido pela retenção ou pertencente a outra conta. Não comprova que a mensagem deixou de ser enviada nem autoriza reenvio automático. Sem `message_id`, preserve a operação e a chave de idempotência para recuperar o aceite original. ## Consultar quando o envio ficou sem resposta ### GET /public/v1/messages/status Se o POST terminou em timeout ou 5xx antes de devolver o `message_id`, consulte com a **Idempotency-Key original**. A leitura encontra inclusive a reserva da tentativa, sem enviar outra mensagem nem liberar a chave. cURL · consultar pela chave original ```bash curl "https://api.patrociniotech.com/public/v1/messages/status" \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Idempotency-Key: aviso-pedido-8421-v1" ``` Para uma mensagem já registrada, também é possível consultar por `client_ref`, de até 64 caracteres. Informe apenas uma das opções por chamada. Use uma referência por operação; se houver duas tentativas com a mesma referência, a API retorna `409 ambiguous_client_ref` e não escolhe uma delas. cURL · consultar pela referência ```bash curl --get "https://api.patrociniotech.com/public/v1/messages/status" \ -H "Authorization: Bearer SUA_CHAVE" \ --data-urlencode "client_ref=pedido-8421" ``` | data.resolution | O que significa | | --- | --- | | matched | A mensagem foi localizada no banco. data.message traz message\_id, status, destinatário, horários e error, no mesmo formato da consulta por ID. message\_id pode ser null em uma recusa anterior ao aceite. | | accepted | Existe um aceite durável da Meta, mas o registro local não foi concluído. message traz o ID original e status sent; isso ainda não confirma entrega. Preserve a operação para reconciliação. | | unconfirmed | Existe uma reserva sem confirmação do envio. message é null. Não significa failed e não permite criar uma nova chave para enviar de novo. | JSON · tentativa ainda sem confirmação ```json { "success": true, "data": { "lookup": "idempotency_key", "resolution": "unconfirmed", "message": null, "checked_at": "2026-11-06T12:01:00Z", "retention": { "idempotency_days": 2, "messages_days": 365, "measured_from": "created_at" } } } ``` > Atenção: Uma consulta por `client_ref` só encontra tentativas já persistidas. Para timeout antes desse registro, use a chave de idempotência. Em qualquer consulta, `404 message_not_found` não comprova que o envio falhou: pode haver processamento em andamento ou remoção pela retenção. `retention` informa os prazos configurados no momento da consulta, contados da criação. São dois dias para a reserva e 365 dias para mensagens por padrão; a manutenção é periódica. Mantenha seu histórico de operações e não use o fim desse prazo como autorização para repetir um envio incerto. ## Repetir a operação com segurança O cabeçalho `Idempotency-Key` é opcional no contrato HTTP e recomendado em todo envio automatizado. Sem ele, repetir o POST pode enviar outra mensagem. A chave identifica uma operação dentro da empresa, inclusive quando existem várias chaves de API. Gere uma `Idempotency-Key` por mensagem e salve-a antes de enviar. Em falha de conexão ou 5xx, repita com **a mesma chave e o mesmo corpo**. A escolha de `delivery_api` faz parte dessa operação. Omitir o campo e enviar `cloud` explicitamente são equivalentes, inclusive para a chave de idempotência. Repetir a mesma chave com a mesma API de entrega conserva o envio original; trocar `cloud` por `marketing`, ou o contrário, usando a mesma chave retorna `409 idempotency_conflict`. Não altere a rota nem gere outra chave para contornar um resultado incerto. | Resultado | Como agir | | --- | --- | | 200 · idempotent\_replay: true | A resposta é de uma operação já aceita. Use o message\_id retornado; a repetição não dispara outro envio. | | 409 · idempotency\_in\_flight | A operação está em andamento ou sem confirmação. Aguarde e repita com a mesma chave. Se persistir, confira os eventos antes de iniciar outro envio. | | 409 · idempotency\_conflict | A chave pertence a outro conteúdo. Confira a operação original; use outra chave somente para uma nova mensagem intencional. | | 409 · idempotency\_legacy\_unverified | O registro antigo não permite comparar o conteúdo. Verifique o envio original e os eventos antes de outra tentativa. | > Atenção: Não gere outra chave para contornar uma tentativa sem confirmação. A reserva não é liberada automaticamente após cinco minutos. A proteção dura pelo período de retenção; mantenha o histórico da operação também no seu sistema. Dados recusados antes do envio podem ser corrigidos. Para tratar limites e indisponibilidade, use a política de [novas tentativas](https://patrociniotech.com/documentacao-api/erros.md). ## Enviar texto livre O contato precisa ter escrito para o mesmo número da empresa nas últimas 24 horas. Confirme a [janela de atendimento](https://patrociniotech.com/documentacao-api/janela.md) quando necessário; ela não determina a gratuidade. cURL · enviar texto livre ```bash curl -X POST https://api.patrociniotech.com/public/v1/messages \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: texto-pedido-8421-v1" \ -d '{ "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "text": "Seu pedido saiu para entrega.", "client_ref": "pedido-8421" }' ``` ## Enviar um template com PDF O cabeçalho `DOCUMENT` exige o arquivo em cada envio. Este exemplo combina um PDF, variáveis nomeadas e um botão de link dinâmico. cURL · enviar template com PDF ```bash curl -X POST https://api.patrociniotech.com/public/v1/messages \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: bilhete-pedido-8421-v1" \ -d '{ "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "template": "entrega_pedido_documento", "language": "pt_BR", "variable_names": [ "pedido", "valor" ], "variables": [ "8421", "R$ 495,75" ], "header_media": { "type": "document", "link": "https://arquivos.suaempresa.com/bilhetes/8421.pdf", "filename": "Bilhete 8421.pdf" }, "button_url_param": "8421", "button_url_index": 0, "client_ref": "pedido-8421" }' ``` > A Meta precisa acessar o link sem uma sessão de navegador. Use HTTPS e mantenha o arquivo disponível durante o envio. A amostra da aprovação não substitui o documento do pedido; para enviar sem PDF, selecione um template sem esse cabeçalho. ## Enviar um código de autenticação Use um modelo `AUTHENTICATION` com botão OTP. A API preenche o corpo e o botão nativo com o mesmo `otp_code`. O índice suportado é `0`. cURL · autenticação ```bash curl -X POST https://api.patrociniotech.com/public/v1/messages \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: verificacao-operacao-1234-v1" \ -d '{ "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "template": "codigo_verificacao", "language": "pt_BR", "otp_code": "123456", "otp_index": 0, "client_ref": "operacao-1234" }' ``` > Atenção: Gere o código no seu servidor e valide prazo, destinatário, operação, uso único e tentativas. O texto de expiração do template não invalida o código nem apaga a mensagem. Veja [validade do código e prazo de entrega](https://patrociniotech.com/documentacao-api/templates.md). ## Referência dos campos Envie somente os campos documentados. Nomes desconhecidos, inclusive dentro de `header_media` e `pix_order`, retornam `422 invalid_request`. Os detalhes indicam o campo a corrigir; ele não é ignorado para prosseguir com valores padrão. ### Destinatário e conteúdo | Campo | Regra | | --- | --- | | to | Obrigatório. Telefone internacional, com DDI e DDD, somente dígitos. Ex.: 5521999999999 (Brasil) ou 351912345678 (Portugal). O envio não acrescenta o país automaticamente. | | phone\_number\_id | ID do seu número conectado. Sem ele, a API usa o número padrão da conta. | | delivery\_api | Opcional: cloud (padrão) ou marketing. marketing exige template da categoria MARKETING e ativação na Meta; não aceita texto livre, UTILITY ou AUTHENTICATION. | | text | Texto livre de até 4.096 caracteres. Exige janela aberta e não pode ser combinado com campos de template. | | template | Nome do modelo aprovado. Use em vez de text. | | language | Idioma do modelo. Padrão: pt\_BR. | | reply\_to\_message\_id | Opcional. ID da mensagem recebida que será citada na resposta. | ### Variáveis e cabeçalho | Campo | Regra | | --- | --- | | variables | Um valor por variável do corpo. Até 20 valores de 1.024 caracteres; a quantidade exigida vem do modelo. | | variable\_names | Nomes associados aos valores em variables, para modelos NAMED. Use a ordem da listagem ou mantenha cada par nome/valor correspondente. | | header\_media | Obrigatório se o modelo exigir mídia. Objeto com type (document, image ou video), link HTTP(S) válido, sem usuário/senha ou espaços, e filename opcional para documento. | | header\_text | Até 200 caracteres. Obrigatório se o cabeçalho de texto tiver uma variável; omita para cabeçalho fixo ou de mídia. | ### Botões e autenticação | Campo | Regra | | --- | --- | | button\_url\_param | Valor da parte variável de um botão de link. Até 200 caracteres; a API aceita um link dinâmico por template. | | button\_url\_index | Posição do botão de link na lista, começando em zero. Padrão: 0. | | coupon\_code / coupon\_index | Código de até 15 caracteres e posição do botão de cupom COPY\_CODE. Não use para autenticação. | | otp\_code | Código de autenticação de até 15 caracteres, sem espaços ou caracteres de controle. variables pode ser omitido. | | otp\_index | Para autenticação, use 0 ou omita. Não combine OTP com anexo, cupom, link dinâmico ou PIX. | | pix\_order | Pedido para um template de cobrança, com botão ORDER\_DETAILS. Veja o exemplo abaixo. | Valores de variáveis não aceitam quebras de linha, tabulações ou mais de quatro espaços consecutivos. Nenhum valor é cortado para caber: a API informa o erro para você corrigir. ## Marketing, consentimento e saída da lista A API permite enviar um template `MARKETING` a um contato com aceite `pending` ou sem cadastro local. Isso não registra consentimento como `accepted`. Conserve no seu sistema a origem da lista e a evidência de que a pessoa espera essa comunicação. Uma recusa de promoções em qualquer número da mesma empresa, ou um bloqueio no número de envio, impede novos envios de marketing com `422 marketing_not_allowed`. Não há parâmetro para ignorar essa exclusão. A consulta de [janela e preferência](https://patrociniotech.com/documentacao-api/janela.md) permite conferir o bloqueio antes; a API verifica novamente ao enviar. > Nas campanhas do painel, incluir contatos com aceite pendente exige confirmação de risco. Essa opção não transforma pendência em aceite e não inclui quem pediu para sair. A API de envio individual não recebe essa opção de campanha. Uma saída de promoções não impede um aviso legítimo de pedido com template `UTILITY` ou um código solicitado com `AUTHENTICATION`. Use a categoria correspondente ao conteúdo; mudar a categoria não é uma forma de reenviar a promoção recusada. ## Relacionar o envio ao seu pedido `client_ref` é opcional, tem até 64 caracteres e volta nos eventos de status. Use um identificador único da operação, como `pedido-8421-entrega-v1`, para consultá-la depois. Ele ajuda na conciliação, mas não substitui a `Idempotency-Key` nem impede envios duplicados. O envio não impõe unicidade de `client_ref`. Garanta essa regra no seu outbox por empresa: bilhete, reembolso e aviso do mesmo pedido são operações diferentes. Uma referência reutilizada pode retornar `409 ambiguous_client_ref` na consulta. Veja [o que persistir e como validar a integração](https://patrociniotech.com/documentacao-api/validacao.md). Guarde também `message_id` e `phone_number_id`: um pedido pode ter várias mensagens e mais de um número de envio. ## Cobrança PIX Disponível para template de detalhes do pedido e conta com pagamentos habilitados na Meta. Os valores são inteiros JSON em centavos; strings, decimais e booleanos são recusados. A API confere a soma dos itens e da taxa, sem arredondar valores. ### Ver exemplo de cobrança e campos de pagamento cURL · cobrança PIX ```bash curl -X POST https://api.patrociniotech.com/public/v1/messages \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: cobranca-pedido-8421-v1" \ -d '{ "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "template": "cobranca_emissao", "language": "pt_BR", "pix_order": { "reference_id": "PED-8421", "pix_code": "CODIGO_PIX_COMPLETO_GERADO_PELO_SEU_PSP", "merchant_name": "Sua Empresa", "key": "CHAVE_PIX_DA_EMPRESA", "key_type": "EVP", "items": [ { "name": "Emissão de passagem", "amount": 49575, "quantity": 1 } ], "tax": 0 }, "client_ref": "pedido-8421" }' ``` | Em pix\_order | Uso | | --- | --- | | reference\_id | Referência da cobrança no seu sistema. Até 64 caracteres. | | pix\_code | Copia e cola completo gerado pelo seu PSP para esta cobrança. Não use o texto ilustrativo acima. | | merchant\_name / key / key\_type | Nome do recebedor, chave PIX e tipo: CNPJ, CPF, EMAIL, PHONE ou EVP. | | items | Até 30 itens com name, amount unitário em centavos e quantity. | | tax / total\_amount | tax é opcional, com padrão 0. Se total\_amount for informado, deve ser igual a Σ(amount × quantity) + tax. | | Limite numérico | Cada valor e o total calculado devem caber em 9.007.199.254.740.991 centavos, limite de inteiro seguro da integração. Isso não substitui os limites da Meta ou do seu PSP. | | goods\_type / retailer\_id | goods\_type: digital-goods (padrão) ou physical-goods. retailer\_id é opcional em cada item; sem ele, a posição identifica o item. | ## Validar um telefone Se o cadastro contém apenas DDD e telefone, informe o país em `country` (ISO de duas letras) nesta consulta antes de enviar. Para um telefone já internacional, use `+` e DDI, ou envie somente os dígitos e omita `country`. Não presuma que todos os clientes usam o DDI 55. ### POST /public/v1/validate cURL · validar formato ```bash curl -X POST https://api.patrociniotech.com/public/v1/validate \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -d '{"to": "(21) 99999-9999", "country": "BR"}' ``` Use o `to` normalizado quando `data.valid` for `true`. Essa consulta valida o formato; não confirma existência no WhatsApp nem autorização para envio. > Atenção: `valid=true` não significa que a mensagem será entregue. Um telefone digitado errado pode ter formato válido. A Cloud API não oferece uma consulta prévia para confirmar se o destinatário tem WhatsApp. Após enviar, guarde `message_id` e `client_ref`. Considere a entrega confirmada apenas ao receber `message.status` com `delivered` ou `read`. O aceite inicial `sent` ainda pode terminar em `failed`. Se chegar `error.reason=recipient_unreachable` com `error.code=131026`, registre a falha e confira o telefone com o cliente por outro canal. Esse código não comprova ausência de WhatsApp. Evite repetições automáticas e variações do número sem confirmação. --- Fonte: https://patrociniotech.com/documentacao-api/mensagens Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Consentimento e saída de promoções Consulte a base em lotes e registre quando e onde a pessoa autorizou o contato. Mantenha a prova original no seu sistema. `marketing_allowed` informa a ausência de bloqueio local para marketing. Esse campo pode ser verdadeiro para um contato desconhecido ou pendente: ele não comprova autorização. Importe o consentimento apenas quando existir uma manifestação real e verificável da pessoa. > `opt_in: accepted` com `opt_in_source: inbound` registra que a pessoa escreveu para atendimento. Não conte esses contatos como “autorizados para campanha”. Nos relatórios, separe origem inbound, declarações de consentimento com prova e recusas. Mesmo uma declaração pela API exige a conferência da prova no sistema de origem e não supera uma saída posterior. ### POST /public/v1/contacts/consent/batch Envie de 1 a 500 telefones internacionais por chamada. O lote consome uma requisição na cota da chave e faz uma leitura do cadastro local, sem consultar a Meta nem enviar mensagens. Números repetidos são removidos preservando a primeira posição. Se algum telefone estiver malformado, o lote inteiro retorna 422; corrija a lista antes de repetir. cURL · consultar consentimentos ```bash curl "https://api.patrociniotech.com/public/v1/contacts/consent/batch" \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ --data '{ "phone_number_id": "123456789012345", "recipients": ["5511999999999", "5521999999999"] }' ``` | Campo | Como usar | | --- | --- | | phone\_number\_id | Número que fará o envio. Se omitido, usamos o número padrão da empresa da chave. | | checked\_at | Horário da consulta. Não é a data do aceite nem uma reserva de permissão para o próximo envio. | | items\[\].known | Existe um cadastro nesse número de envio. False não significa que a pessoa não tem WhatsApp. | | items\[\].opt\_in | accepted, pending, declined ou null se não há cadastro. Descreve o número selecionado, conforme opt\_in\_scope: phone\_number. | | items\[\].opt\_in\_at / opt\_in\_source | Quando e por qual origem o estado local foi registrado. null significa ausência de informação legada. inbound, manual ou import não são, por si só, prova de autorização para uma campanha. | | items\[\].marketing\_allowed | false se houver recusa em qualquer número da empresa ou bloqueio no número consultado. true indica somente ausência dessas restrições locais. | | items\[\].marketing\_block\_reason | opted\_out para saída de promoções; blocked para bloqueio no número consultado; null sem restrição local. | | items\[\].latest\_api\_record | Última declaração recebida pela API para esse número, com data, origem e referência à prova. É histórico: uma saída posterior pode impedir marketing mesmo quando o registro anterior diz accepted. | JSON · contato com saída registrada ```json { "success": true, "data": { "phone_number_id": "123456789012345", "checked_at": "2026-11-06T15:00:00Z", "items": [ { "to": "5511999999999", "known": true, "opt_in": "declined", "opt_in_at": "2026-11-06T14:50:00Z", "opt_in_source": "opt_out", "opt_in_scope": "phone_number", "marketing_allowed": false, "marketing_block_reason": "opted_out", "latest_api_record": null } ] } } ``` > Uma recusa de promoções vale para a empresa inteira. O cadastro de um número pode continuar accepted quando a recusa está em outro; nesse caso marketing\_allowed será false. Verifique esse campo ao preparar a lista e trate também recusas no envio, porque a situação pode mudar depois da consulta. ### PUT /public/v1/contacts/{to}/consent Use esta rota para registrar uma manifestação coletada no site, aplicativo ou outro canal. A chave define a empresa e quem registrou a operação. A API guarda um histórico com a data declarada, a data de registro, a origem e a referência à prova; ela não verifica a autenticidade dessa prova nem substitui sua conservação. cURL · registrar aceite coletado no site ```bash curl -X PUT "https://api.patrociniotech.com/public/v1/contacts/5511999999999/consent" \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ --data '{ "phone_number_id": "123456789012345", "event_id": "consentimento-site-001", "state": "accepted", "occurred_at": "2026-11-06T11:40:00-03:00", "source": "web", "evidence_ref": "consentimentos/001", "notice_version": "marketing-v2" }' ``` | Campo | Regra | | --- | --- | | to | Telefone com DDI e DDD no caminho. Não deduzimos o país nem acrescentamos 55. | | event\_id | Obrigatório, até 128 caracteres: letras ASCII, números, ponto, sublinhado, dois-pontos ou hífen. Identifica uma manifestação na empresa inteira; não reutilize para outra pessoa ou outro conteúdo. | | state | accepted registra aceite no número escolhido. declined registra saída de marketing para a empresa inteira; não impede avisos transacionais legítimos de utilidade ou autenticação. | | occurred\_at | Data real da manifestação, com fuso. Até cinco minutos de tolerância futura. Registramos UTC com precisão de milissegundos. | | source | web, app, form, phone, in\_person ou other. A origem é declarada pela integração. | | evidence\_ref | Obrigatório, até 200 caracteres. Referência à prova no seu sistema. Não envie chaves, documentos ou texto de conversas. | | notice\_version | Opcional, até 100 caracteres. Versão do aviso de consentimento que a pessoa viu. | | idempotent\_replay | true quando o evento já estava registrado com esse conteúdo. A repetição não reaplica o aceite: uma saída ocorrida depois continua respeitada. | | contact | Estado atual do contato após o registro, no mesmo formato dos itens da consulta em lote. Pode refletir uma alteração posterior à manifestação original. | Aceite registrado pela API usa `opt_in_source: api`; a origem detalhada, como web, fica em `latest_api_record.source`. Os registros legados permanecem como estão: não criamos datas ou provas retroativas. Registrar consentimento não envia mensagens nem abre uma janela de atendimento. | Resposta | Como tratar | | --- | --- | | 200 | Manifestação registrada ou repetição reconhecida. Guarde event\_id e confira contact. | | 409 consent\_event\_conflict | O event\_id já identifica outra declaração. Não altere dados de um evento existente; use a identidade correta da nova manifestação. | | 409 consent\_blocked | O contato está bloqueado ou já pediu saída. A API não remove essa restrição. A equipe deve revisar a nova prova e o cadastro no painel. | | 409 stale\_consent | O aceite enviado é anterior ao estado local atual. Não troque a data para contornar a validação; confira a manifestação mais recente. | | 422 invalid\_request | Campo ausente, desconhecido ou inválido. Use error.details.fields para localizar o problema. | > Atenção: Não transforme uma lista importada em accepted para liberar campanhas. Uma revogação posterior deve continuar valendo. Se houver timeout, repita o mesmo event\_id e conteúdo; não crie outra identidade para a mesma manifestação. ## Receber uma saída sem comparar o texto do botão O evento `message.received` mantém os campos anteriores e acrescenta `interaction`, com o tipo, o ID/payload e o título do botão. Quando o Patrocinio reconhece e aplica um pedido de saída, `consent.action` será `marketing_opt_out` e `marketing_allowed` será false. JSON · campos adicionais em message.received.data ```json { "interaction": { "type": "button_reply", "id": "acao-saida-001", "title": "Parar promoções" }, "consent": { "action": "marketing_opt_out", "marketing_allowed": false, "scope": "tenant", "changed": true } } ``` Para mensagens sem ação de consentimento, `consent` é null; isso não significa aceite. Um pedido repetido pode ter `changed: false`, mantendo a pessoa fora das promoções. `interaction.id` preserva o valor recebido da Meta; ele não altera o consentimento sozinho. Também é possível assinar `contact.marketing_opt_out` em Integrações. Ele é emitido quando o estado muda, tanto por pedido reconhecido no WhatsApp quanto por uma revogação registrada nesta API. Inclui `to`, `phone_number_id`, `source`, `occurred_at`, `scope: tenant` e `marketing_allowed: false`. No WhatsApp, `message_id` identifica o pedido; pela API, `event_id` identifica a declaração e message\_id é null. O evento e o estado são gravados na mesma transação. A entrega HTTP pode ser repetida ou chegar fora de ordem: valide a assinatura e deduplique por [delivery\_id](https://patrociniotech.com/documentacao-api/webhook.md). Não reative marketing ao receber uma mensagem comum ou uma entrega antiga de aceite. --- Fonte: https://patrociniotech.com/documentacao-api/consentimento Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Resultados de campanhas Acompanhe os disparos feitos no painel e consulte a entrega de cada mensagem, sem enviar outra campanha. A chave Bearer permite consultar somente as campanhas da sua empresa. Esta consulta lê os registros locais, atualizados pelos envios e pelos webhooks da Meta. Ela não consulta a Meta em tempo real. ## Encontrar a campanha ### GET /public/v1/campaigns cURL · listar campanhas ```bash curl "https://api.patrociniotech.com/public/v1/campaigns?limit=20" \ -H "Authorization: Bearer SUA_CHAVE" ``` | Parâmetro | Regra | | --- | --- | | limit | De 1 a 100. Padrão: 20 campanhas por página, da mais recente para a mais antiga. | | before\_id | Na próxima página, use data.next\_before\_id da resposta anterior. Omita na primeira consulta. | | data.items | Campanhas, com id, nome, número, template, idioma, agendamento e progresso do disparo. | | data.has\_more | true quando há outra página. next\_before\_id fica null quando a lista termina. | O `id` é o mesmo usado na URL de detalhes do painel. `status: done` significa que o processamento do disparo acabou. Ainda podem chegar confirmações de entrega, leitura e falha. O estado `paused` suspende novas tentativas da campanha. Uma requisição já em andamento pode terminar, e os webhooks de envios anteriores continuam atualizando os resultados. Pausar não cancela uma mensagem que a Meta já aceitou. `cancelled` encerra a fila por ação do usuário e não permite retomada dessa campanha. Os destinatários ainda sem tentativa passam a `not_sent` nos resultados; uma tentativa em andamento sem confirmação permanece `uncertain`. Mensagens já aceitas continuam recebendo atualizações de entrega e leitura. ## Resultados e destinatários ### GET /public/v1/campaigns/{campaign_id}/results cURL · consultar falhas ```bash curl "https://api.patrociniotech.com/public/v1/campaigns/302/results?status=failed&limit=50" \ -H "Authorization: Bearer SUA_CHAVE" ``` | Parâmetro | Regra | | --- | --- | | campaign\_id | Identificador positivo da campanha. Campanha inexistente ou de outra empresa retorna 404/not\_found. | | limit | De 1 a 200 destinatários por página. Padrão: 50. | | status | all, pending, sending, sent, delivered, read, failed, skipped, uncertain, not\_sent ou opted\_out. Padrão: all. | | after | Copie data.next\_after da resposta anterior e mantenha o mesmo filtro. Omita na primeira página; não altere ou incremente o cursor. | JSON · exemplo ilustrativo ```json { "success": true, "data": { "campaign": { "id": 302, "name": "Oferta de novembro", "phone_number_id": "123456789012345", "template": "oferta_novembro", "language": "pt_BR", "delivery_api": "cloud", "status": "done", "total": 3, "accepted": 3, "dispatch_failed": 0, "created_at": "2026-11-06T12:00:00Z", "scheduled_at": null, "started_at": "2026-11-06T12:00:00Z", "finished_at": "2026-11-06T12:00:03Z" }, "counts": { "pending": 0, "sending": 0, "sent": 0, "delivered": 1, "read": 1, "failed": 1, "skipped": 0, "uncertain": 0, "not_sent": 0, "opted_out": 1 }, "delivered_including_read": 2, "records": 3, "untracked": 0, "complete_tracking": true, "total_filtered": 1, "items": [ { "to": "5511900000000", "name": null, "contact_wa_id": "5511900000000", "message_id": "wamid.exemplo", "dispatch_status": "sent", "status": "failed", "delivery_api": "cloud", "delivery_api_confirmed": null, "error_code": "marketing_recipient_limited", "reason": "Marketing limitado pela Meta para este destinatário", "attempted_at": "2026-11-06T12:00:01Z", "status_at": null, "pricing_category": null, "billable": null, "opted_out_at": null } ], "has_more": false, "next_after": null, "snapshot_at": "2026-11-06T12:05:00Z" } } ``` ## Como ler os totais | Campo | Interpretação | | --- | --- | | campaign.total | Destinatários selecionados para o disparo. Não significa que receberam a mensagem. | | campaign.delivery\_api | API selecionada para esta campanha: cloud ou marketing. A segunda opção exige template MARKETING e ativação Meta; não há fallback automático entre as rotas. | | campaign.accepted | Envios aceitos pela API da Meta. Uma mensagem aceita ainda pode falhar depois. | | campaign.dispatch\_failed | Contador histórico de ocorrências durante o disparo. Pode incluir exclusões e tentativas sem confirmação; não é o total de falhas definitivas. Use counts e o status de cada destinatário para decidir o tratamento. | | campaign.paused\_at / pause\_reason | Horário UTC e motivo da pausa local. manual\_pause: ação no painel; worker\_interrupted: processamento interrompido; template\_paused, template\_not\_found ou template\_not\_approved: disponibilidade do modelo; template\_params\_mismatch, template\_param\_format ou invalid\_template: parâmetros ou formato recusados; template\_catalog\_unavailable ou template\_category\_unavailable: não foi possível conferir o catálogo ou a categoria, e a fila foi preservada; meta\_template\_paused/disabled/rejected: evento da Meta. null quando não informado ou não reconhecido. | | counts.delivered / counts.read | Estados separados: delivered não inclui read. Para o total de entregas, use delivered\_including\_read. | | counts.opted\_out | Pessoas que pediram para parar promoções e cuja saída foi vinculada à campanha. Sobrepõe a entrega; não some esse valor aos demais estados. | | counts.sent | Aceitas, ainda sem entrega, leitura ou falha posterior registrada. Não considere entregues nem falhadas. | | counts.uncertain | Tentativas sem confirmação de aceite nem de recusa. Não entram em counts.failed e não devem ser reenviadas automaticamente. | | counts.not\_sent | Destinatários para os quais não há tentativa registrada após o encerramento ou a interrupção do disparo. Revise o motivo e a elegibilidade antes de preparar outra campanha. | | records / untracked | records conta destinatários rastreados, inclusive excluídos. Pode superar campaign.total quando a trilha mantém exclusões da importação ou da revisão do público agendado. untracked é a diferença positiva para o público selecionado. Ausência de registro não confirma falha. | | total\_filtered | Total que corresponde ao filtro em toda a campanha. Não é o tamanho da página nem o número restante depois do cursor. | > Os totais são da campanha inteira e não mudam conforme a página ou o filtro. Em históricos com rastreamento incompleto, `complete_tracking` é false. Nesse caso, não trate os registros ausentes como entregas, falhas ou custo zero. ## O resultado de cada pessoa | Campo | Interpretação | | --- | --- | | to / contact\_wa\_id | to é o telefone solicitado no disparo. contact\_wa\_id é o identificador confirmado pela Meta quando disponível; ele pode vir normalizado. | | message\_id | WAMID aceito pela Meta. Pode ser usado na consulta de status da mensagem. null quando não há aceite registrado; isso não comprova que a Meta recusou o envio. | | dispatch\_status / status | dispatch\_status registra a tentativa local; status considera a confirmação posterior de entrega. Um disparo sent pode terminar com status failed. | | delivery\_api | API selecionada para a mensagem: cloud ou marketing; null em registros sem essa informação. A escolha da campanha não preenche retroativamente a evidência de cada mensagem. | | delivery\_api\_confirmed | Rota comprovada pela classificação do webhook Meta: marketing\_lite confirma marketing; marketing confirma cloud. null não comprova falha nem permite inferir a rota utilizada. | | error\_code / reason | Código estável e explicação da falha conhecida. Programe contra error\_code; reason é texto para leitura. null indica informação não registrada. | | attempted\_at / status\_at | Horário da tentativa e da última atualização disponível na trilha. status\_at não garante o instante exato de entrega. | | pricing\_category / billable | Categoria e indicação de cobrança recebidas da Meta. null significa não informado. Esta rota não calcula o valor da fatura. | | opted\_out\_at | Horário da saída de promoções atribuída à campanha. A pessoa ainda pode aparecer com status read ou delivered. | `marketing_recipient_limited` indica limitação de marketing para aquele destinatário. `marketing_experiment` indica um teste de entrega da Meta. Esses motivos não confirmam banimento do remetente e não justificam reenviar automaticamente. ## API de entrega da campanha Na criação da campanha pelo painel, a escolha vale para o disparo: `cloud` é o padrão e `marketing` usa a Marketing Messages API da Meta somente com template `MARKETING`. A consulta pública informa a escolha em `campaign.delivery_api`. A API pública de campanhas continua sendo de leitura; para enviar uma mensagem pela integração, use [POST /messages com delivery\_api](https://patrociniotech.com/documentacao-api/mensagens.md). Os cuidados de consentimento, bloqueios, qualidade e disponibilidade do modelo são os mesmos. A rota de marketing usa política `STRICT`; uma recusa não faz a campanha mudar silenciosamente para Cloud API. Alterar a API de entrega não é uma forma de contornar quem pediu para sair ou uma limitação da Meta. Separe a seleção `campaign.delivery_api` da evidência `items[].delivery_api_confirmed`. A confirmação depende da classificação recebida no webhook, pode chegar depois e pode permanecer nula no histórico. O estado de entrega continua sendo informado em `status`. ## Comparar resultados das duas opções Separe públicos equivalentes e sem sobreposição; não envie a mesma promoção duas vezes para a mesma pessoa só para comparar rotas. Use o mesmo conteúdo, critérios de seleção, período e tempo de observação após o envio. Registre a opção escolhida e mantenha aceites, entregas, falhas, incertezas e saídas separados. | Medida | Como comparar | | --- | --- | | Entrega | Use delivered\_including\_read. No mundo real, mensagens lidas também foram entregues; counts.delivered e counts.read são separados apenas na resposta da API. | | Leitura | Compare counts.read com o mesmo denominador em ambos os grupos. Não some leituras novamente ao total que já inclui read. | | Tempo | Use o mesmo intervalo desde a tentativa. Resultados recém-enviados não são comparáveis a uma campanha que acumulou entregas durante horas. | | Custo | Compare cobrança efetivamente informada pela Meta, na mesma moeda e categoria. Estimativa ou valor ausente não comprova economia. | > A escolha da API não garante aumento de entrega, leitura ou conversão. Sem grupos comparáveis e tempo suficiente, a diferença observada não demonstra que a rota foi a causa. ## Ritmo e previsão de término O painel mostra o ritmo observado das tentativas e uma estimativa para terminar de processar a fila. O tempo depende da resposta da Meta, das pausas e da gravação do resultado; não há uma quantidade fixa garantida por segundo. Entrega e leitura acontecem depois e podem continuar mudando mesmo com o disparo encerrado. > O teto de 600 requisições por minuto da API pública não é a velocidade da campanha nem uma garantia de entrega. Ele controla chamadas autenticadas feitas pela integração. Os limites e as restrições da Meta continuam independentes. ## Pausa e retomada no painel Administradores e supervisores com acesso à gestão de campanhas podem pausar e retomar o processamento pelo painel. Um webhook de template `PAUSED`, `DISABLED` ou `REJECTED` pausa as campanhas em processamento ou agendadas que usam o mesmo modelo, idioma e conta da Meta. Qualidade `RED` gera aviso; não equivale, por si só, a template pausado. A aprovação posterior do template não retoma campanhas automaticamente. A retomada exige ação no painel, template aprovado e elegibilidade atual dos contatos. Ela continua apenas destinatários ainda sem tentativa registrada; pessoas com aceite, falha de tentativa ou resultado incerto ficam fora dessa retomada. Não há prazo fixo de liberação: confira o estado atual informado pela Meta. Se a consulta do catálogo ou da categoria estiver indisponível, a campanha pausa e preserva os destinatários sem tentativa. Essa pausa não comprova rejeição do template nem falha de entrega. Tente retomar pelo painel quando a consulta voltar a funcionar. Os motivos `marketing_api_not_ready` e `cloud_marketing_disabled` pedem revisão da configuração da API escolhida; `delivery_api_template_mismatch` indica incompatibilidade com a categoria. A fila pausa, preserva quem ainda não teve tentativa e não muda de API automaticamente. A retomada mantém a escolha original. Veja [os códigos e como agir](https://patrociniotech.com/documentacao-api/erros.md). > A API pública oferece consulta. Pausar e retomar são ações autenticadas do painel. Não crie outra campanha para substituir uma fila pausada sem antes revisar quem já recebeu uma tentativa. Cancelar pelo painel é definitivo para a fila dessa campanha. A confirmação não apaga o histórico nem recolhe mensagens no WhatsApp. A pausa aplicada pela Meta nunca cancela automaticamente a campanha. ## Exportar e preparar uma nova tentativa Exporte os resultados para investigar o motivo por destinatário. A exportação registra uma fotografia; não autoriza reenviar a lista inteira. Consulte novamente os resultados e o consentimento antes de confirmar uma nova campanha. | Situação | Tratamento | | --- | --- | | pending / sending | Aguarde a tentativa atual. Não abra uma campanha concorrente para essas pessoas. | | sent / delivered / read | Não reenviar: há aceite ou entrega registrada. sent ainda aguarda confirmação posterior. | | uncertain / send\_uncertain | Separar para conciliação. Uma resposta inválida, conexão interrompida ou falta de WAMID não comprova recusa. Não criar outro envio para contornar a incerteza. | | not\_sent | Conferir o encerramento da fila e a elegibilidade atual. Pode compor uma nova campanha após revisão. | | failed | Ler error\_code e corrigir a causa antes de decidir. Limitação de marketing, experimento da Meta e telefone inacessível não são convites para repetir automaticamente. | | opted\_out / skipped | Respeitar a saída ou o motivo da exclusão. Não incluir contatos que recusaram promoções nem remover bloqueios para repetir o disparo. | Reentregar um webhook repete a notificação para o servidor da integração; não envia uma nova mensagem no WhatsApp. Não transforme uma falha de entrega do webhook em uma nova campanha. O evento `message.status` relata um status recebido da Meta e inclui `campaign_id` quando há vínculo com a campanha. Uma tentativa local sem WAMID não gera uma confirmação de falha da Meta. A ausência desse evento também não comprova que nada foi enviado; consulte os resultados e mantenha os casos incertos separados. ## Paginação e atualização Continue enquanto `has_more` for true, usando `next_after` como `after`. A ordem é lexical por telefone e cada resposta lê os registros disponíveis naquele momento. Novos webhooks podem mudar o status entre páginas; para exportar ou reconciliar, guarde pelo par campanha e destinatário, atualize o mesmo registro e faça uma nova leitura depois. A integração não deve chamar esta rota antes de cada envio. Faça consultas periódicas com pausa e respeite a cota compartilhada por chave. Filtros inválidos retornam 422; autenticação inválida retorna 401; excesso de requisições retorna 429. Um 503 é indisponibilidade temporária da consulta, não evidência de que o disparo falhou: aguarde e repita somente o GET. --- Fonte: https://patrociniotech.com/documentacao-api/campanhas Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Templates Consulte aprovação, qualidade e campos de envio dos modelos vinculados ao seu número. ### GET /public/v1/templates cURL · listar templates do número ```bash curl "https://api.patrociniotech.com/public/v1/templates?phone_number_id=SEU_NUMERO_ID" \ -H "Authorization: Bearer SUA_CHAVE" ``` cURL · acompanhar todos os status ```bash curl "https://api.patrociniotech.com/public/v1/templates?phone_number_id=SEU_NUMERO_ID&status=ALL" \ -H "Authorization: Bearer SUA_CHAVE" ``` | Parâmetro | Uso | | --- | --- | | phone\_number\_id | ID do número que fará o envio. Se omitido, a listagem usa a conta WhatsApp principal; informe-o quando houver mais de uma conta vinculada. | | status | Padrão: APPROVED. Use ALL para incluir modelos em análise, rejeitados e pausados. | > Antes de usar o modelo, confira `sendable: true` e `api_supported: true`. Aprovação na Meta e suporte desta API são verificações diferentes. Para um template `MARKETING`, escolha `delivery_api: cloud` ou `delivery_api: marketing` no envio. O padrão permanece Cloud API; Marketing Messages API exige ativação na Meta. Aprovação do modelo, sozinha, não confirma que essa ativação foi concluída. `UTILITY` e `AUTHENTICATION` continuam em Cloud API. Veja [como escolher a API de entrega](https://patrociniotech.com/documentacao-api/mensagens.md). A consulta usa cache de até 90 segundos. Ela não é uma confirmação de entrega: o status pode mudar após a consulta e o envio continua sujeito às regras da Meta. Se a consulta falhar ou não puder trazer todas as páginas dentro do limite, retorna `503 templates_unavailable`; uma falha não vira catálogo vazio. Reaproveite o catálogo por conta e número, atualizando periodicamente ou após uma recusa relacionada ao modelo. Consultá-lo antes de cada mensagem consome a mesma cota de 600 requisições por minuto por chave usada pelos envios. > O histórico preserva até dez botões na ordem do modelo, incluindo ações e respostas rápidas. Isso não amplia as combinações aceitas no envio: confira `api_supported`, os índices e os parâmetros exigidos pelo catálogo. Se um modelo for editado depois, a recuperação de um novo envio usa o texto e os botões guardados no aceite. Histórico antigo sem essa cópia não é reconstruído com conteúdo atual. ## Como ler o modelo Para cadastrar um modelo pela integração, use a seção [Criar template](https://patrociniotech.com/documentacao-api/templates.md) nesta página. O catálogo continua sendo a fonte para conferir a aprovação antes de enviar. JSON · um item de data.templates ```json { "id": "123456789012345", "name": "entrega_pedido_documento", "language": "pt_BR", "category": "UTILITY", "status": "APPROVED", "sendable": true, "api_supported": true, "api_unsupported_reason": null, "quality_score": { "score": "UNKNOWN", "date": null, "reasons": [] }, "rejected_reason": null, "message_send_ttl_seconds": null, "parameter_format": "NAMED", "header": { "format": "DOCUMENT", "needs_media": true, "needs_text": false }, "body": { "text": "Seu pedido {{pedido}} está disponível. Valor: {{valor}}. Consulte os detalhes.", "variables_count": 2, "variable_names": ["pedido", "valor"] }, "buttons": [{ "index": 0, "type": "URL", "text": "Ver pedido", "url": "https://suaempresa.com/pedidos/{{1}}", "needs_url_param": true, "needs_coupon": false, "needs_otp": false }] } ``` A listagem retorna esses itens em `data.templates`. O exemplo mostra os campos usados para preparar o envio; a resposta também inclui rodapé e componentes originais da Meta. | Campo | Como interpretar | | --- | --- | | id | Identificador do modelo na Meta. Nome e idioma também identificam a versão usada no envio. | | status | Situação informada pela Meta. PENDING está em análise; APPROVED indica aprovação. | | sendable | true somente quando status é APPROVED. Não garante que esta API suporte todos os componentes nem que o envio será entregue. | | api\_supported | Indica se esta API sabe preencher o formato. Quando false, api\_unsupported\_reason explica a limitação. | | quality\_score | Objeto com score, date e reasons informados pela Meta, ou null quando indisponível. Qualidade não é aprovação. | | rejected\_reason | Motivo de rejeição fornecido pela Meta, ou null. A ausência de motivo não significa aprovação. | | message\_send\_ttl\_seconds | Prazo de entrega configurado no modelo, em segundos, quando informado pela Meta. Não é a validade de um código na sua aplicação. | ## Preencher as variáveis | Formato | Como preencher | | --- | --- | | NAMED | Associe cada nome de `body.variable_names` ao valor correspondente. O exemplo abaixo usa a ordem retornada. | | POSITIONAL | Envie `variables` na ordem de `{{1}}`, `{{2}}` e seguintes. Omita `variable_names`. | JSON · trecho do envio com variáveis nomeadas ```json { "template": "entrega_pedido_documento", "language": "pt_BR", "variable_names": ["pedido", "valor"], "variables": ["8421", "R$ 495,75"] } ``` Use a quantidade de `body.variables_count`. Uma variável repetida no texto recebe o mesmo valor; não envie um valor extra por repetição. > O texto fixo pode ter quebras de linha. Uma variável não pode conter quebras, tabulações ou mais de quatro espaços consecutivos. Para uma lista, use `AD: A798R5 • B6: MBSERQ`; o WhatsApp ajusta a quebra visual à largura da tela. ## Campos exigidos pelo modelo | Quando a resposta informa | Preencha no envio | | --- | --- | | header.needs\_media: true | O objeto `header_media`, com tipo compatível com o cabeçalho e link do arquivo. | | header.needs\_text: true | `header_text` | | buttons\[\].needs\_url\_param: true | `button_url_param` e `button_url_index`, conforme `buttons[].index`. | | buttons\[\].needs\_coupon: true | `coupon_code` e `coupon_index`. Este é o botão de cupom, não de autenticação. | | buttons\[\].needs\_otp: true | `otp_code`; `otp_index` deve ser 0 ou omitido. | | footer | Não é um parâmetro de envio. footer.text mostra o rodapé aprovado; quando não houver rodapé, footer será null. | Consulte [todos os campos de POST /messages](https://patrociniotech.com/documentacao-api/mensagens.md) para limites de tamanho e exemplos completos. ## Modelos com PDF e sem PDF Um cabeçalho `DOCUMENT` exige o documento em cada envio. A amostra cadastrada na aprovação não é enviada automaticamente. Sem PDF, use um modelo aprovado sem esse cabeçalho. JSON · campos do documento no envio ```json { "header_media": { "type": "document", "link": "https://arquivos.suaempresa.com/bilhetes/8421.pdf", "filename": "Bilhete 8421.pdf" } } ``` Use uma URL HTTPS acessível pela Meta sem sessão de navegador e disponível durante o envio. Veja o [exemplo completo com PDF](https://patrociniotech.com/documentacao-api/mensagens.md). ## Código de verificação e expiração Para confirmar acesso ou uma ação, como saque, use `AUTHENTICATION` com botão OTP. No painel de Templates, o formato disponível é **Copiar código**. WhatsApp · prévia ilustrativa ```text Seu código de verificação é *123456*. Para sua segurança, não o compartilhe. Expira em 10 minutos. [Copiar código] ``` | Controle | O que faz | | --- | --- | | Prazo no texto | Informa a validade do código ao destinatário. Não executa a validação da sua operação. | | Validade no seu servidor | Impede usar o código vencido ou já utilizado. Associe-o à pessoa e à operação e limite as tentativas. | | Prazo de entrega da Meta | Limita as tentativas de entregar a mensagem. Não apaga ou edita uma mensagem já recebida. | > Atenção: A Patrocinio Tech transporta o código. Seu sistema é responsável por gerar, expirar e validar o código antes de autorizar o saque ou outra ação. Não dependa da aparência do botão para decidir se o código ainda vale. Veja [como enviar o código pela API](https://patrociniotech.com/documentacao-api/mensagens.md) e a referência da Meta para [templates com Copiar código](https://developers.facebook.com/documentation/business-messaging/whatsapp/templates/authentication-templates/copy-code-button-authentication-templates). ### E o preenchimento automático com um toque? A Meta documenta a desativação do botão após o prazo no formato de [preenchimento automático com um toque](https://developers.facebook.com/documentation/business-messaging/whatsapp/templates/authentication-templates/autofill-button-authentication-templates/), que exige integração com aplicativo Android. Esse formato é diferente do botão Copiar código criado aqui. ## Aprovação e mudanças de status | Status da Meta | Próximo passo | | --- | --- | | APPROVED | Confira api\_supported e preencha os campos exigidos. | | PENDING | Aguarde a análise da Meta. | | REJECTED | Consulte o motivo da rejeição no painel de Templates. | | PAUSED / DISABLED | Confira a qualidade, o motivo e as opções de reativação na Meta. | | IN\_APPEAL | Aguarde o resultado da contestação na Meta. | | PENDING\_DELETION / DELETED / ARCHIVED | Não use o modelo para novos envios. | | LIMIT\_EXCEEDED | Confira o limite indicado pela Meta antes de tentar novamente. | A categoria e a aprovação pertencem à Meta e podem mudar. Reconsulte a listagem se um modelo antes disponível passar a falhar. O filtro `status` aceita esses códigos ou `ALL`, sem distinguir maiúsculas de minúsculas. Um filtro vazio ou desconhecido retorna `400 invalid_template_status`. ## Qualidade do modelo | quality\_score.score | Significado | | --- | --- | | GREEN | Qualidade alta informada pela Meta. | | YELLOW | Qualidade média; acompanhe o desempenho e o conteúdo. | | RED | Qualidade baixa; revise conteúdo e público antes de ampliar os envios. | | UNKNOWN / null | Sem avaliação disponível. Não trate como qualidade alta nem como rejeição. | `quality_score.date` é o horário Unix, em segundos, informado pela Meta para a avaliação, quando disponível. Não é o horário desta consulta. `reasons` preserva as informações de motivo que a Meta disponibilizar. Códigos futuros devem aparecer como desconhecidos na sua interface, sem interromper a leitura do catálogo. > Habilite `template.status` e `template.quality` em Integrações para receber as mudanças enviadas pela Meta. As assinaturas existentes são preservadas. Consulte também o catálogo com `status=ALL`: eventos podem chegar fora de ordem, e uma consulta reconcilia o estado atual. ## Formatos disponíveis nesta API `api_supported: false` vem acompanhado de `api_unsupported_reason`, uma explicação em português. A aprovação da Meta, indicada por `sendable`, não substitui essa verificação. | Recurso | Suporte | | --- | --- | | Texto, imagem, vídeo e documento | Disponíveis conforme o cabeçalho aprovado. | | Link variável | Um botão de link dinâmico por template. | | Autenticação | Um botão OTP, na posição 0, sem cabeçalho. | | Cobrança PIX | Template de detalhes do pedido com um botão ORDER\_DETAILS na posição 0. | | Carrossel, oferta por tempo limitado, localização, GIF e Flow | Indisponíveis neste envio. A tentativa é recusada com template\_format\_unsupported. | O campo `components` permite inspecionar os dados originais da Meta; ele não amplia os formatos aceitos. Novidades como Direct Send dependem de suporte específico na API. Acompanhe [as mudanças desta integração](https://patrociniotech.com/documentacao-api/mudancas.md). ## Criar template ### POST /public/v1/templates A chave da integração permite submeter modelos da própria empresa. Informe sempre o número que seleciona a conta WhatsApp. A validação é a mesma do painel; nome e idioma identificam o modelo na Meta. Criar não envia mensagens e não garante aprovação. JSON · modelo de utilidade ```json { "phone_number_id": "SEU_NUMERO_ID", "name": "pedido_recebido", "language": "pt_BR", "category": "UTILITY", "body": "Recebemos seu pedido {{pedido}}. Aguarde nossa confirmação.", "body_examples": { "pedido": "DEMO-1" }, "footer": "Acompanhe pelo nosso site.", "buttons": [ { "type": "URL", "text": "Ver pedidos", "url": "https://suaempresa.com/pedidos" } ] } ``` | Campo | Regra | | --- | --- | | name / language / category | Nome com letras minúsculas, números e sublinhado, até 60 caracteres. Idioma como pt\_BR. Categoria UTILITY, MARKETING ou AUTHENTICATION. | | body / body\_examples | Corpo até 1.024 caracteres. Variáveis numeradas consecutivas ou nomeadas, sem misturar; um exemplo fictício por variável. | | header / footer | Texto fixo de até 60 caracteres cada. Sem variáveis. Não combine cabeçalho de texto com mídia. | | header\_format / header\_handle | IMAGE, VIDEO ou DOCUMENT exigem o handle de amostra devolvido pelo upload abaixo. TEXT usa header. | | buttons | Até 10, respeitando as combinações suportadas: QUICK\_REPLY, URL, PHONE\_NUMBER, COPY\_CODE e ORDER\_DETAILS. A criação não amplia o suporte do envio. | | code\_expiration\_minutes | Autenticação: de 1 a 90 minutos, padrão 10. Corpo e botão Copiar código são definidos pela Meta; não envie body, footer ou buttons personalizados. | | message\_send\_ttl\_seconds | Prazo para entrega da mensagem. Na autenticação, 30 a 900 segundos, sem ultrapassar a validade do código; padrão até 600 segundos. Expire o código no seu backend também. | JSON · aceite da criação ```json { "success": true, "data": { "name": "pedido_recebido", "id": "123456789012345", "status": "PENDING", "category": "UTILITY" } } ``` > Atenção: Em timeout ou `503 template_creation_unconfirmed`, o resultado é incerto. Consulte o catálogo pelo mesmo nome e idioma antes de repetir. Não gere nomes novos automaticamente; `name_taken` pede conferir o modelo existente. Esta rota não usa a Idempotency-Key do envio de mensagens. ### POST /public/v1/templates/header-sample Envie `multipart/form-data` com `phone_number_id` e `file`. JPEG/PNG até 5 MB; MP4/3GPP/PDF até 16 MB. O tipo e a assinatura do arquivo são validados. A resposta contém `data.handle` e `data.format`; use-os na criação. O handle não é uma URL nem o media\_id usado ao enviar mensagens. Use arquivos fictícios como amostra. cURL · amostra de PDF ```bash curl "https://api.patrociniotech.com/public/v1/templates/header-sample" \ -H "Authorization: Bearer SUA_CHAVE" \ -F "phone_number_id=SEU_NUMERO_ID" \ -F "file=@exemplo.pdf;type=application/pdf" ``` --- Fonte: https://patrociniotech.com/documentacao-api/templates Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Do envio à confirmação de entrega Um aviso de pedido com referência própria, proteção contra duplicação e atualização de status pelo webhook. > Os dados e nomes de template são ilustrativos. As funções de banco e fila representam operações que você implementa no seu sistema; não são endpoints da Patrocinio Tech. ## O fluxo em quatro etapas | Etapa | Resultado | | --- | --- | | [1. Preparar](https://patrociniotech.com/documentacao-api/exemplo.md) | Telefone normalizado, template compatível e operação gravada antes do envio. | | [2. Enviar](https://patrociniotech.com/documentacao-api/exemplo.md) | Aceite da Meta associado ao seu pedido e ao message\_id. | | [3. Receber eventos](https://patrociniotech.com/documentacao-api/exemplo.md) | Status confirmado no seu banco, sem aplicar eventos repetidos. | | [4. Recuperar eventos](https://patrociniotech.com/documentacao-api/exemplo.md) | Entregas do webhook que falharam reenfileiradas após corrigir o receptor. | ## 1. Prepare a operação Consulte [o template do número de envio](https://patrociniotech.com/documentacao-api/templates.md). Confira o consentimento do contato e salve sua operação com uma chave única antes de chamar a API. | No seu registro | Exemplo | | --- | --- | | Referência da operação | `pedido-8421-aviso-entrega` | | Empresa, conexão e pedido | Vínculo local com o pedido 8421. Reembolso e outros avisos terão operações próprias. | | Chave de idempotência | `pedido-8421-aviso-entrega-v1` | | Dados de envio | Destinatário solicitado, phone\_number\_id, delivery\_api, template, idioma e variáveis. | | Estado inicial | Pendente. Registre o resultado de cada tentativa sem substituir a chave original. | ### Normalizar o telefone antes de enviar cURL · validar telefone ```bash curl -X POST https://api.patrociniotech.com/public/v1/validate \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -d '{"to": "(21) 99999-9999", "country": "BR"}' ``` Quando `data.valid` for `true`, use o telefone de `data.to`. Em caso contrário, corrija o cadastro. A validação confirma o formato, não a existência no WhatsApp. ## 2. Envie e registre o aceite O modelo abaixo usa duas variáveis numeradas: nome e pedido. Mantenha o mesmo corpo e a mesma chave ao repetir esta operação. cURL · enviar aviso ```bash curl -X POST https://api.patrociniotech.com/public/v1/messages \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: pedido-8421-aviso-entrega-v1" \ -d '{ "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "delivery_api": "cloud", "template": "aviso_entrega", "language": "pt_BR", "variables": ["Maria", "8421"], "client_ref": "pedido-8421-aviso-entrega" }' ``` JSON · envio aceito ```json { "success": true, "data": { "message_id": "wamid.EXEMPLO", "status": "sent", "to": "5521999999999", "client_ref": "pedido-8421-aviso-entrega", "delivery_api": "cloud" } } ``` Associe `message_id` à operação gravada. `client_ref` volta no webhook para ajudar na conciliação, mas não substitui a chave de idempotência. A chave é opcional no HTTP e recomendada para automações. O servidor não impõe unicidade de `client_ref`; garanta isso no outbox por empresa. Preserve `data.to` junto ao destinatário solicitado. Uma resposta com `idempotent_replay: true` contém o aceite anterior, não o estado atual de entrega. > `sent` significa aceite da Meta. O relatório só deve contar como entregue após o evento correspondente. Em falha de conexão ou resultado incerto, preserve a operação e use a [política de novas tentativas](https://patrociniotech.com/documentacao-api/erros.md). Para uma fila de envios, aplique o limite de 600 requisições por minuto por chave ao conjunto dos workers, incluindo validações e consultas. Somar vários processos com limites separados pode ultrapassar o teto. Reserve margem e pause a fila da chave pelo `Retry-After` quando receber 429. Se o webhook não atualizar uma operação que já tem `message_id`, confira [GET /messages/{message\_id}/status](https://patrociniotech.com/documentacao-api/mensagens.md). A consulta não reenvia a mensagem. Preserve o estado confirmado mais avançado e não transforme `unknown` ou 404 em uma nova tentativa de envio. ## 3. Receba e processe o webhook Configure o endpoint em Integrações. Verifique a [assinatura sobre o corpo original](https://patrociniotech.com/documentacao-api/webhook.md), grave o evento em uma fila durável e só então responda 200. JSON · confirmação de entrega ```json { "event": "message.status", "occurred_at": "2026-10-06T14:02:11Z", "delivery_id": "8814", "data": { "message_id": "wamid.EXEMPLO", "status": "delivered", "status_rank": 3, "to": "5521999999999", "phone_number_id": "SEU_NUMERO_ID", "client_ref": "pedido-8421-aviso-entrega", "campaign_id": null, "delivery_api": "cloud", "delivery_api_confirmed": null, "error": null } } ``` Node.js · recepção com fila durável ```javascript // Configure req.rawBody e assinaturaValida conforme a página Webhook. app.post("/webhooks/patrociniotech", async (req, res) => { if (!assinaturaValida(req, process.env.PT_WEBHOOK_SECRET)) return res.sendStatus(401); if (req.body?.data?.test === true) return res.sendStatus(200); const { delivery_id, event } = req.body; const eventos = ["message.status", "message.received", "contact.marketing_opt_out", "template.status", "template.quality"]; if (!delivery_id || !eventos.includes(event)) { return res.sendStatus(400); } try { await inserirEventoSeNovo({ integracao: conexao.id, // conexão identificada no servidor e validada pela assinatura id: delivery_id, // índice único: integração + id corpo: req.body, }); return res.sendStatus(200); // inclui duplicata já gravada } catch { return res.sendStatus(503); // a gravação não foi confirmada } }); ``` Node.js · processamento da fila ```javascript async function processarEvento(evento) { if (evento.corpo?.data?.test === true) return; // defesa adicional para testes antigos na fila await transacao(async (tx) => { const item = await tx.bloquearEvento(evento.integracao, evento.id); if (item.processado) return; const { event, data } = item.corpo; if (event === "message.status") { // Busque no escopo da conexão/empresa e valide o remetente. // Se wamid e client_ref apontarem para operações diferentes, não escolha uma. const vinculo = await tx.localizarOperacao(item.integracao, data); if (!vinculo.operacao || vinculo.conflito) { await tx.aguardarConciliacao(item.id); // fila durável com alerta e tentativas limitadas return; // NÃO marque o evento como processado. } // sent < delivered < read; failed pode substituir sent, // mas não pode apagar uma entrega ou leitura confirmada. // sent atrasado também não substitui failed. Vale para campanhas e bilhetes. await tx.atualizarStatusSemRegressao(vinculo.operacao, data); } else if (event === "message.received") { await tx.registrarMensagemRecebidaSeNova(data); } else if (event === "contact.marketing_opt_out") { await tx.registrarSaidaDePromocoesSeNova(data); } else if (event === "template.status" || event === "template.quality") { await tx.registrarMudancaDeTemplate(data, event); } await tx.marcarEventoProcessado(item.id); }); } ``` > As funções de banco do exemplo são ilustrativas: implemente índices únicos, fila, transações e tratamento de cada evento habilitado. Valide a assinatura antes de descartar qualquer evento com `data.test === true`; testes recebem 2xx sem persistência ou alteração de cadastro. Se o evento chegar antes da resposta do envio, mantenha-o para conciliação posterior. Efeitos externos, como emitir um bilhete, precisam de uma operação própria com idempotência. Uma resposta HTTP 200 do receptor confirma a recepção, mesmo se o corpo disser `unknown_message`. Esse texto não provoca repetição automática na Patrocinio. Não descarte um evento real sem vínculo; confira o [exemplo de transição e o roteiro de testes](https://patrociniotech.com/documentacao-api/validacao.md). ## 4. Recupere eventos que falharam A fila tenta entregar os eventos por cerca de 31 horas, com pausas progressivas. Se as tentativas esgotarem, corrija o receptor antes de solicitar o reenvio. cURL · consultar eventos com falha ```bash curl "https://api.patrociniotech.com/public/v1/deliveries?status=failed&limit=200" \ -H "Authorization: Bearer SUA_CHAVE" ``` cURL · reenfileirar eventos com falha ```bash curl -X POST "https://api.patrociniotech.com/public/v1/deliveries/retry-failed" \ -H "Authorization: Bearer SUA_CHAVE" ``` > Reenviar uma entrega do webhook repete o evento para o seu servidor. Isso não envia outra mensagem ao contato. O receptor deve reconhecer o mesmo `delivery_id`. Veja [paginação e reenvio de entregas](https://patrociniotech.com/documentacao-api/entregas.md). ## Estados no seu sistema | Estado | Evidência | | --- | --- | | Aceita | Resposta do envio com message\_id e status sent. | | Entregue / lida | Evento message.status com delivered ou read, conciliado com a operação. | | Falhou | Recusa confirmada ou evento failed. Guarde error.code e error.reason quando presentes. | | Aguardando confirmação | Sem evidência suficiente do resultado. Preserve a operação e investigue; não presuma falha nem gere outro envio. | --- Fonte: https://patrociniotech.com/documentacao-api/exemplo Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Webhook Receba confirmações de entrega e mensagens dos contatos no seu sistema. Cadastre a URL do seu endpoint no painel, em Integrações, e escolha os eventos. Fazemos um `POST` assinado para cada evento selecionado. ## Eventos disponíveis | Evento | Dispara quando | Serve para | | --- | --- | --- | | contact.marketing\_opt\_out | O contato pede para parar promoções. | Registrar a saída sem depender do rótulo do botão. | | template.status | A Meta comunica aprovação, rejeição, pausa ou outro estado. | Atualizar o catálogo e suspender modelos indisponíveis. | | template.quality | A Meta informa mudança de qualidade do modelo. | Acompanhar a nota, incluindo a primeira leitura. | | `message.status` | Uma mensagem enviada pela plataforma muda de status. | Confirmar entrega, leitura ou falha. | | `message.received` | Uma mensagem do contato é recebida. | Processar a resposta e atualizar a janela de atendimento. | > Escolha os eventos usados pela sua integração. Para receber respostas dos contatos, habilite também `message.received`; ele contém dados da conversa. Os novos eventos são opcionais. Habilite-os em Integrações quando seu receptor estiver preparado; não alteramos as assinaturas já existentes. Uma entrega HTTP 2xx confirma que o receptor aceitou o evento, não que concluiu a operação. Use [o catálogo](https://patrociniotech.com/documentacao-api/templates.md) com `status=ALL` para reconciliar o estado atual dos modelos. ## Testar pelo painel Em Integrações → Webhook → Testar um evento, escolha o evento e clique em Enviar teste. Os cinco tipos estão disponíveis conforme as assinaturas salvas. O webhook precisa estar ativo, sem alterações por salvar. > Todo teste contém `data.test: true`, inclusive `contact.marketing_opt_out`. Valide primeiro a assinatura; depois, se o marcador for o booleano `true`, responda 2xx sem gravar o evento nem alterar cadastros. Faça isso antes das validações de telefone e das operações de negócio. Os dados são fictícios: telefone composto por zeros, identificadores de exemplo e nenhum cadastro real consultado. Cada clique gera um `delivery_id` diferente, com prefixo `test_`; trate o identificador como texto. A assinatura e o cabeçalho adicional seguem a configuração salva. JSON · teste de saída de promoções ```json { "event": "contact.marketing_opt_out", "occurred_at": "2026-11-06T16:00:00Z", "delivery_id": "test_0123456789abcdef0123456789abcdef", "data": { "message_id": "wamid.TESTE", "to": "000000000000", "phone_number_id": "0", "occurred_at": "2026-11-06T16:00:00Z", "source": "whatsapp", "scope": "tenant", "marketing_allowed": false, "campaign_id": null, "interaction": { "type": "button_reply", "id": "TEST_MARKETING_OPT_OUT", "title": "Parar promoções" }, "test": true } } ``` O teste faz uma única tentativa, mostra o HTTP retornado e registra a ação na auditoria. Não entra na fila de entregas, não tem repetição automática e não altera contatos, consentimentos ou templates. Um 2xx confirma a recepção HTTP; não comprova o processamento de eventos reais, que não recebem esse marcador. ## Mudanças de templates JSON · template.status ```json { "event": "template.status", "occurred_at": "2026-11-06T16:00:00Z", "delivery_id": "2001", "data": { "waba_id": "123456789012345", "template_id": "234567890123456", "name": "pedido_recebido", "language": "pt_BR", "status": "APPROVED", "reason": "NONE" } } ``` O evento `template.quality` mantém a identificação do modelo e traz `previous_quality` e `quality`, no lugar de status/reason. Campos indisponíveis podem ser nulos; aceite códigos futuros sem quebrar o receptor. A WABA identifica a conta, pois um template pode servir a vários números. O horário do envelope é o registro do evento na plataforma; não prova o instante exato da decisão da Meta. ## Saída de promoções `message.received` preserva texto e acrescenta `interaction` para botões/listas e `consent` quando uma saída é reconhecida. Use `consent.action = marketing_opt_out`, em vez de comparar o texto “Parar promoções”. O evento opcional `contact.marketing_opt_out` comunica essa mesma ação com identificador da mensagem e contato. Não soma uma segunda recusa; deduplique a sua operação pelo contato/mensagem. A recusa de marketing vale para os números da empresa. Não representa cancelamento dos avisos legítimos de pedidos. A estrutura e as regras de aceite estão em [Consentimento](https://patrociniotech.com/documentacao-api/consentimento.md). ## message.status corpo do POST ```text { "event": "message.status", "occurred_at": "2026-07-30T18:00:00Z", "delivery_id": "1841", "data": { "message_id": "wamid.HBgNNTUyMT...", "status": "delivered", "status_rank": 3, "to": "5521999999999", "error": null, "phone_number_id": "840747309123670", "client_ref": "pedido-8421", "campaign_id": null, "delivery_api": "cloud", "delivery_api_confirmed": null } } ``` > Eventos de status podem chegar antes da resposta do envio. Grave a operação antes do POST e concilie por `message_id` ou `client_ref`, dentro da empresa e do remetente corretos. Sem vínculo ou com identificadores conflitantes, preserve o evento para conciliação; não associe pelo telefone nem dê baixa como processado. Veja [o roteiro de validação](https://patrociniotech.com/documentacao-api/validacao.md). ## message.received O evento informa o conteúdo recebido, o contato e o número da empresa. Use esses dados para atualizar sua aplicação ou preparar uma resposta. corpo do POST ```text { "event": "message.received", "occurred_at": "2026-08-02T11:14:22Z", "delivery_id": "1842", "data": { "message_id": "wamid.HBgNNTUyMT...", "from": "5521999999999", "phone_number_id": "1234567890", "type": "text", "text": "Recebi, obrigado!", "media_id": null, "media_mime": null, "media_filename": null, "reply_to_message_id": "wamid.HBgNNTUyMT...", "contact_name": "Maria", "occurred_at": "2026-08-02T11:14:22Z", "window_expires_at": "2026-08-03T11:14:22Z" } } ``` | Campo | O que é | | --- | --- | | from | Telefone do contato que enviou a mensagem. Em `message.status`, o telefone do destinatário aparece em `to`. | | type | text, image, audio, video, document, sticker, location, contacts, button, interactive, unsupported. | | interaction | Objeto com type, id e title para botão ou lista recebida; nulo nos demais casos. id preserva o payload do botão ou o identificador da opção. | | consent | Quando reconhecemos uma saída de promoções: action, marketing\_allowed, scope e changed. Nulo quando não houve essa ação. Não deduza consentimento pelo texto da mensagem. | | text | O texto, a legenda de uma mídia ou o rótulo de um botão/opção recebida. Nulo quando não há texto. O identificador da interação fica no campo interaction. | | media\_id | Presente quando veio arquivo. Consulte a conversa no painel para acessar a mídia. O identificador não é uma URL pública nem garante disponibilidade permanente. | | reply\_to\_message\_id | ID da mensagem citada pelo contato. Use-o para localizar o envio e a operação correspondente. | | window\_expires\_at | Até quando a mensagem recebida permite resposta com texto livre. A janela não garante gratuidade. | > Atenção: **window\_expires\_at** representa a janela associada à mensagem recebida. Eventos podem chegar fora de ordem: mantenha a maior validade por contato e número da empresa. Perto de expirar, confirme com [GET /window](https://patrociniotech.com/documentacao-api/janela.md). A cobrança segue as regras vigentes da Meta, separadamente dessa autorização de envio. > Na coexistência, o eco de uma saída pelo celular pode vir sem a mensagem citada. Este evento descreve as mensagens recebidas dos contatos. ## Headers | Header | O que é | | --- | --- | | X-PT-Timestamp | Timestamp Unix em segundos. Faz parte da assinatura e muda a cada tentativa. | | X-PT-Signature | Prefixo `sha256=` mais o HMAC em hexadecimal minúsculo. | | X-PT-Delivery-Id | Id desta entrega. Repete nas retentativas do mesmo evento. | | X-PT-Attempt | Número da tentativa, começando em 1. | | X-PT-Event | Um dos cinco eventos disponíveis acima. Confira também event no corpo assinado. | ## Conferir a assinatura A assinatura é o HMAC-SHA256 de `"timestamp" + "." + corpo cru`, em hexadecimal minúsculo, com o prefixo `sha256=`. O segredo aparece no painel, em Integrações. > Atenção: Calcule a assinatura sobre os **bytes originais** do corpo. Converter o JSON e serializá-lo novamente pode alterar o conteúdo usado no HMAC. Node.js (Express) ```javascript const crypto = require("crypto"); // Guarde o corpo CRU. Sem isto o express.json() já consumiu o stream. app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } })); function assinaturaValida(req, segredo) { const ts = req.get("X-PT-Timestamp"); const assinada = req.get("X-PT-Signature") || ""; if (!segredo || !Buffer.isBuffer(req.rawBody)) return false; if (!/^\d{1,12}$/.test(ts || "") || !/^sha256=[a-f0-9]{64}$/.test(assinada)) return false; if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const esperada = "sha256=" + crypto.createHmac("sha256", segredo).update(`${ts}.`).update(req.rawBody).digest("hex"); // Comparação em tempo constante: comparar com === vaza, pelo tempo, o quanto você acertou. const a = Buffer.from(assinada); const b = Buffer.from(esperada); if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return false; return true; } app.post("/webhooks/patrociniotech", async (req, res) => { if (!assinaturaValida(req, process.env.PT_WEBHOOK_SECRET)) return res.sendStatus(401); if (req.body?.data?.test === true) return res.sendStatus(200); const { delivery_id, event } = req.body; const eventos = ["message.status", "message.received", "contact.marketing_opt_out", "template.status", "template.quality"]; if (!delivery_id || !eventos.includes(event)) { return res.sendStatus(400); } try { // Implemente com armazenamento durável e chave única por integração + delivery_id. // A operação precisa confirmar a gravação ou reconhecer uma duplicata já gravada. await gravarNaFilaDuravel(req.body); return res.sendStatus(200); } catch { return res.sendStatus(503); // permite nova tentativa se a gravação falhou } }); ``` PHP ```php $corpoCru = file_get_contents('php://input'); $ts = $_SERVER['HTTP_X_PT_TIMESTAMP'] ?? ''; $assinada = $_SERVER['HTTP_X_PT_SIGNATURE'] ?? ''; $formatoValido = preg_match('/^[0-9]{1,12}$/D', $ts) && preg_match('/^sha256=[a-f0-9]{64}$/D', $assinada); if (!$segredo || !$formatoValido || abs(time() - (int) $ts) > 300) { http_response_code(401); exit; } $esperada = 'sha256=' . hash_hmac('sha256', $ts . '.' . $corpoCru, $segredo); if (!hash_equals($esperada, $assinada)) { http_response_code(401); exit; } try { $evento = json_decode($corpoCru, true, 512, JSON_THROW_ON_ERROR); if (($evento['data']['test'] ?? false) === true) { http_response_code(200); exit; // teste assinado: não persistir nem executar operações } // Implemente com chave única por integração + delivery_id e confirme a gravação. gravarNaFilaDuravel($evento); http_response_code(200); } catch (JsonException $erro) { http_response_code(400); } catch (Throwable $erro) { http_response_code(503); } ``` > Cada tentativa recebe uma nova assinatura e um timestamp atual. A tolerância de cinco minutos do exemplo vale para a tentativa HTTP, não para a idade do evento. ## Campos de message.status | Campo | O que é | | --- | --- | | message\_id | O mesmo id devolvido no envio. | | status | sent, delivered, read ou failed. | | status\_rank | sent=2, delivered=3, read=4. Ignore regressões entre esses estados. failed=1 é um desfecho separado: pode substituir sent, mas não delivered ou read; não aplique uma comparação numérica simples para falhas. | | to | Número do destinatário. Mesmo nome do campo no envio. | | error | Objeto `{code, message, reason}` só quando o status é failed; nulo nos outros. `reason` é o nosso código estável, `code` é o número da Meta. | | client\_ref | Referência informada no envio. Pode ser nula; combine-a com message\_id para localizar a operação. | | campaign\_id | Preenchido quando a mensagem saiu de uma campanha do painel. | | delivery\_api | API selecionada para a mensagem: cloud ou marketing. Pode ser null quando não há registro da escolha, especialmente em históricos. | | delivery\_api\_confirmed | API comprovada pela classificação do webhook Meta: marketing\_lite confirma marketing; marketing confirma cloud. null indica ausência dessa evidência, não falha nem confirmação de entrega. | | delivery\_id | Id desta entrega. Repete nas retentativas, então serve de chave de idempotência do seu lado. | | occurred\_at | No envelope: quando o evento entrou na fila. Permanece fixo nas tentativas. Em message.received, data.occurred\_at informa o horário da mensagem recebido da Meta. | ## Confirmar o recebimento do evento Para eventos reais, qualquer coisa fora de `2xx` entra na fila de retentativa. Se o seu processamento é demorado, confirme primeiro a gravação numa fila durável, responda e processe depois. Há limite de 10 segundos por tentativa e redirecionamentos HTTP não são seguidos. > Atenção: Uma resposta `2xx` confirma que seu endpoint aceitou o evento. A plataforma não interpreta o corpo para decidir se deve repetir. Para eventos reais, não responda sucesso antes de persistir: se o processo cair, o evento ainda precisa estar disponível para seu worker. O mesmo `delivery_id` pode chegar mais de uma vez. Use uma chave única por integração e entrega para não repetir efeitos já aplicados. ## Se o seu endpoint cair Eventos habilitados são persistidos numa fila. Após a tentativa inicial, repetimos em 30s, 2min, 10min, 1h, 6h, 24h, com variação de até 20% para distribuir a carga: sete tentativas automáticas e cerca de 31 horas de espera acumulada. O que esgota as tentativas fica guardado esperando reenvio, que você dispara pelo painel ou pela [API de entregas](https://patrociniotech.com/documentacao-api/entregas.md). --- Fonte: https://patrociniotech.com/documentacao-api/webhook Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Entregas do webhook Consulte as tentativas de entrega ao seu endpoint e recupere eventos que falharam. Cada evento enfileirado tem uma entrega. As tentativas atualizam a mesma linha, mantendo seu identificador. Consulte se o endpoint aceitou, confira a última resposta e reenvie falhas. > Esta fila mostra a entrega do **webhook ao seu servidor**. Ela não é uma consulta do status atual de uma mensagem no WhatsApp. Um evento pode informar uma mensagem entregue mesmo quando o seu endpoint falhou ao receber essa informação. ## Listar ### GET /public/v1/deliveries | Parâmetro | O que é | | --- | --- | | status | pending, delivering, delivered ou failed. Sem ele, vêm todas. | | message\_id | ID da mensagem para conferir apenas seus eventos. Até 128 caracteres. Pode ser combinado com event e status. | | event | message.status, message.received, contact.marketing\_opt\_out, template.status ou template.quality. Sem ele, vêm todos os tipos enfileirados. | | limit | De 1 a 200 por página. Padrão 50. Valores fora do intervalo retornam 422. | | before\_id | Cursor: traz o que é anterior a esse id. Use o next\_before\_id da resposta. | curl ```bash curl "https://api.patrociniotech.com/public/v1/deliveries?status=failed&limit=100" \ -H "Authorization: Bearer SUA_CHAVE" ``` resposta ```text { "success": true, "data": { "deliveries": [ { "id": 1841, "event": "message.status", "status": "failed", "attempts": 7, "last_status": 502, "next_attempt_at": "2026-07-31T01:12:30Z", "delivered_at": null, "created_at": "2026-07-29T18:00:00Z", "message_id": "wamid.HBgNNTUyMT...", "message_status": "delivered", "recipient": "5521999999999", "client_ref": "pedido-8421" } ], "next_before_id": null, "retention": { "delivered_days": 30, "failed_days": 60, "pending_pruned": false, "delivering_pruned": false, "measured_from": "created_at" } } } ``` Para investigar uma mensagem, filtre pelo ID e pelo tipo do evento. Continue usando os mesmos filtros ao passar o cursor `next_before_id`. cURL · eventos de uma mensagem ```bash curl --get "https://api.patrociniotech.com/public/v1/deliveries" \ -H "Authorization: Bearer SUA_CHAVE" \ --data-urlencode "message_id=wamid.EXEMPLO" \ --data-urlencode "event=message.status" \ --data-urlencode "limit=50" ``` | Campo do registro | Como interpretar | | --- | --- | | status | Situação da entrega HTTP deste evento ao seu endpoint. | | message\_status | Status do WhatsApp informado neste evento, quando disponível. Outro evento mais recente pode ter atualizado a mensagem. | | last\_status | Código HTTP retornado pelo seu endpoint na última tentativa, quando houve resposta. | > Para consultar a próxima página, envie o `next_before_id` recebido como` before_id`. Mantenha os filtros e continue até o cursor ser nulo. ## Ver uma entrega ### GET /public/v1/deliveries/{id} Igual à listagem, mais `last_error`: um resumo da falha ou um trecho da última resposta, guardado mesmo em sucesso. Credenciais conhecidas são ocultadas. Não devolva dados sensíveis no corpo do seu endpoint. O trecho da resposta é limitado e pode estar incompleto; não o interprete como um JSON completo. Respostas gzip e deflate são decodificadas antes do registro. Se não for possível ler o conteúdo comprimido, o diagnóstico informa essa limitação e preserva o resultado HTTP. > Atenção: Uma resposta `2xx` confirma o recebimento HTTP. Se o evento não produziu o resultado esperado, confira também os registros de processamento no seu sistema. curl ```bash curl "https://api.patrociniotech.com/public/v1/deliveries/1841" \ -H "Authorization: Bearer SUA_CHAVE" ``` ## Reenviar ### POST /public/v1/deliveries/{id}/retry Devolve a entrega para a fila. Vale para `failed` (esgotou as tentativas) e para `delivered` (reprocessar do seu lado). O corpo é assinado de novo no disparo, com o timestamp do momento, então uma entrega reenviada horas depois não esbarra na janela de replay do seu endpoint. Essa ação reenvia somente o evento ao seu endpoint. Não envia outra mensagem ao contato no WhatsApp. > O reenvio mantém `delivery_id` e o histórico de tentativas. Se o evento já foi aplicado, a deduplicação deve continuar impedindo efeitos repetidos. Para refazer uma operação de negócio, use seu próprio processo de recuperação; reenviar o webhook não cria outro evento. curl ```bash curl -X POST "https://api.patrociniotech.com/public/v1/deliveries/1841/retry" \ -H "Authorization: Bearer SUA_CHAVE" ``` ### POST /public/v1/deliveries/retry-failed Recoloca até 500 entregas com falha na fila por chamada. Devolve `requeued` com quantas voltaram. Corrija o endpoint antes de usar; se houver mais registros, confira a fila novamente antes de solicitar outro lote. curl ```bash curl -X POST "https://api.patrociniotech.com/public/v1/deliveries/retry-failed" \ -H "Authorization: Bearer SUA_CHAVE" ``` ## Rotina sugerida Consulte falhas periodicamente e avise a equipe responsável. O exemplo abaixo verifica uma página da fila; não solicita reenvio automaticamente. Node.js ```javascript async function varrerFalhas() { const r = await fetch(`https://api.patrociniotech.com/public/v1/deliveries?status=failed&limit=200`, { headers: { Authorization: `Bearer ${process.env.PT_API_KEY}` }, }); const { data } = await r.json(); if (!r.ok || !data?.deliveries) throw new Error("Falha ao consultar as entregas."); if (!data.deliveries.length) return; // alertar é uma função do seu sistema. Confirme a recuperação antes do reenvio. await alertar(`${data.deliveries.length} entregas de webhook falharam`); // Após confirmar a recuperação do endpoint, solicite o reenvio em uma ação separada. } ``` ## Por quanto tempo ficam disponíveis Por padrão, entregas com `status=delivered` são mantidas por 30 dias e as que terminaram em `failed` por 60 dias, contados de `created_at`. A resposta da listagem informa os prazos efetivos em `data.retention`; eles podem variar conforme a configuração do serviço. A manutenção remove registros em lotes, por isso alguns podem continuar visíveis depois do prazo. Eventos em `pending` ou `delivering` não são removidos somente por idade. Um reenvio manual preserva a data de criação e não reinicia a retenção. > Guarde os eventos processados no seu sistema. A fila serve para diagnóstico e recuperação dentro desses prazos; uma lista vazia ou um 404 não comprova que a mensagem no WhatsApp deixou de ser entregue. ## Situações | status | O que significa | | --- | --- | | pending | Na fila, esperando a próxima tentativa. | | delivering | Uma tentativa de entrega está em andamento. | | delivered | Seu endpoint respondeu 2xx. Isso não confirma o processamento da operação no seu sistema. | | failed | Tentativas esgotadas ou webhook desativado/removido. Consulte last\_error antes de reenviar. | `next_attempt_at` só representa um agendamento ativo quando o estado é` pending`. Nos demais estados pode conservar o horário da tentativa anterior.` delivered_at` registra uma aceitação anterior e não substitui o estado atual depois de um reenvio manual. --- Fonte: https://patrociniotech.com/documentacao-api/entregas Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Da operação à entrega confirmada O que guardar, como conferir cada etapa e quais testes executar antes de liberar a integração. > Use dados fictícios e somente um destinatário autorizado nos testes reais. Não use clientes como amostra de teste. Ensaios de timeout e eventos fora de ordem devem começar em ambiente local, com transporte simulado. ## Escolher Cloud ou Marketing | 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](https://patrociniotech.com/documentacao-api/mensagens.md) e [como comparar campanhas](https://patrociniotech.com/documentacao-api/campanhas.md). ## Uma operação por mensagem 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 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. JavaScript · transição de status ```javascript 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 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. > Um HTTP 200 com `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](https://patrociniotech.com/documentacao-api/exemplo.md). ## Erros que não devem disparar outra mensagem | 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](https://patrociniotech.com/documentacao-api/erros.md). ## Roteiro de validaçã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. --- Fonte: https://patrociniotech.com/documentacao-api/validacao Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Erros e limites Use o status HTTP e error.code para decidir entre corrigir os dados, aguardar ou verificar uma operação sem confirmação. JSON · exemplo de recusa ```json { "success": false, "error": { "code": "outside_24h_window", "message": "Faz mais de 24h que essa pessoa não escreve. Envie um template aprovado." } } ``` `error.code` identifica a condição para sua aplicação. `error.message` explica o problema para uma pessoa e pode mudar sem alterar o contrato. Em `422 invalid_request`, `error.details.fields` indica até 20 campos inválidos, com caminho, código e orientação. Os valores enviados não são devolvidos. O campo `details` é opcional: trate também erros que só tenham código e mensagem. JSON · campo inválido ```json { "success": false, "error": { "code": "invalid_request", "message": "Dados inválidos. Revise os campos.", "details": { "fields": [ { "field": "body.header_media.link", "code": "url_parsing", "message": "Informe uma URL válida." } ] } } } ``` Guarde o header `X-Request-Id`, o status HTTP e o código do erro junto à sua operação. Use essa referência para investigar uma chamada com o suporte; não registre o header `Authorization` nem códigos de autenticação. ## O que fazer com cada resposta | HTTP | Interpretação e ação | | --- | --- | | 200 | Leia data. No envio, sent significa aceite; confirme entrega pelo webhook. | | 400 / 422 | Corrija os campos ou a regra indicada em error.code. 422 também cobre validações locais, não apenas recusas da Meta. | | 401 | Confira o header Authorization e se a chave foi revogada. | | 404 | Confira o identificador e a conta à qual o recurso pertence. | | 409 | Confira a operação original. Não troque a chave para contornar idempotência. | | 429 | Reduza o ritmo e respeite Retry-After quando informado. | | 5xx / falha de conexão | O resultado pode estar incerto. Preserve corpo e chave ao tentar novamente. | > Atenção: Uma resposta sem confirmação não prova que a mensagem deixou de ser enviada. A sua política de retry deve manter a mesma `Idempotency-Key`, limitar tentativas e encaminhar casos persistentes para conferência. ## Encontrar o código de erro ### Dados da requisição | Código | Correção | | --- | --- | | invalid\_request | Confira error.details.fields, tipos e limites no OpenAPI. Campos desconhecidos no envio são recusados; não são ignorados. | | invalid\_recipient | Use telefone internacional com DDI e DDD. Para um cadastro local, normalize antes com POST /validate e country; o envio não acrescenta 55. | | empty\_message / ambiguous\_message | Informe text ou template. Não deixe ambos ausentes nem envie os dois juntos. | | invalid\_idempotency\_key | Informe uma chave não vazia, de até 64 caracteres. | | variable\_too\_long | Reduza a variável ao limite de 1.024 caracteres, sem perder dados essenciais. | | variable\_bad\_format / template\_param\_format | Remova quebras de linha, tabulações e sequências de mais de quatro espaços das variáveis. | | invalid\_pix\_order / pix\_needs\_template | Confira o template de cobrança, os campos PIX e a soma dos itens em centavos. | ### Template e conteúdo | Código | Correção | | --- | --- | | outside\_24h\_window | Use template aprovado quando a janela de texto livre estiver fechada. | | template\_not\_found | Confira nome, idioma e número conectado. | | invalid\_template / template\_paused | Confira aprovação, qualidade e disponibilidade do modelo na Meta. | | template\_format\_unsupported | Consulte api\_supported e api\_unsupported\_reason e escolha um modelo compatível. | | template\_params\_mismatch | Confira quantidade e nomes das variáveis, cabeçalho e posição dos botões. | | template\_required\_header\_media | Envie o arquivo exigido em header\_media ou use o modelo sem anexo. | | template\_header\_mismatch | Use o tipo de mídia ou texto de cabeçalho exigido pelo modelo. | | invalid\_otp | Confira código, categoria AUTHENTICATION e botão no índice 0. Não use campos de cupom. | | invalid\_template\_status | Na consulta de templates, use APPROVED, ALL ou outro status documentado. Não envie um filtro vazio. | ### Escolha da API de entrega | Código | Correção | | --- | --- | | invalid\_request · 422 | Para delivery\_api, use cloud ou marketing. Omitir o campo equivale a cloud. Confira o campo indicado em error.details.fields. | | delivery\_api\_template\_mismatch · 422 | marketing exige template da categoria MARKETING. Texto livre, UTILITY e AUTHENTICATION usam cloud; trocar a rota não muda a categoria. | | marketing\_api\_not\_ready · 422 | Confira ativação, permissões e configuração da Marketing Messages API na Meta. Não repita em laço nem alterne automaticamente para Cloud API. | | cloud\_marketing\_disabled · 422 | Meta 131063: marketing por Cloud API indisponível para essa conta. Confira a configuração Meta e a rota adequada antes de uma nova operação; não há fallback automático. | Preserve a operação original e confirme se houve aceite antes de preparar outra. A mesma `Idempotency-Key` com `delivery_api` diferente retorna `409 idempotency_conflict`. Nem uma falha de configuração nem uma mudança de rota autorizam contornar recusas de marketing, bloqueios ou resultados incertos. ### Consulta e recuperação do webhook | Código | Ação | | --- | --- | | invalid\_status | Use pending, delivering, delivered ou failed ao consultar a fila do webhook. | | invalid\_event | No filtro de entregas, use um dos eventos documentados: message.status, message.received, contact.marketing\_opt\_out, template.status ou template.quality. | | invalid\_status\_lookup | Na consulta GET /messages/status, informe client\_ref ou o cabeçalho Idempotency-Key, nunca os dois nem um valor vazio. | | ambiguous\_client\_ref | A referência foi reutilizada em mais de uma tentativa. Consulte pela chave de idempotência original ou pelo message\_id; a API não escolhe o registro mais recente. | | not\_found | A entrega não existe nesta conta. Confira o identificador retornado pela listagem. | | message\_not\_found | O envio não foi encontrado nesta conta. Confira message\_id, eventos e retenção. Um 404 não comprova que a mensagem não foi enviada e não deve provocar reenvio automático. | | not\_retryable | A entrega não existe nesta conta ou não está em failed ou delivered. Confira o identificador e o estado antes de reenviar. | ### Acesso, destinatário e conta | Código | Ação | | --- | --- | | missing\_api\_key / invalid\_api\_key | Confira a chave no servidor e o header Authorization. Revogue a credencial se houver exposição. | | no\_connected\_number | Confira o número selecionado e a conexão da conta no painel. | | recipient\_unreachable | A mensagem não pôde ser entregue. O código Meta 131026 não confirma ausência de WhatsApp. Confira o telefone com o cliente antes de reenviar. | | marketing\_not\_allowed | O contato recusou promoções ou está bloqueado para marketing. Não repita o envio promocional. Avisos transacionais usam templates de utilidade adequados ao pedido. | | marketing\_experiment | A entrega de marketing foi limitada para esse destinatário. Não tente contornar a restrição com repetições. | | marketing\_recipient\_limited | Meta 131049: marketing limitado para esse destinatário. Não comprova bloqueio do número da empresa. Evite repetir imediatamente. | | delivery\_limited | Meta 131048: entrega limitada por qualidade ou sinais de spam. Confira a situação do número e a qualidade dos envios na Meta. | | account\_restricted | Confira e resolva a restrição da conta na Meta antes de repetir. | | payment\_issue | Confira a elegibilidade de pagamento e a configuração de cobrança na Meta. O código 131042 não comprova saldo insuficiente. | Uma mensagem aceita para processamento ainda pode falhar por pagamento. Confira o status pelo webhook antes de reenviar; se houver cobrança pré-paga, verifique também o saldo no painel da Meta. A modalidade de cobrança não muda o endpoint de envio. > O erro `131026` pode ocorrer por número sem cadastro, aplicativo desatualizado ou termos ainda não aceitos pelo destinatário. A Meta não distingue essas causas nesse código. Peça ao cliente, por outro canal, que confirme o telefone e tente iniciar uma conversa com o número da empresa. Consulte os [códigos de erro da Meta](https://developers.facebook.com/documentation/business-messaging/whatsapp/support/error-codes). ### Operação em andamento ou indisponibilidade | Código | Nova tentativa | | --- | --- | | idempotency\_in\_flight | Aguarde e use a mesma chave e corpo. Se continuar sem confirmação, confira os eventos. | | idempotency\_conflict | Não repita automaticamente: a chave pertence a outro conteúdo. | | idempotency\_legacy\_unverified | Confira o registro antigo e os eventos; a API não pode comparar o conteúdo. | | send\_uncertain | Não há confirmação de aceite nem de recusa. Preserve a operação e consulte o status com a chave original ou client\_ref. Não gere outra chave nem reenvie automaticamente. | | api\_rate\_limited | Respeite o teto por chave e Retry-After quando informado. | | rate\_limited | Reduza o ritmo; este limite foi informado pela Meta. | | templates\_unavailable / meta\_unavailable / db\_unavailable | Aguarde e use a mesma chave e conteúdo, com limite de tentativas. | | network\_error / send\_failed | Preserve a operação, confira os status e investigue antes de iniciar outro envio. | ## Limites da integração | Item | Limite | | --- | --- | | Requisições da API | 600 por minuto, por chave, somando todos os endpoints autenticados e todos os workers que usam essa chave. | | Idempotency-Key | Até 64 caracteres. Retenção padrão de dois dias; não substitui seu histórico permanente de operações. | | Texto livre | Até 4.096 caracteres. | | Variáveis de template | Até 20 valores de 1.024 caracteres. Valores maiores são recusados, não cortados. | | Código OTP / cupom | Até 15 caracteres. São campos e tipos de botão diferentes. | | Consulta de entregas | Até 200 itens por página, com paginação por cursor. | O teto é de requisições, não de mensagens: enviar, validar telefone, consultar janela, consultar templates e acompanhar a fila consomem a mesma cota. Enviar a cada 100 ms já ocupa 600 chamadas por minuto, antes dessas consultas e das novas tentativas. Use um limitador compartilhado entre os workers, reserve margem para consultas e reaproveite o catálogo e a janela recebida no webhook. Ao receber `429 api_rate_limited`, pause a fila dessa chave pelo `Retry-After`; quando informado, `error.details.retry_after_seconds` traz a mesma pausa em segundos. ### Acompanhar a cota antes do 429 Respostas de chamadas autenticadas que passaram pelo contador incluem os cabeçalhos abaixo, tanto em sucesso quanto em recusas posteriores. Falhas anteriores à autenticação, respostas do proxy ou uma conexão interrompida podem não ter esses campos. | Cabeçalho | Uso | | --- | --- | | X-RateLimit-Limit | Teto da janela de um minuto: 600 requisições por chave. | | X-RateLimit-Remaining | Saldo imediatamente após contar esta chamada, nunca negativo. Chamadas concorrentes podem consumir o saldo antes da próxima requisição. | | X-RateLimit-Reset | Instante de início da próxima janela, em segundos Unix UTC. Não é uma duração. | | X-RateLimit-Scope | api\_key: contador compartilhado. api\_key\_worker: contingência local quando o contador compartilhado está indisponível; mantenha seu próprio limitador global de 600/min. | | Retry-After | Em 429 por cota, segundos a aguardar. A Meta também pode limitar uma chamada independentemente do saldo local. | HTTP · exemplo de cota ```text X-RateLimit-Limit: 600 X-RateLimit-Remaining: 541 X-RateLimit-Reset: 1793966460 X-RateLimit-Scope: api_key ``` O saldo é uma fotografia, não uma reserva para disparar esse volume imediatamente. Somar leituras e envios na mesma fila e manter margem evita que uma consulta em lote concorra com a entrega de mensagens transacionais. > O limite da Meta considera destinatários únicos em uma janela móvel de 24 horas e pode ser compartilhado pelo portfólio. Ele é separado dos 600 pedidos por minuto desta API. Consulte o limite atual no painel e no WhatsApp Manager; a qualidade do número não informa, sozinha, essa capacidade. ## Exemplo de novas tentativas Este exemplo trata uma operação: repete condições temporárias conhecidas, falhas de conexão e 5xx, mantendo corpo e chave, respeitando `Retry-After` e encerrando após quatro tentativas. Ele não substitui o limitador compartilhado da fila. Recusas desconhecidas não entram em retry automático. Node.js · retry com a mesma operação ```javascript const TEMPORARIOS = new Set([ "api_rate_limited", "rate_limited", "templates_unavailable", "meta_unavailable", "db_unavailable", "idempotency_in_flight", ]); const esperar = (ms) => new Promise((ok) => setTimeout(ok, ms)); async function enviarComRetry(operacao) { const token = process.env.PT_API_KEY; const chave = operacao.chave; if (!token || !chave) throw new Error("Configure a chave de API e a chave da operação."); // Salve a operação antes de chamar. Não gere uma chave nova dentro deste laço. const corpo = JSON.stringify(operacao.corpo); for (let tentativa = 1; tentativa <= 4; tentativa++) { let resposta = null; let dados = null; try { resposta = await fetch("https://api.patrociniotech.com/public/v1/messages", { method: "POST", signal: AbortSignal.timeout(15000), headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", "Idempotency-Key": chave, }, body: corpo, }); dados = await resposta.json().catch(() => null); } catch { // Timeout não comprova que a mensagem foi recusada. } if (resposta?.ok && dados?.success === true && dados?.data?.message_id) { return { estado: "aceita", dados: dados.data }; } const codigo = typeof dados?.error?.code === "string" ? dados.error.code : null; if (codigo === "send_uncertain") { // Não converter ausência de confirmação em recusa ou novo disparo. return { estado: "verificar", http: resposta.status, erro: dados.error }; } const temporario = !resposta || resposta.status >= 500 || resposta.status === 429 || (resposta.ok && dados?.success !== false) || TEMPORARIOS.has(codigo); if (!temporario) { const verificar = !codigo || codigo.startsWith("idempotency_"); return { estado: verificar ? "verificar" : "recusada", http: resposta.status, erro: dados?.error }; } if (tentativa === 4) break; const header = resposta?.headers.get("Retry-After"); const segundos = header ? Number(header) : NaN; const prazo = Number.isFinite(segundos) ? segundos * 1000 : Date.parse(header || "") - Date.now(); const recuo = 2 ** tentativa * 1000 + Math.random() * 1000; await esperar(Math.max(recuo, Number.isFinite(prazo) ? prazo : 0)); } return { estado: "verificar", motivo: "Confira os eventos e preserve a mesma operação." }; } ``` | estado retornado pelo exemplo | Tratamento no seu sistema | | --- | --- | | aceita | Guarde o message\_id. Aguarde o webhook para confirmar entrega. | | recusada | Mostre o motivo e corrija os dados ou a configuração antes de tentar novamente. | | verificar | Mantenha a operação pendente de conferência. Consulte eventos; não crie outra chave automaticamente. | Execute em um worker e persista a operação antes de enviar. Para pausas longas, reagende na sua fila. Uma resposta de infraestrutura pode não ser JSON; o exemplo trata essa condição sem assumir sucesso. Para a conciliação completa, veja [envio e processamento do webhook](https://patrociniotech.com/documentacao-api/exemplo.md). --- Fonte: https://patrociniotech.com/documentacao-api/erros Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt --- # Mudanças da API Acompanhe alterações nos endpoints, nos campos e nas regras de integração. ## A regra de compatibilidade Prepare sua integração para novos campos e mensagens de erro, preservando o tratamento dos códigos e formatos já documentados. | Isso pode mudar sem versão nova | Isso só muda em versão nova | | --- | --- | | Campo novo numa resposta. | Remoção ou renomeação de um campo existente. | | Valor novo num campo de texto livre (ex.: um \`reason\` inédito). | Tipo de um campo mudar. | | A frase de \`message\` num erro. | O \`code\` de um erro mudar de nome. | | Endpoint novo. | Endpoint existente sumir. | > Atenção: **Ignore campos desconhecidos** e trate erros pelo `code`. O texto de `message` pode mudar sem alterar a condição do erro. Mudança que quebra contrato ganha caminho novo (`/public/v2/`) e a v1 continua no ar. As duas quebras marcadas abaixo aconteceram **antes** de existir cliente integrado além do primeiro, que foi avisado direto; daqui em diante a regra vale. ## Histórico 2026 Mudou### Contrato de resposta e roteiro de validação 9 de novembro de 2026- OpenAPI descreve as respostas de sucesso de envio, validação de telefone e reentrega de webhooks. O retorno de envio diferencia aceite inicial e `idempotent_replay`; históricos sem registro da rota continuam compatíveis. - `Idempotency-Key` permanece opcional no HTTP e recomendado para automações. O envio não impõe unicidade de `client_ref`: a integração deve garantir uma referência por operação na empresa. Não houve nova exigência de campos nem mudança de autenticação. - O [roteiro de validação](https://patrociniotech.com/documentacao-api/validacao.md) cobre Cloud/Marketing, vínculo ao pedido, eventos antecipados, falhas atrasadas e testes reais versus sintéticos. O exemplo preserva eventos sem vínculo em uma fila de conciliação. As mesmas orientações estão em Markdown, llms e MCP. Novo### Escolha da API de entrega para marketing 9 de novembro de 2026- O envio aceita `delivery_api`: `cloud` permanece o padrão; `marketing` seleciona a Marketing Messages API para templates `MARKETING`. A URL da Patrocinio e a autenticação continuam iguais. - A opção exige ativação da conta na Meta e usa `product_policy: STRICT`, sem fallback automático. Não altera termos, compartilhamento de atividade, bloqueios ou consentimento; não promete mais entrega nem custo menor. - A API selecionada passa a integrar a idempotência e aparece na resposta/status e nos dados da campanha. Omitir e informar `cloud` são equivalentes; trocar a rota mantendo a mesma chave retorna conflito. O campo `delivery_api_confirmed` em status, webhook e destinatários preserva a evidência recebida da Meta, sem inferir o histórico ausente. - Novos erros de configuração e compatibilidade orientam corrigir a conta ou o envio, sem fallback. Veja [envio e contrato](https://patrociniotech.com/documentacao-api/mensagens.md), [códigos de erro](https://patrociniotech.com/documentacao-api/erros.md) e [comparação dos resultados](https://patrociniotech.com/documentacao-api/campanhas.md). Mudou### Campanhas: resultados sem confirmação separados de falhas 9 de novembro de 2026- Os resultados distinguem tentativas `uncertain` de falhas confirmadas. Quando falta confirmação de aceite ou recusa, o código `send_uncertain` orienta conciliar a operação, sem reenvio automático. - A consulta de campanhas admite `status: paused`. Pausas operacionais do template suspendem novas tentativas, sem cancelar envios já aceitos. Reaprovação não retoma automaticamente; a continuação pelo painel inclui somente destinatários ainda sem tentativa. - O estado `cancelled` indica cancelamento definitivo da fila pelo painel. Preserva histórico e atualizações de entregas anteriores; não recolhe mensagens já aceitas. Pendentes sem tentativa aparecem como `not_sent`, e tentativas sem confirmação continuam `uncertain`. - O contador histórico `campaign.dispatch_failed` permanece por compatibilidade e pode incluir exclusões ou incertezas. Use `counts`, `status` e `error_code` de cada destinatário para decidir o tratamento. - O guia de campanhas explica a diferença entre previsão de processamento e entrega, os cuidados ao exportar e preparar outra tentativa e a distinção entre reentregar um webhook e reenviar uma mensagem. Novo### Teste de todos os eventos pelo painel 6 de novembro de 2026- Em Integrações → Webhook, escolha entre os cinco eventos habilitados na configuração salva, incluindo `contact.marketing_opt_out`. Cada teste usa dados fictícios e `data.test: true`, assinatura normal e identificador próprio. - Valide a assinatura e responda 2xx sem gravar ou alterar cadastros quando o marcador for o booleano true. Os exemplos Node.js, PHP e o guia para IA seguem essa regra para todos os eventos. Veja [como testar](https://patrociniotech.com/documentacao-api/webhook.md). - O teste tem uma tentativa, fica na auditoria e não entra na fila de entregas. Nenhum contato é descadastrado pelo teste na Patrocinio; o receptor também precisa respeitar o marcador. Novo### Conciliação, consentimento em lote e gestão de templates 6 de novembro de 2026- Consulte um envio sem message\_id por `GET /public/v1/messages/status`, usando client\_ref ou Idempotency-Key. Resultado incerto ou não encontrado não autoriza gerar outra operação. O contrato informa retenção e limitações. - Nova consulta de até 500 contatos por lote e registro explícito de consentimento com data, origem, referência da prova e event\_id idempotente. Registros legados não ganham provas inventadas. A API não remove recusa nem bloqueio existente. - Campanhas do painel podem ser consultadas por `/public/v1/campaigns` e `/campaigns/{id}/results`. Totais e destinatários paginados distinguem aceite, entrega, leitura, falha e saída de promoções. - Criação de templates e upload de amostras disponíveis pela API. Novas assinaturas opcionais: `contact.marketing_opt_out`, `template.status` e `template.quality`. O evento message.received também informa a interação e a saída reconhecida de forma estruturada. - A consulta de números acrescenta os motivos da restrição e indica leitura antiga. As respostas autenticadas incluem headers de cota. Entregas de webhook aceitam filtros por message\_id e evento e informam a retenção aplicada. Mudou### Resposta do receptor de webhook 6 de novembro de 2026- O detalhe de entrega preserva um trecho legível de respostas gzip e deflate, com limite também após a descompressão e ocultação de credenciais conhecidas. O nome `last_error` permanece por compatibilidade; ele pode conter a resposta mesmo quando o HTTP foi 200. - Problemas de leitura do conteúdo comprimido não transformam um HTTP aceito em falha de entrega. A correção vale para novas tentativas; não reenvia eventos antigos nem reconstrói respostas já truncadas. Novo### Números conectados e referência para IA 6 de novembro de 2026- Novo `GET /public/v1/numbers`: números da empresa, qualidade, capacidade do portfólio e última saúde registrada. Paginação por cursor, até 50 itens, horários por leitura e indicação de indisponibilidade parcial. - O guia [Integrar com IA](https://patrociniotech.com/documentacao-api/ia.md) reúne instrução copiável, Markdown e OpenAPI. O MCP público oferece busca e leitura da documentação, sem acesso a contas ou ferramentas de envio. - Históricos de novos envios preservam até dez botões. Recuperação de um aceite usa o conteúdo salvo naquela operação, sem substituí-lo por um template editado depois. Códigos de autenticação continuam mascarados. - Checkpoints antigos sem cópia completa usam apenas os dados comprovados da operação; não recuperamos texto ou botões antigos consultando o catálogo atual. Mudou### Validação, consentimento e consulta de entrega 6 de novembro de 2026- O envio recusa campos desconhecidos, índices que não sejam inteiros e URLs de mídia inválidas. O erro `invalid_request` pode incluir `error.details.fields`, com o campo, código e orientação, sem repetir valores sensíveis. - Telefones precisam incluir o código do país. Para cadastros locais, use `/validate` com `country`; o envio não acrescenta DDI automaticamente. - A consulta da janela acrescenta `marketing_allowed` e `marketing_block_reason`. Ausência de bloqueio não prova consentimento: aceite pendente continua diferente de aceito, e recusa ou bloqueio impedem marketing. - O limite é de 600 requisições por minuto por chave, somando envios e consultas de todos os workers. Em `api_rate_limited`, respeite `Retry-After`; o JSON também pode informar `error.details.retry_after_seconds`. - Use `GET /public/v1/messages/{message_id}/status` para consultar um envio já registrado, sem reenviar. O webhook continua sendo o caminho principal para acompanhar entregas. - Mudanças de status, qualidade, categoria e componentes invalidam o catálogo de templates entre os processos da API. As assinaturas opcionais de templates foram acrescentadas posteriormente nesta mesma data, conforme a entrada acima. Novo### Qualidade e contrato do catálogo de templates 7 de outubro de 2026- Cada modelo passa a informar `id`, `quality_score`, `rejected_reason` e `message_send_ttl_seconds`, com valores nulos quando a Meta não os fornece. O OpenAPI descreve a resposta completa. - Use `status=ALL` para acompanhar análise, aprovação e outros estados. Filtro vazio ou desconhecido retorna `400 invalid_template_status`. A consulta não entrega catálogo parcial como se estivesse completo. - Aprovação e qualidade continuam independentes. O integrador deve conferir `sendable` e `api_supported` antes de preparar um envio. Mudou### Horários UTC e estado da conta 7 de outubro de 2026- Os campos `data.occurred_at` e `data.window_expires_at` de `message.received` passam a indicar UTC explicitamente com `Z`. O instante representado não muda. - A chave da API exige conta ativa ou em avaliação. Suspensão, desativação ou remoção da conta também bloqueiam chamadas autenticadas por chave. Mudou### Validação de templates, autenticação e proteção contra envio duplicado 6 de outubro de 2026- A listagem de templates aceita `phone_number_id` e informa `parameter_format`, `body.variable_names` e `buttons[].needs_otp`. O envio confere as variáveis, o cabeçalho e os botões antes de chamar a Meta. - Novo `otp_code` com `otp_index` para templates de autenticação. O código é repetido no corpo e no botão nativo; validade e uso único continuam sob responsabilidade do sistema emissor. - Uma chave de idempotência usada com conteúdo diferente retorna `idempotency_conflict`. Tentativas com resultado incerto não são reenviadas automaticamente após cinco minutos. Registros antigos sem hash retornam `idempotency_legacy_unverified`. - A consulta da janela sem `phone_number_id` usa o número padrão do envio. A janela permite escolher o formato; não informa gratuidade. - O painel permite escolher eventos de status e respostas recebidas sem apagar a seleção existente. O reenvio preserva o identificador da entrega; o consumidor deve deduplicá-lo. Novo### Idempotência reservada antes do envio, e erros de template separados 11 de agosto de 2026- A `Idempotency-Key` passou a ser reservada antes da chamada à Meta, protegendo também as tentativas concorrentes e as falhas de conexão durante o envio. - Resposta nova: `409 idempotency_in_flight`, quando outra chamada com a mesma chave ainda está em andamento. Espere alguns segundos e repita **com a mesma chave**. - Uma recusa confirmada permite corrigir o pedido e reutilizar a chave. Falhas de conexão e resultados sem confirmação mantêm a reserva; confira a regra atual em [idempotência](https://patrociniotech.com/documentacao-api/mensagens.md). - `invalid_template` se dividiu. Agora existem `template_params_mismatch` (as variáveis não batem: cabeçalho com anexo, botão com link variável ou variável com NOME também precisam ser enviados), `template_not_found` e `template_paused`. Cada um pede uma ação diferente, e a mensagem única mandava investigar as três. - `marketing_experiment`: a Meta separa parte dos destinatários num grupo que não recebe marketing, para medir o efeito. Não é falha do envio e reenviar não muda. Novo### Janela de 24h, templates, anexo no envio e resposta do contato 2 de agosto de 2026- `GET /public/v1/window/{to}`: diz se dá para mandar texto livre agora. É a consulta que orienta a escolha entre texto livre e template. - `GET /public/v1/templates`: lista os seus templates já decompostos em cabeçalho, corpo, rodapé e botões, dizendo o que cada um exige no envio. - `POST /public/v1/messages` ganhou `header_media`, `header_text`, `button_url_param`, `variable_names` e `reply_to_message_id`. Sem eles, template com anexo (bilhete, comprovante) ou com botão dinâmico não tinha como ser enviado pela API. - Novo evento de webhook `message.received`: o que o contato respondeu, com o texto, a mídia, a citação e o `window_expires_at`, que é até quando você pode responder dentro da janela de atendimento. Novo### Consulta e reenvio das entregas do webhook 30 de julho de 2026- `GET /public/v1/deliveries`, com filtro por `status` e paginação por cursor (`before_id`). - `GET /public/v1/deliveries/{id}`, que traz também o corpo do que o seu endpoint respondeu. - `POST /public/v1/deliveries/{id}/retry` e `POST /public/v1/deliveries/retry-failed`. - Especificação OpenAPI pública em `/public/v1/openapi.json`, só das rotas públicas. Quebra### Formato do evento de status alinhado 30 de julho de 2026- `error` virou OBJETO `{code, message, reason}`, no lugar de uma string com um `error_code` solto ao lado. Quem lia a string precisou passar a ler `error.reason`. - `recipient` passou a se chamar `to`, o mesmo nome do campo no envio. - `timestamp` passou a se chamar `occurred_at`, para não confundir com o `X-PT-Timestamp` do header, que tem outro significado. - Entrou `status_rank`, para o seu lado descartar evento fora de ordem sem inventar regra própria. Quebra### Assinatura com timestamp e header de autenticação 30 de julho de 2026- A assinatura passou a ser o HMAC de `"timestamp" + "." + corpo`, e não mais só do corpo. Assinar só o corpo deixava a requisição válida para sempre, então quem capturasse uma entrega poderia reenviá-la meses depois. - Um só padrão de assinatura: a versão antiga foi removida em vez de conviver com a nova. - Passou a ser possível configurar um **header de autenticação próprio** do seu endpoint, para gateways que recusam antes de olhar a assinatura. Novo### Correlação, idempotência e fila de entrega 30 de julho de 2026- `client_ref` no envio, devolvido em todo evento de status. Acaba com a tabela de-para entre o id do WhatsApp e o seu registro. - Header `Idempotency-Key`: repetir a mesma chamada devolve a resposta original em vez de enviar de novo. - Limite por CHAVE (600 por minuto) no lugar do limite por IP, que estrangulava campanha grande e misturava clientes atrás do mesmo IP. - O webhook ganhou fila com retentativa de cerca de 31 horas. Antes, endpoint fora do ar significava evento perdido. Novo### Primeira versão da API pública 28 de julho de 2026- `POST /public/v1/messages`: texto na janela de 24h e template fora dela. - `POST /public/v1/validate`: confere o formato do número antes do envio. - Webhook de saída com o evento `message.status`, assinado com HMAC-SHA256. - Chaves de API por conta, com prefixo visível e revogação. ## Como você fica sabendo ### Antes de acontecer Mudança que exige ação sua é avisada por e-mail para o responsável da conta, com prazo. Nada que quebre integração entra sem aviso. ### Para acompanhar Consulte este histórico e a [especificação OpenAPI](https://api.patrociniotech.com/public/v1/openapi.json) para acompanhar os contratos. O [catálogo de erros](https://patrociniotech.com/documentacao-api/erros.md) informa as condições conhecidas e a ação recomendada para cada uma. --- Fonte: https://patrociniotech.com/documentacao-api/mudancas Índice para IA: https://patrociniotech.com/documentacao-api/llms.txt