# 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. ## 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, anexo, botão 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 - [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. 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). | | 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). | ## Antes de liberar envios reais 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" }' ``` ## Resposta de envio aceito JSON · HTTP 200 ```json { "success": true, "data": { "message_id": "wamid.EXEMPLO", "status": "sent", "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. 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", "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. | | 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 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**. | 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. | | 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. 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. ## 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", "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", "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.accepted | Envios aceitos pela API da Meta. Uma mensagem aceita ainda pode falhar depois. | | campaign.dispatch\_failed | Falhas durante o envio. As falhas posteriores de entrega estão em counts.failed. | | 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. | | records / untracked | records conta destinatários com evidência disponível; 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. | | 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. | | 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. ## 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. 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` | | Chave de idempotência | `pedido-8421-aviso-entrega-v1` | | Dados de envio | Destinatário, phone\_number\_id, 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", "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" } } ``` 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. > `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, "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: "patrociniotech", 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.id); if (item.processado) return; const { event, data } = item.corpo; if (event === "message.status") { // Atualize por message_id, client_ref e número da empresa. // sent < delivered < read; failed pode substituir sent, // mas não pode apagar uma entrega ou leitura confirmada. await tx.atualizarStatusSemRegressao(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. ## 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 } } ``` ## 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\_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 --- # 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. | ### 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. | | 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; 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 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