# 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. Comece pelo guia inicial; 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. Use a chave somente no servidor. 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. - [Conceitos do WhatsApp](https://patrociniotech.com/documentacao-api/conceitos.md): Janela de 24h, template, opt-in, qualidade e limite diário. ## 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. - [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. ## 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. Consulte seus templates 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 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 --- # 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" } } ``` | 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 você. | | opt\_in | accepted, pending, declined ou nulo. Vale para campanha, não para a janela. | ## 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. 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. ## 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 ### Destinatário e conteúdo | Campo | Regra | | --- | --- | | to | Obrigatório. Telefone com país e DDD, somente dígitos. Ex.: 5521999999999. | | 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 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. ## Relacionar o envio ao seu pedido `client_ref` é opcional, tem até 64 caracteres e volta nos eventos de status. Use um identificador da sua operação, como `pedido-8421`. Ele ajuda na conciliação, mas não substitui a `Idempotency-Key`. 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 em centavos; a API confere a soma dos itens e da taxa. ### 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. | ## Validar um telefone ### 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 --- # 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. ## Como ler o modelo 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. > Aprovações e avisos de qualidade aparecem em Notificações do painel. O webhook público atual oferece `message.status` e `message.received`; ele não publica mudanças de templates. Para acompanhar modelos na integração, consulte este catálogo com `status=ALL`. ## 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). --- 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. Somar vários processos com limites separados pode ultrapassar o teto. ## 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); const { delivery_id, event } = req.body; if (!delivery_id || !["message.status", "message.received"].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) { await transacao(async (tx) => { const item = await tx.bloquearEvento(evento.id); if (item.processado) return; const { event, data } = item.corpo; if (event === "message.status" && !data.test) { // 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); } await tx.marcarEventoProcessado(item.id); }); } ``` > Implemente os índices únicos, a fila e as transações no seu banco. 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 | | --- | --- | --- | | `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. Aprovação, rejeição e qualidade de templates são avisos operacionais do painel. Não chegam nesses dois eventos de mensagem. Para acompanhar esses estados na integração, consulte [o catálogo de templates](https://patrociniotech.com/documentacao-api/templates.md) com `status=ALL`. ## 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. | | 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 payload do botão e o ID da opção não são expostos neste evento. | | 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 | message.status ou message.received. 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); const { delivery_id, event } = req.body; if (!delivery_id || !["message.status", "message.received"].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); // 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 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. 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. ## Listar ### GET /public/v1/deliveries | Parâmetro | O que é | | --- | --- | | status | pending, delivering, delivered ou failed. Sem ele, vêm todas. | | limit | Até 200 por página. Padrão 50. | | 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 } } ``` > 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. > 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. > 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. } ``` ## 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. ## 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 tipos, campos obrigatórios e limites no OpenAPI. | | invalid\_recipient | Use telefone com país e DDD, somente dígitos. | | 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. | ### 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\_experiment | A entrega de marketing foi limitada para esse destinatário. Não tente contornar a restrição com repetições. | | delivery\_limited | A Meta limitou a entrega por qualidade ou engajamento. Confira o número e o destinatário antes de outro envio. | | 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. Os limites da Meta continuam aplicáveis. | | 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. | ## Exemplo de novas tentativas Este exemplo repete condições temporárias conhecidas, falhas de conexão e 5xx. Mantém corpo e chave, respeita `Retry-After` e encerra após quatro tentativas. 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### 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