# Pré-autorizando uma Confirmação Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/confirmations/issue-confirmation POST /confirmations Emite antecipadamente o token de confirmação que uma ação destrutiva registrada exigiria no header X-Confirmation-Token. Algumas operações da API são disruptivas o suficiente para exigir uma confirmação explícita: elas trocam algo que já está em uso e, se disparadas sem querer, causam uma interrupção. Essas operações exigem o header `X-Confirmation-Token` e, sem ele, respondem `409` (`confirmation_required`) trazendo, no próprio corpo do erro, um token pronto para repetir a chamada. Este endpoint existe para quem prefere não depender desse `409`: você pede o token antecipadamente, já sabendo qual operação vai confirmar, e envia a chamada original já com o header preenchido. As duas formas produzem exatamente o mesmo tipo de token e são totalmente intercambiáveis. ### O token O `confirmation_token` retornado é assinado pela própria API. Ele é vinculado ao usuário autenticado, à ação (`action`), ao recurso (`resource`) e aos campos enviados em `params` que definem a mudança: um token emitido para um conjunto de parâmetros não confirma uma chamada com parâmetros diferentes. Ele expira em `expires_at`, cerca de 5 minutos após a emissão. ### Ações registradas O campo `action` só aceita ações que a API reconhece como confirmáveis. Cada uma exige um campo diferente em `params`, que é exatamente o que o token passa a confirmar: | `action` | `params` obrigatório | | ----------------------------- | ----------------------------------------------------------- | | `instance.migrate_connection` | `to`: o tipo de conexão de destino (`unofficial` ou `waba`) | | `instance.reconnect` | `phone_number_id`: o ID do número de telefone de destino | Omitir o campo obrigatório de `params` é um erro de validação `400`, de propósito: um token emitido sem ele confirmaria uma mudança vazia e falharia depois, na operação real, com um erro de incompatibilidade difícil de entender. Este endpoint não verifica se o `resource` existe nem se pertence a você, de propósito. O token só passa a valer alguma coisa quando a operação que ele confirma é chamada de verdade, e essa operação sempre confere a posse do recurso por conta própria antes de agir. Um token emitido para um recurso de outra conta simplesmente não confirma nada quando usado. ### O que isso não é Essa confirmação não é uma camada de segurança nem um passo de autorização. Quem já tem o token de acesso da sua conta sempre consegue emitir uma confirmação, para qualquer ação registrada. Ela existe para tornar deliberada uma mudança disruptiva que, de outra forma, poderia acontecer por engano, e não para impedir chamadas mal-intencionadas. ### Exemplo ```bash cURL theme={null} curl -X POST https://api.zapsterapi.com/v1/confirmations \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "action": "instance.reconnect", "resource": "ozj35qv418rpmlrb", "params": { "phone_number_id": "1016102021584086" } }' ``` ```javascript Node.js (fetch) theme={null} const response = await fetch('https://api.zapsterapi.com/v1/confirmations', { method: 'POST', headers: { Authorization: 'Bearer SEU_TOKEN', 'Content-Type': 'application/json', }, body: JSON.stringify({ action: 'instance.reconnect', resource: 'ozj35qv418rpmlrb', params: { phone_number_id: '1016102021584086' }, }), }) const confirmation = await response.json() console.log(confirmation.confirmation_token) ``` ```python Python theme={null} import requests response = requests.post( "https://api.zapsterapi.com/v1/confirmations", headers={"Authorization": "Bearer SEU_TOKEN"}, json={ "action": "instance.reconnect", "resource": "ozj35qv418rpmlrb", "params": {"phone_number_id": "1016102021584086"}, }, timeout=30, ) confirmation = response.json() print(confirmation["confirmation_token"]) ``` Use o `confirmation_token` retornado no header `X-Confirmation-Token` da chamada original, dentro dos `expires_at` retornados junto. # Adicionar Participantes Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/add-participants POST /wa/instances/{instance_id}/groups/{group_id}/participants Adicionar novo participante em um grupo. # Criar um Grupo Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/create POST /wa/instances/{instance_id}/groups Este endpoint permite a criação de grupos no WhatsApp. Para criar um grupo, apenas o campo `name` é obrigatório. Os demais campos como foto de perfil, descrição e participantes são opcionais. Após a criação, você pode modificar qualquer um destes campos utilizando o endpoint de [Atualizar um Grupo](/pt-BR/v1/api-reference/groups/update-data). ## Foto de Perfil Para definir uma foto de perfil durante a criação do grupo, utilize o campo `profile_picture`. Este campo aceita tanto uma URL quanto uma string em formato base64 contendo a imagem. Recomendamos fortemente o uso de URLs ao invés de base64, pois é uma prática mais eficiente e adequada, especialmente para arquivos de maior tamanho. Embora o formato base64 seja suportado, seu uso pode impactar negativamente a performance da requisição. ## Participantes Para adicionar participantes durante a criação do grupo, utilize o campo `participants`. Os números informados neste campo serão automaticamente adicionados como membros após a criação do grupo ser concluída. **Importante:** Para adicionar participantes ao grupo, é necessário que: 1. O usuário tenha configurado a opção "Who can add me to groups" como "Everyone" nas configurações de privacidade do WhatsApp, ou 2. O usuário tenha a instância salva como contato Caso contrário, o usuário só poderá entrar no grupo através de um link de convite. # Remover Participantes Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/delete-participants DELETE /wa/instances/{instance_id}/groups/{group_id}/participants Remover participante(s) de um grupo. # Rebaixar Participantes Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/demote-participants DELETE /wa/instances/{instance_id}/groups/{group_id}/demote-participants Rebaixar participantes para membros comum de um grupo. # Listar Grupos Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/fetch-all GET /wa/instances/{instance_id}/groups Listar todos os grupos em que a instância participa # Listar Participantes Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/fetch-participants GET /wa/instances/{instance_id}/groups/{group_id}/participants Listar todos os participantes de um grupo # Entrar em Grupos Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/join-group POST /wa/instances/{instance_id}/groups/join Entre em grupos usando um código de convite # Promover Participantes Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/promote-participants POST /wa/instances/{instance_id}/groups/{group_id}/promote-participants Promover participantes para administrador de um grupo. # Atualizar um Grupo Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/groups/update-data PATCH /wa/instances/{instance_id}/groups/{group_id} Utilize este endpoint para atualizar os campos `name` (Nome), `profile_picture` (Foto de perfil) e/ou `description` (Descrição do perfil) de um determinado. Todas as atualizações são opcionais, ou seja, apenas as informações presentes no corpo da requisição serão alteradas. # Criando Instância Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/create-instance POST /wa/instances Ao criar uma instância, você escolhe o tipo de conexão: * **Não oficial** (`connection_type: "unofficial"`): o padrão. Depois de criar, você conecta via QR code ou código de pareamento. * **Oficial WABA** (`connection_type: "waba"`): usa a API oficial da Meta. Você precisa fornecer as credenciais no objeto `waba`. Para instâncias WABA, existem duas formas de obter as credenciais: 1. **Embedded Signup** (recomendado): fluxo OAuth pelo dashboard, sem precisar mexer no Meta Business Manager. Veja o [guia passo a passo](/pt-BR/v1/guides/connect-waba-instance). 2. **Token manual**: você gera um System User Token no Meta Business Manager e passa direto na API. Veja o [método avançado](/pt-BR/v1/guides/connect-waba-instance#método-2-token-manual-avançado). Quando `connection_type` é `waba`, o objeto `waba` com `access_token`, `phone_number_id` e `waba_id` é obrigatório. Para instâncias não oficiais, esse campo é ignorado. Para entender as diferenças entre os dois tipos, veja o [comparativo WABA vs não oficial](/pt-BR/v1/guides/waba-vs-unofficial). # Criando Webhooks Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/create-webhook POST /wa/instances/{instance_id}/webhooks Este endpoint permite registrar um novo **webhook** para uma instância específica. Os webhooks são usados para receber notificações em tempo real sobre eventos importantes na instância, como mensagens recebidas ou mudanças de status. **⚠️ Importante:** É obrigatório fornecer **ou** uma `url` **ou** um `webhook_id`. Se nenhum dos dois for informado, a requisição falhará. ### 🔍 Considerações * Pelo menos um evento deve ser especificado na criação do webhook. * Se uma `url` e um `webhook_id` forem fornecidos ao mesmo tempo, o `webhook_id` será priorizado. * O webhook pode ser desativado posteriormente usando a propriedade `enabled`. # Excluindo Instância Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/delete-instance DELETE /wa/instances/{instance_id} Utilize este endpoint quando você precisar excluir definitivamente uma instância. **Atenção**: Esta é uma ação irreversível, sua instância será desconectada (caso esteja) e não poderá mais ser usada para para recebimento ou envio de mensagens. # Excluindo Webhooks Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/delete-webhook DELETE /wa/instances/{instance_id}/webhooks/{webhook_id} Este endpoint permite **desvincular um webhook de uma instância**, removendo sua associação com a instância específica. **O webhook não será excluído**, apenas **desassociado** da instância, permanecendo ativo em outras instâncias e na conta do usuário caso ainda esteja vinculado a outras instâncias. # Dados da Instância Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/instance-details GET /wa/instances/{instance_id} # Obtendo QR Code Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/instance-qrcode GET /wa/instances/{instance_id}/qrcode Este endpoint permite capturar o QR Code de uma instância para autenticação no WhatsApp. Ele retorna a imagem no formato `image/png`, permitindo exibi-la diretamente em uma tag `` no HTML, se necessário. Para acessar este recurso, é obrigatório fornecer um **token de acesso**, que pode ser passado no cabeçalho da requisição ou na **query string**. Para utilizar este endpoint, é necessário fornecer um **token de acesso** válido. A forma recomendada é enviá-lo no **cabeçalho da requisição**. Opcionalmente, ele pode ser passado na URL como **query string**, mas isso expõe o token e não é recomendado. ### Usando o Token na Query String ⚠️ (Caso de Uso Específico) ```html theme={null} ``` **⚠️ Importante:** O uso do token na URL pode expô-lo em logs de servidores e históricos de navegadores, o que representa um risco de segurança. Sempre prefira a autenticação via cabeçalho HTTP. 🚀 **Por que essa opção está disponível?** A renderização do QR Code via **query string** foi criada para permitir que o cliente compartilhe o link diretamente com seu usuário final, usando um **token temporário**. Dessa forma, o usuário pode simplesmente abrir o link no navegador, visualizar o QR Code e conectar a instância. Essa funcionalidade pode ser útil em cenários onde o cliente final não tem acesso ao painel da API, mas precisa conectar a conta do WhatsApp rapidamente. ### ❌ QR Code Indisponível Se a instância já estiver conectada ou o QR Code não estiver disponível, a API retornará um erro: ```json theme={null} { "errors": [ { "code": "qrcode_unavailable", "message": "The instance's QR code is not available. This might be because your instance is already connected." } ] } ``` ### 📡 QR Code em Tempo Real Se deseja atualizar o QR Code em tempo real sem que seu usuário precise sair da sua plataforma, recomendamos utilizar os eventos da instância para acompanhar as atualizações. Para isso, você pode configurar um webhook para escutar o evento `instance.qrcode`. Sempre que o QR Code da sua instância for atualizado, seu sistema receberá uma notificação automática, permitindo que você atualize a exibição do QR Code em tempo real e garanta uma experiência fluida para o usuário. ### 📌 Considerações Finais * O QR Code muda constantemente, então é necessário **atualizar periodicamente** até a conexão ser estabelecida. * Instâncias conectadas **não possuem QR Code disponível**. * Para segurança, evite expor tokens na URL; prefira enviá-los via cabeçalho HTTP. * Se for utilizar a query string, certifique-se de que o **token seja temporário** para evitar riscos de exposição. # Listando Instâncias Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/list-instances GET /wa/instances # Migrando o Tipo de Conexão Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/migrate-instance POST /wa/instances/{instance_id}/migrate Migra a instância entre a conexão não oficial (QR Code) e a API oficial (WABA), preservando o ID, os webhooks e as configurações. Use este endpoint para trocar o tipo de conexão de uma instância existente sem perder nada da sua integração: o ID da instância, os webhooks cadastrados e as configurações permanecem exatamente como estavam. A migração funciona nos dois sentidos. ### Não oficial para API oficial (WABA) Envie `connection_type: "waba"` junto com o objeto `waba`, contendo `access_token`, `phone_number_id` e `waba_id`. O Embedded Signup (login com o Facebook) só está disponível pelo painel da Zapster: ele depende de uma página hospedada por nós e não pode ser reproduzido por chamada de API. Para migrar por aqui, gere um System User Token no Meta Business Manager e informe-o no objeto `waba`. O ambiente de execução da conexão não oficial é desativado e o número passa a operar pela Cloud API da Meta. Ao final, a instância fica `connected`. ### API oficial (WABA) para não oficial Envie `connection_type: "unofficial"`, sem credenciais. A configuração WABA é removida com segurança na Meta e um novo ambiente de conexão é preparado. A instância fica `disconnected`, aguardando a leitura do QR Code (ou código de pareamento) para conectar o número. ### Webhooks são revalidados Os tipos de conexão suportam conjuntos diferentes de eventos de webhook, e a API oficial suporta um conjunto menor. Antes de migrar, os webhooks já cadastrados na instância são validados contra o tipo de destino: se algum estiver inscrito em um evento que o destino não emite, a migração é rejeitada com `400` (`unsupported_webhook_event`), listando todos os eventos incompatíveis e os eventos válidos. Ajuste as inscrições e repita a chamada. ### Confirmação para instâncias em uso Migrar uma instância que não está `disconnected` interrompe o serviço dela durante a troca, mesmo quando ela está apenas `offline`: só a instância já `disconnected` dispensa a confirmação. Nesse caso, a chamada exige o header `X-Confirmation-Token`. Esse token não é um valor arbitrário: é assinado pela própria API e vinculado ao usuário autenticado, à ação (`instance.migrate_connection`), à instância e ao tipo de conexão de destino. Um token obtido para migrar para `waba` não confirma uma migração para `unofficial`, e vice-versa. Ele expira em cerca de 5 minutos. Existem duas formas de obter o token, e ambas produzem o mesmo resultado: 1. **Chame o endpoint sem o header.** A resposta é `409` (`confirmation_required`), e o corpo traz em `details.confirmation_token` um token já pronto para a chamada que você acabou de tentar, junto com `expires_at`, `from`, `to`, `status` e `resource`. Basta repetir a chamada idêntica com esse valor no header. 2. **Peça o token antes de tentar migrar**, em [`POST /confirmations`](/pt-BR/v1/api-reference/confirmations/issue-confirmation), informando `action: "instance.migrate_connection"`, o `resource` (ID da instância) e `params.to` com o tipo de conexão de destino. Se o token estiver ausente, malformado, expirado ou não corresponder exatamente à chamada (outro tipo de destino, por exemplo), a API responde com o mesmo `409` e um token novo pronto para uso. O campo `details.reason` existe só para depuração: o fluxo do cliente é sempre "recebi 409, pego o token, tento de novo", nunca uma decisão baseada no motivo. Essa confirmação não é uma camada de segurança. Quem tem o token de acesso da conta sempre consegue emitir uma confirmação; ela existe para tornar deliberada uma migração disruptiva que aconteceria por engano, não para impedir chamadas mal-intencionadas. # Obtendo Cód. Pareamento Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/pairing-code POST /wa/instances/{instance_id}/pairing-code O **código de pareamento** é uma alternativa ao QR Code para conectar uma instância do WhatsApp a um dispositivo. Ele permite autenticar e vincular a conta sem a necessidade de escanear um QR Code, tornando o processo mais prático em algumas situações. ### 🛠️ Quando Usar o Código de Pareamento? * Caso esteja conectando um dispositivo sem acesso a uma câmera para escanear o QR Code. * Se o processo de conexão precisar ser automatizado em um fluxo onde a leitura de QR Code não é viável. * Para oferecer uma opção alternativa ao QR Code, facilitando a conexão. ### 📌 Como Funciona? * O WhatsApp gera um código temporário para vinculação. * Esse código deve ser inserido no aplicativo WhatsApp no dispositivo que deseja conectar. * Após a confirmação, a instância será pareada e estará pronta para uso. # Desligando Instância Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/power-instance-off POST /wa/instances/{instance_id}/power-off Este endpoint permite desligar uma instância que está conectada ou desconectada. Ao ser ligada novamente, a instância pode retornar já conectada, mantendo a sessão anterior. Em alguns casos, se a instância ficar desligada por muito tempo ou se o WhatsApp desconectar dispositivos inativos, ela poderá retornar desconectada ao ser ligada. Disponível apenas para instâncias não oficiais. Instâncias oficiais (WhatsApp Cloud API) rodam na infraestrutura da Meta, estão sempre disponíveis e não podem ser ligadas, desligadas ou reiniciadas. A requisição retorna `400` com o código `waba_feature_not_supported`. # Ligando Instância Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/power-instance-on POST /wa/instances/{instance_id}/power-on Este endpoint permite ligar uma instância que está atualmente offline. Se a instância já estiver conectada, esta ação não terá efeito. Disponível apenas para instâncias não oficiais. Instâncias oficiais (WhatsApp Cloud API) rodam na infraestrutura da Meta, estão sempre disponíveis e não podem ser ligadas, desligadas ou reiniciadas. A requisição retorna `400` com o código `waba_feature_not_supported`. # Reconectando uma Instância WABA Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/reconnect-instance POST /wa/instances/{instance_id}/reconnect Reconecta uma instância WABA, rotacionando as credenciais ou apontando o mesmo ID para outro número, preservando os webhooks e as configurações. Use este endpoint para reconectar uma instância da API oficial (WABA) sem precisar excluí-la e criar outra. O ID da instância, os webhooks cadastrados e as configurações permanecem exatamente como estavam: você só fornece novamente as credenciais do número. Diferente do que valia antes, a instância não precisa estar `disconnected` para chamar este endpoint. O comportamento muda conforme a situação: | Situação | O que acontece | | ------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | Instância `disconnected` | Reconecta normalmente, sem exigir confirmação | | Instância em uso, **mesmo** `phone_number_id` | Rotaciona as credenciais (ex.: token revogado no Meta Business Manager), sem exigir confirmação | | Instância em uso, `phone_number_id` **diferente** | Aponta a instância para outro número, exige o header `X-Confirmation-Token` | Rotacionar as credenciais do mesmo número é uma operação segura e idempotente, e é o caso mais comum: um token revogado no Meta Business Manager, por exemplo. Já apontar a instância para outro número é o único caso realmente arriscado, pois o ID, os webhooks e as configurações continuam os mesmos, mas a instância passa a atender por um número diferente. Por isso só esse caso pede confirmação: uma confirmação que aparecesse em toda chamada viraria um clique automático e deixaria de proteger qualquer coisa. ### Credenciais Envie o objeto `waba` com `access_token`, `phone_number_id` e `waba_id` — os mesmos campos aceitos na criação de instância. Campos opcionais como `app_id`, `app_secret`, `webhook_verify_token` e `auth_method` também são aceitos. Se o número foi conectado pelo login com o Facebook, em vez de um token gerado no Meta Business Manager, faça a reconexão pelo painel da Zapster. Esse login precisa acontecer em uma página hospedada por nós e não pode ser reproduzido por chamada de API. ### O que acontece na reconexão Você não precisa refazer nada do que já estava configurado. Ao receber as credenciais, a Zapster: 1. **Confere o token com a Meta**, para garantir que ele é válido e tem acesso ao número informado. 2. **Guarda as credenciais com segurança**, criptografadas. 3. **Aponta as mensagens do número de volta para a sua instância**, para que os webhooks voltem a chegar no endereço que você já tinha cadastrado. 4. **Conclui o registro do número na Meta**, quando ainda for necessário. Terminado isso, a instância volta ao status `connected` e volta a enviar e receber normalmente. Como o ID não muda, **nada precisa ser alterado no seu código**. Se alguma etapa falhar (token inválido, por exemplo), a instância permanece no estado anterior e a resposta traz o motivo. Basta corrigir e chamar de novo. ### Confirmação para trocar o número da instância Apontar uma instância em uso para um `phone_number_id` diferente do atual exige o header `X-Confirmation-Token`. Esse token não é um valor arbitrário: é assinado pela própria API e vinculado ao usuário autenticado, à ação (`instance.reconnect`), à instância e ao `phone_number_id` de destino. Um token obtido para um número não confirma a troca para outro. Ele expira em cerca de 5 minutos. Existem duas formas de obter o token, e ambas produzem o mesmo resultado: 1. **Chame o endpoint sem o header.** Se as credenciais enviadas apontam para um número diferente do atual, a resposta é `409` (`confirmation_required`), e o corpo traz em `details.confirmation_token` um token já pronto para a chamada que você acabou de tentar, junto com `expires_at`, `from`, `to`, `status` e `resource`. Basta repetir a chamada idêntica com esse valor no header. 2. **Peça o token antes de tentar reconectar**, em [`POST /confirmations`](/pt-BR/v1/api-reference/confirmations/issue-confirmation), informando `action: "instance.reconnect"`, o `resource` (ID da instância) e `params.phone_number_id` com o número de destino. Se o token estiver ausente, malformado, expirado ou não corresponder exatamente à chamada (outro `phone_number_id`, por exemplo), a API responde com o mesmo `409` e um token novo pronto para uso. O campo `details.reason` existe só para depuração: o fluxo do cliente é sempre "recebi 409, pego o token, tento de novo", nunca uma decisão baseada no motivo. Essa confirmação não é uma camada de segurança. Quem tem o token de acesso da conta sempre consegue emitir uma confirmação; ela existe para tornar deliberada uma troca de número que aconteceria por engano, não para impedir chamadas mal-intencionadas. ### Exemplos Os exemplos abaixo cobrem o caso mais comum: rotacionar as credenciais do mesmo número, que não exige o header `X-Confirmation-Token`. Para apontar a instância a outro número, adicione o header com o token obtido conforme a seção anterior. ```bash cURL theme={null} curl -X POST https://api.zapsterapi.com/v1/wa/instances/ozj35qv418rpmlrb/reconnect \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "waba": { "access_token": "EAAxxxxxxx...", "phone_number_id": "1016102021584086", "waba_id": "419378847918255" } }' # Trocando o número da instância, com o token de confirmação: curl -X POST https://api.zapsterapi.com/v1/wa/instances/ozj35qv418rpmlrb/reconnect \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -H "X-Confirmation-Token: eyJ2IjoxLCJhY3QiOiJpbnN0YW5jZS5yZWNvbm5lY3QifQ.q1w2e3r4t5y6" \ -d '{ "waba": { "access_token": "EAAxxxxxxx...", "phone_number_id": "1016102099998765", "waba_id": "419378847918255" } }' ``` ```javascript Node.js (fetch) theme={null} const instanceId = 'ozj35qv418rpmlrb' const response = await fetch( `https://api.zapsterapi.com/v1/wa/instances/${instanceId}/reconnect`, { method: 'POST', headers: { Authorization: 'Bearer SEU_TOKEN', 'Content-Type': 'application/json', }, body: JSON.stringify({ waba: { access_token: 'EAAxxxxxxx...', phone_number_id: '1016102021584086', waba_id: '419378847918255', }, }), }, ) const instance = await response.json() console.log(instance.status) // connected ``` ```javascript Node.js (axios) theme={null} import axios from 'axios' const instanceId = 'ozj35qv418rpmlrb' const { data: instance } = await axios.post( `https://api.zapsterapi.com/v1/wa/instances/${instanceId}/reconnect`, { waba: { access_token: 'EAAxxxxxxx...', phone_number_id: '1016102021584086', waba_id: '419378847918255', }, }, { headers: { Authorization: 'Bearer SEU_TOKEN' } }, ) console.log(instance.status) // connected ``` ```javascript JavaScript (navegador) theme={null} // Nunca chame a Zapster direto do navegador: o seu token daria acesso total // à conta a quem abrisse o DevTools. Chame o seu próprio backend, e é ele // quem fala com a Zapster usando o token guardado no servidor. const response = await fetch('/api/instancias/ozj35qv418rpmlrb/reconectar', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ access_token: 'EAAxxxxxxx...', phone_number_id: '1016102021584086', waba_id: '419378847918255', }), }) const instance = await response.json() console.log(instance.status) // connected ``` ```python Python theme={null} import requests instance_id = "ozj35qv418rpmlrb" response = requests.post( f"https://api.zapsterapi.com/v1/wa/instances/{instance_id}/reconnect", headers={"Authorization": "Bearer SEU_TOKEN"}, json={ "waba": { "access_token": "EAAxxxxxxx...", "phone_number_id": "1016102021584086", "waba_id": "419378847918255", } }, timeout=30, ) instance = response.json() print(instance["status"]) # connected ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { instanceID := "ozj35qv418rpmlrb" body, _ := json.Marshal(map[string]any{ "waba": map[string]string{ "access_token": "EAAxxxxxxx...", "phone_number_id": "1016102021584086", "waba_id": "419378847918255", }, }) url := fmt.Sprintf( "https://api.zapsterapi.com/v1/wa/instances/%s/reconnect", instanceID, ) req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer SEU_TOKEN") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() var instance map[string]any json.NewDecoder(res.Body).Decode(&instance) fmt.Println(instance["status"]) // connected } ``` ```php PHP theme={null} [ 'access_token' => 'EAAxxxxxxx...', 'phone_number_id' => '1016102021584086', 'waba_id' => '419378847918255', ], ]); $ch = curl_init("https://api.zapsterapi.com/v1/wa/instances/{$instanceId}/reconnect"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer SEU_TOKEN', 'Content-Type: application/json', ], ]); $instance = json_decode(curl_exec($ch), true); curl_close($ch); echo $instance['status']; // connected ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class ReconnectInstance { public static void main(String[] args) throws Exception { String instanceId = "ozj35qv418rpmlrb"; String payload = """ { "waba": { "access_token": "EAAxxxxxxx...", "phone_number_id": "1016102021584086", "waba_id": "419378847918255" } } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create( "https://api.zapsterapi.com/v1/wa/instances/" + instanceId + "/reconnect")) .header("Authorization", "Bearer SEU_TOKEN") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(payload)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` # Reiniciando Instância Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/restart-instance POST /wa/instances/{instance_id}/restart Este endpoint reinicia uma instância, colocando-a offline temporariamente por um curto período. A instância poderá ficar offline por até 1 minuto durante o processo de reinicialização. Em alguns casos, dependendo da estabilidade da sessão, pode ser necessário um novo login no WhatsApp. Disponível apenas para instâncias não oficiais. Instâncias oficiais (WhatsApp Cloud API) rodam na infraestrutura da Meta, estão sempre disponíveis e não podem ser ligadas, desligadas ou reiniciadas. A requisição retorna `400` com o código `waba_feature_not_supported`. # Atualizando a Instância Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/update-instance PATCH /wa/instances/{instance_id} Atualiza os dados da instância na plataforma, como o nome e os metadados. Use este endpoint para atualizar os dados da própria instância na plataforma: o nome (`name`), os metadados (`metadata`) e o identificador de pesquisa (`lookup_key`). Ele não altera o perfil do WhatsApp (nome de exibição, foto, descrição). Para isso, use o endpoint de [atualização de perfil](/pt-BR/v1/api-reference/instance/update-profile). As configurações de comportamento têm o seu próprio endpoint de [atualização de configurações](/pt-BR/v1/api-reference/instance/update-settings). ### Atualize só o que precisar Você não precisa enviar todos os campos a cada chamada. Envie apenas o que quer mudar e tudo o que ficar de fora continua exatamente como está. Nenhum campo aceita `null`: `name` é sempre um texto, e os valores de `metadata` são sempre um texto ou um número. Para "limpar" um valor, veja a regra da string vazia logo abaixo. ### Como o `metadata` é atualizado O `metadata` também é atualizado de forma parcial: as chaves enviadas são alteradas e todas as outras permanecem intactas. Não é necessário (nem recomendado) reenviar o objeto inteiro. * Para **alterar ou criar** uma chave, envie a chave com o novo valor. * Para **manter** uma chave como está, simplesmente não a envie. * Para **limpar** o valor de uma chave, envie uma string vazia `""`. Por exemplo, se a instância tem os metadados `customer_id`, `customer_name` e `campaign`, a requisição abaixo altera `customer_id`, limpa `campaign` e mantém `customer_name` intacto: ```json theme={null} { "metadata": { "customer_id": "789012", "campaign": "" } } ``` ### Chaves reservadas `wa_` Chaves de metadata iniciadas com `wa_` são gerenciadas automaticamente pela plataforma e funcionam como somente leitura: se você enviá-las na requisição, elas são ignoradas e os valores originais são preservados. Use outros nomes para as suas próprias chaves. # Atualizando Perfil Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/update-profile PATCH /wa/instances/{instance_id}/profile Utilize este endpoint para atualizar os campos `name` (Nome), `profile_picture` (Foto de perfil) e/ou `about` (Descrição do perfil) no perfil do usuário. Todas as atualizações são opcionais, ou seja, apenas as informações presentes no corpo da requisição serão alteradas. ### Comportamento Se você enviar apenas uma ou duas informações, apenas esses campos serão atualizados. Campos não incluídos na requisição permanecerão inalterados. **Nota**: Em alguns casos, a atualização do nome do perfil pode falhar. Estamos continuamente trabalhando para melhorar este comportamento, porém, há dependências externas que podem afetar o sucesso da operação. # Atualizando Configurações Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/update-settings PATCH /wa/instances/{instance_id}/settings Atualiza as configurações de comportamento da instância. Envie apenas o que quer mudar: tudo o que ficar de fora continua exatamente como está. Use este endpoint para atualizar as configurações de comportamento da sua instância: rejeição de ligações, delay antes do envio de mensagens, presença (online/digitando), limpeza de conversa e confirmação de leitura automática. ### Atualize só o que precisar Você não precisa enviar todas as configurações a cada chamada. Envie apenas o que quer mudar e tudo o que ficar de fora continua exatamente como está. Isso vale até para configurações com subcampos, como `message_delay` e o formato segmentado de `read_confirmation`: cada subcampo que você não enviar também fica como estava. Por exemplo, enviando apenas: ```json theme={null} { "settings": { "read_confirmation": { "chats": true } } } ``` Você liga a confirmação de leitura automática só para conversas individuais, e `groups` e `status` seguem do jeito que estavam. ### Configurações disponíveis * **`call_rejection`**: define se ligações recebidas devem ser rejeitadas automaticamente. Aceita `all` (rejeita todas), `none` (não rejeita nenhuma), `video_only` (rejeita apenas chamadas de vídeo) ou `audio_only` (rejeita apenas chamadas de áudio). * **`message_delay`**: adiciona um atraso proposital antes do envio de cada mensagem, útil para simular um comportamento mais humano. É um objeto com `enabled` (liga/desliga o delay), `min` e `max` (limites em segundos, o tempo real é sorteado dentro desse intervalo). * **`delay_per_word`**: quando ligado, o delay antes do envio passa a ser calculado com base na quantidade de palavras da mensagem, até um teto de 10 segundos. Quando `message_delay` está configurado, ele tem prioridade e `delay_per_word` é ignorado. * **`presence_behavior`**: controla quando a instância aparece como "online" para os contatos. `only_composing` mostra "online" apenas durante o envio da mensagem (recomendado), `always_online` mantém a instância sempre online e `always_offline` evita aparecer online exceto quando necessário para o envio. * **`delete_chat_after_sent`**: quando ligado, limpa a conversa no aparelho logo depois do envio da mensagem, para evitar acúmulo de histórico no dispositivo conectado. * **`read_confirmation`**: controla a confirmação de leitura automática das mensagens recebidas. Aceita os valores `never` (nunca confirma) ou `always` (sempre confirma), ou um objeto segmentado por tipo de conversa com os campos `chats`, `groups` e `status`, cada um booleano e independente. Quando o segmento `status` está ligado, os status publicados pelos seus contatos são marcados como vistos assim que recebidos. Mensagens enviadas pela própria instância, listas de transmissão e publicações de canais nunca recebem confirmação automática, independentemente dessa configuração. Se você precisa de controle fino sobre o momento exato da confirmação de leitura, por exemplo, marcar como lida só depois que um atendente responder, desligue a confirmação automática do segmento desejado e use o endpoint de [leitura sob demanda](/pt-BR/v1/api-reference/messages/read-message) para confirmar mensagem por mensagem. A atualização é aplicada à instância em tempo real, sem necessidade de reiniciá-la. # Atualizando Webhooks Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/instance/update-webhook PATCH /wa/instances/{instance_id}/webhooks/{webhook_id} Este endpoint permite **atualizar um webhook existente** para uma instância específica. ### ⚠️ Importante: Alteração de Nome ou URL Modificar o **nome** ou a **URL** de um webhook existente impactará **todas as instâncias** que utilizam esse webhook. Isso significa que qualquer instância vinculada será automaticamente atualizada para refletir as mudanças. Se precisar utilizar um **novo nome** ou uma **URL diferente** sem afetar instâncias existentes, recomendamos criar um **novo webhook** em vez de modificar um já em uso. Dessa forma, o novo webhook será vinculado **apenas aos recursos desejados**, evitando impactos inesperados em outras configurações. ### 🔍 Considerações * Todos os parâmetros são opcionais, sendo possível atualizar apenas os campos desejados. * Se a URL do webhook for alterada, a API pode exigir uma nova validação. * O webhook pode ser desativado definindo `enabled` como `false`. # Cancelar Mensagem Agendada Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/messages/cancel-message DELETE /wa/messages/{id} Cancela uma mensagem que ainda não foi enviada. Só funciona para mensagens com status `scheduled` ou `pending`. Mensagens já enviadas, falhadas ou canceladas retornam erro 422. Use este endpoint para cancelar uma mensagem que ainda não foi enviada. Só é possível cancelar mensagens com status `scheduled` ou `pending`. Se a mensagem já foi enviada, falhou ou já está cancelada, a API retorna erro 422. Ao cancelar, o status da mensagem muda para `canceled` e o campo `canceled_at` é preenchido com a data e hora do cancelamento. O cancelamento é definitivo. Não é possível "descancelar" uma mensagem. Se precisar reenviá-la, crie uma nova. # Listar Mensagens Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/messages/list-messages GET /wa/messages Lista todas as mensagens (imediatas e agendadas) dentro de um período. Retorna informações de status, entrega e leitura de cada mensagem. Usa paginação cursor-based para navegar entre páginas de resultados. O período máximo por consulta é de 90 dias. O quão longe no passado você pode consultar depende do seu plano (retenção do histórico). Lista todas as mensagens enviadas e agendadas dentro de um período. Retorna informações de status, entrega e leitura de cada mensagem. ## Parâmetros obrigatórios Os campos `from` e `to` definem o período da consulta. O período máximo é de 90 dias por requisição. O quão longe no passado você pode consultar depende do seu plano: | Plano | Retenção do histórico | | ---------- | --------------------- | | Essential | 24 horas | | Pro | 30 dias | | Enterprise | 180 dias | ## Filtros disponíveis Você pode combinar qualquer filtro na mesma requisição: | Filtro | Descrição | Exemplo | | ----------------- | ------------------------------ | -------------------------- | | `status` | Filtrar por status da mensagem | `status=scheduled` | | `instance_id` | Filtrar por instância | `instance_id=inst_xyz` | | `recipient` | Filtrar por destinatário | `recipient=5511999999999` | | `message_id` | Buscar pelo ID do WhatsApp | `message_id=wamid.HBgM...` | | `connection_type` | Filtrar por tipo de conexão | `connection_type=waba` | ## Paginação A listagem usa paginação cursor-based. Cada resposta inclui um objeto `meta` com: * `has_more`: indica se existem mais resultados * `next_cursor`: valor para passar no parâmetro `after` da próxima requisição * `limit`: quantidade de itens por página Para navegar entre páginas: 1. Faça a primeira requisição sem o parâmetro `after` 2. Se `has_more` for `true`, pegue o valor de `next_cursor` 3. Faça a próxima requisição com `after=` 4. Repita até `has_more` ser `false` O padrão é 20 itens por página. Você pode ajustar com o parâmetro `limit` (mínimo 1, máximo 100). ## Sobre o campo errors Quando uma mensagem falha (`status=failed`), o campo `errors` contém os detalhes do problema. Para instâncias WABA, inclui o código de erro da Meta e um link para a documentação. Erros comuns: | Código | Descrição | | ------ | ------------------------------------------------- | | 131047 | Janela de 24 horas expirada (precisa de template) | | 132000 | Template não encontrado ou parâmetros incorretos | | 132001 | Template não existe na tradução informada | # Marcar Mensagem como Lida Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/messages/read-message POST /wa/messages/{id}/read Marca uma mensagem recebida como lida sob demanda. Útil quando a leitura automática está desligada e o seu fluxo decide o momento certo de confirmar a leitura (por exemplo, depois que um atendente responde). A instância pode ser informada pelo campo `instance_id` no corpo da requisição ou pelo cabeçalho `X-Instance-Id`. Use este endpoint para marcar uma mensagem recebida como lida sob demanda. Ele é o complemento ideal da leitura automática desligada: seu sistema decide o momento certo de confirmar a leitura, por exemplo depois que um atendente responde ao cliente. O `id` da mensagem é o mesmo recebido nos webhooks de mensagem, como o campo `data.id` do evento `message.received`. A instância pode ser informada pelo campo `instance_id` no corpo ou pelo cabeçalho `X-Instance-Id`. A resposta traz o resultado da operação no campo `status`: * `read`: a mensagem foi marcada como lida. * `ignored`: a mensagem foi enviada pela própria instância, então não há leitura a confirmar. * `not_found`: a mensagem está fora do prazo de leitura (veja abaixo) ou nunca foi processada pela instância. ## Prazo para marcar como lida Cada mensagem fica disponível para leitura por um período limitado depois que chega na instância: * Conversas individuais e grupos: até 3 dias após o recebimento da mensagem. * Status: até 24 horas após a publicação, o mesmo período em que o status fica visível no WhatsApp. Depois desse prazo o retorno para aquele ID é `not_found`. Isso não indica uma falha na instância: a janela de leitura expirou e a confirmação não pode mais ser enviada. Para marcar várias mensagens de uma vez, use o endpoint de [leitura em lote](/pt-BR/v1/api-reference/messages/read-messages-batch). Se a conta do WhatsApp conectada estiver com a confirmação de leitura desativada nas configurações de privacidade, a mensagem é marcada como lida apenas localmente e o remetente não vê o tique azul. Este endpoint está disponível apenas para instâncias não oficiais. Instâncias com API oficial (WABA) retornam erro por enquanto. # Marcar Mensagens como Lidas (Lote) Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/messages/read-messages-batch POST /wa/messages/read Marca até 100 mensagens recebidas como lidas em uma única requisição. As mensagens podem pertencer a conversas diferentes. O resultado é discriminado por mensagem: `read` (lida), `ignored` (mensagem enviada pela própria instância, sem leitura a confirmar) ou `not_found` (mensagem fora do prazo de leitura ou nunca processada pela instância). Use este endpoint para marcar até 100 mensagens recebidas como lidas em uma única requisição. As mensagens podem pertencer a conversas diferentes, incluindo grupos, sem custo adicional. Os `ids` são os mesmos recebidos nos webhooks de mensagem, como o campo `data.id` do evento `message.received`. Requisições com mais de 100 IDs retornam erro de validação. A resposta discrimina o resultado por mensagem no campo `results`, nunca um sucesso genérico: * `read`: a mensagem foi marcada como lida. * `ignored`: a mensagem foi enviada pela própria instância, então não há leitura a confirmar. * `not_found`: a mensagem está fora do prazo de leitura (veja abaixo) ou nunca foi processada pela instância. ## Prazo para marcar como lida Cada mensagem fica disponível para leitura por um período limitado depois que chega na instância: * Conversas individuais e grupos: até 3 dias após o recebimento da mensagem. * Status: até 24 horas após a publicação, o mesmo período em que o status fica visível no WhatsApp. Depois desse prazo o retorno para aquele ID é `not_found`. Isso não indica uma falha na instância: a janela de leitura expirou e a confirmação não pode mais ser enviada. Para marcar uma única mensagem, use o endpoint de [leitura unitária](/pt-BR/v1/api-reference/messages/read-message). Se a conta do WhatsApp conectada estiver com a confirmação de leitura desativada nas configurações de privacidade, as mensagens são marcadas como lidas apenas localmente e o remetente não vê o tique azul. Este endpoint está disponível apenas para instâncias não oficiais. Instâncias com API oficial (WABA) retornam erro por enquanto. # Enviando Mensagens Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/messages/sending POST /wa/messages ## Respondendo Mensagens Para responder a mensagens já enviadas ou recebidas, utilize a propriedade `reply_to`. Isso permite que sua resposta seja vinculada diretamente à mensagem original, proporcionando um contexto claro na conversa (como mostrado na imagem abaixo). reply_to parameter **Limitação:** Atualmente, só é possível responder a mensagens que foram enviadas/recebidas nos últimos 7 dias. ## Agendando mensagens Para agendar uma mensagem para envio futuro, adicione o campo `send_at` ao body da requisição com a data e hora no formato ISO 8601. Quando `send_at` está presente, a resposta muda: * Status HTTP **201** (em vez de 200) * Corpo com `message_id`, `status: "scheduled"` e `send_at` em UTC O campo `send_at` é opcional. Quando não informado, a mensagem é enviada imediatamente como sempre. O `send_at` deve ser no mínimo 1 minuto no futuro. O limite máximo depende do seu plano. Veja os [limites por plano](/pt-BR/v1/concepts/scheduled-messages#limites-por-plano). Para cancelar uma mensagem agendada, veja [Cancelar mensagem](/pt-BR/v1/api-reference/messages/cancel-message). Para listar e rastrear todas as mensagens, veja [Listar mensagens](/pt-BR/v1/api-reference/messages/list-messages). # Verificação de Número Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/utils/fetch-recipient GET /wa/instances/{instance_id}/recipients/{recipient} ## Introdução Este endpoint permite verificar a existência de um destinatário em uma determinada instância do WhatsApp. A API retorna informações básicas sobre o destinatário, como o ID, se é uma conta comercial, o nome e a URL da foto de perfil. Esse endpoint é útil para garantir que o número fornecido está registrado e ativo na plataforma, antes de enviar mensagens ou realizar outras operações. ## Casos de Uso * **Validação antes de envio de mensagens**: Antes de enviar uma mensagem a um destinatário, verifique se o número existe e está registrado no WhatsApp para evitar erros e falhas no envio. * **Validação em formulários**: Utilize este endpoint para validar números de telefone inseridos por usuários em formulários. Antes de permitir que o usuário prossiga, a aplicação pode verificar se o número inserido está registrado no WhatsApp, garantindo que os dados fornecidos são válidos e que o destinatário é alcançável via WhatsApp. ## Pontos de Atenção O campo `name` nem sempre estará presente na resposta da API. Isso ocorre devido a uma limitação técnica onde a Zapster só consegue armazenar em cache o nome do contato se ele tiver conversado pelo menos uma vez com a instância. Se o contato nunca conversou, este campo estará ausente. O campo `profile_picture` retornará `null` em dois casos: se o contato tiver configurado a foto de perfil como privada ou se não houver nenhuma foto de perfil atribuída. # Verificação de Números em Lote Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/utils/fetch-recipient-batch POST /wa/instances/{instance_id}/recipients/batch ## Introdução Este endpoint é similar ao [Verificação de Número](/pt-BR/v1/api-reference/utils/fetch-recipient), mas permite a verificação de **até 100 números** em uma única requisição, o que é ideal para quando você precisa verificar a existência de múltiplos números de WhatsApp de forma eficiente. Para garantir o melhor uso dessa rota, por favor, consulte os [Pontos de Atenção](/pt-BR/v1/api-reference/utils/fetch-recipient#pontos-de-atencao), onde explicamos algumas nuances importantes sobre o `name` e `profile_picture`. ## Como funciona? A resposta desse endpoint será sempre uma **lista de objetos**, onde cada objeto representará o status de um número enviado na requisição. Cada número consultado será avaliado quanto à sua **existência no WhatsApp** e se o número corresponde a uma conta de **WhatsApp Business**. Além disso, caso o número seja inválido ou não encontrado, a resposta fornecerá detalhes sobre o erro. Vamos supor que você informe dois números para a consulta. Se um dos números existir e o outro não, a resposta será algo parecido com o seguinte exemplo: ```json Exemplo de Resposta theme={null} [ { "exists": true, "id": "551112341234", "is_business": false, "original": "551112341234", "profile_picture": "https://zapsterapi.s3.us-east-1.amazonaws.com/..." }, { "error": { "code": "recipient_not_found", "message": "The specified recipient could not be found." }, "exists": false, "original": "5511998765432" } ] ``` ## Diferença entre `original` e `id` Em alguns casos, o número informado na lista de consulta pode ser ajustado pelo WhatsApp. Isso geralmente acontece devido a variações regionais, como a inclusão ou exclusão do nono dígito para números de celular no Brasil. O campo `original` serve para garantir que você veja exatamente o número que foi enviado na sua requisição, enquanto o campo `id` mostra o número que o WhatsApp conseguiu encontrar após eventuais ajustes. ### Como Funciona? * **`original`**: É o número exatamente como você o enviou na requisição, sem nenhuma alteração. * **`id`**: É o número ajustado ou resolvido pelo WhatsApp, que pode ser diferente do original caso o WhatsApp tenha encontrado uma versão corrigida. A seguir, mostramos três exemplos que ilustram diferentes cenários de consulta de números, usando os campos `original` e `id`. ```json Nono Dígito Adicionado theme={null} { "exists": true "id": "5511998765432", "original": "5511998765432", } ``` ```json Nono Dígito Removido theme={null} { "exists": true "id": "5511998765432", "original": "551198765432", } ``` ```json Número Não Encontrado theme={null} { "original": "551123456789", "exists": false, "error": { "message": "The specified recipient could not be found.", "code": "recipient_not_found" } } ``` # Atualização de Presença Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/utils/presence-update PATCH /wa/instances/{instance_id}/presence ## Introdução Este endpoint permite que você atualize o status de presença de um destinatário em uma instância específica do WhatsApp. Você pode definir se o destinatário verá uma indicação de que você está "digitando..." ou "gravando...". Esta funcionalidade é útil para melhorar a experiência do usuário durante interações em tempo real, especialmente em aplicações que dependem de feedback instantâneo, como chats ao vivo ou integrações com IA. ## Casos de Uso * **Integração com OpenAI**: Enquanto a IA está gerando uma resposta, você pode definir a presença como "digitando..." ou "gravando..." para simular a experiência de uma interação humana e manter o usuário informado sobre o processamento em andamento. ## Estratégias de Uso * **Definindo a Presença de Curta Duração**: Utilize a estratégia `maximum_duration` para garantir que o status de "digitando..." ou "gravando..." seja exibido por um período específico de tempo. Esta estratégia é ideal para interações em que o tempo de resposta é previsível. * **Até a Próxima Mensagem**: Use a estratégia `until_next_message` para manter o status ativo até que a próxima mensagem seja enviada, proporcionando uma transição suave entre o momento em que o usuário percebe a presença e a entrega da mensagem final. Isso é útil em cenários onde o tempo de processamento pode variar, como durante a geração de respostas por IA. # Excluir Webhook Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/webhooks/delete-webhook DELETE /webhooks/{webhook_id} # Listar Webhooks Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/webhooks/list-webhooks GET /webhooks Este endpoint permite listar todos os **webhooks** cadastrados na conta do usuário. O retorno inclui informações detalhadas sobre cada webhook registrado, como **status**, **nome**, **URL de produção** e **data de criação**. # Atualizar Webhook Source: https://developer.zapsterapi.com/pt-BR/v1/api-reference/webhooks/update-webhook PATCH /webhooks/{webhook_id} Utilize este endpoint quando precisar atualizar dados do webhook. **Atenção**: Toda e qualquer modificação utilizando este endpoint afetará todas as instâncias conectadas a este webhook. # Quickstart para Agentes Source: https://developer.zapsterapi.com/pt-BR/v1/cli/agent-quickstart Cole este prompt no seu agente (Claude Code, Codex, OpenClaw) e ele instala, configura e valida a CLI da Zapster automaticamente. A CLI foi desenhada para ser configurada por agentes. Cole o bloco abaixo no Claude Code, Codex, OpenClaw ou qualquer agente que execute comandos shell, e ele cuida do resto: ````markdown theme={null} Você vai configurar a CLI da Zapster (`@zapsterapi/cli`). Siga rigorosamente, e pare imediatamente se qualquer passo falhar. 1. Verifique se o Node.js 22+ está instalado: `node --version`. Se < 22, peça ao usuário para atualizar e pare. 2. Instale a CLI globalmente: `npm i -g @zapsterapi/cli`. Se falhar com EACCES, sugira `sudo npm i -g @zapsterapi/cli` ou um Node gerenciado pelo nvm. 3. Confirme com `zapsterapi --version`. 4. Pergunte ao usuário pelo token da Zapster API (ele pode gerar em https://app.zapsterapi.com/tokens). NÃO assuma nenhum valor. 5. Autentique: `zapsterapi auth login --token `. 6. Valide com `zapsterapi auth whoami`. Se falhar, peça um token novo e repita. 7. Liste as instâncias: `zapsterapi instance list --json`. Se a lista vier vazia, oriente o usuário a criar uma instância no dashboard antes de continuar. 8. Confirme: "CLI configurada. Pronto para enviar mensagens." Após configurada, use a CLI assim: ```bash zapsterapi message send --recipient 5511999999999 --text "olá" zapsterapi recipient fetch --recipient 5511999999999 --json zapsterapi instance list --json ``` Se o usuário tiver múltiplas contas, use `--profile ` em todos os comandos. ```` ## Por que isso funciona bem com agentes * **Saída JSON em todos os comandos** (`--json`). Agentes preferem JSON estruturado a parse de texto. * **Códigos de erro estáveis** no envelope JSON (`error.code`). Permite o agente tomar decisões sem heurística textual. * **Multi-profile via `~/.zapsterapi/credentials`**. O agente não precisa carregar tokens em variáveis ou flags. * **Telemetria privacy-safe** que ajuda a melhorar a CLI sem expor dados do usuário. ## Atalhos copy-paste para agentes * **`llms.txt`** ([download](/llms.txt)): índice de toda a documentação no formato [llmstxt.org](https://llmstxt.org/), pronto pra colar como contexto inicial num agente. * **`SKILL.md`** ([copy-paste na doc](/pt-BR/v1/cli/skill)): skill no formato Claude Code que você cola em `~/.claude/skills/zapsterapi-cli/SKILL.md` pra que o agente saiba usar a CLI sem prompts adicionais. ## Próximos passos * [Referência de comandos](/pt-BR/v1/cli/reference/auth) * [Saída JSON em detalhe](/pt-BR/v1/cli/json-output) * [Múltiplos perfis](/pt-BR/v1/cli/profiles) # Exemplos Source: https://developer.zapsterapi.com/pt-BR/v1/cli/examples Receitas práticas para usar a CLI da Zapster em scripts e automações. ## Disparar uma mensagem em CI ```bash theme={null} #!/usr/bin/env bash set -euo pipefail zapsterapi auth login --token "$ZAPSTERAPI_TOKEN" >/dev/null zapsterapi message send \ --recipient "$RECIPIENT" \ --text "Build $GITHUB_RUN_ID concluído com sucesso." \ --json ``` ## Verificar disponibilidade antes de enviar ```bash theme={null} exists=$(zapsterapi recipient fetch --recipient "$NUMERO" --json | jq '.data.exists') if [ "$exists" = "true" ]; then zapsterapi message send --recipient "$NUMERO" --text "Olá!" else echo "Número não está no WhatsApp" fi ``` ## Listar instâncias e iterar ```bash theme={null} zapsterapi instance list --json | jq -r '.data.rows[] | select(.status=="connected") | .id' | while read -r id; do echo "Instância conectada: $id" done ``` ## Alternar entre contas (cliente → time interno) ```bash theme={null} zapsterapi auth login --token "$TOKEN_CLIENTE" zapsterapi auth login --token "$TOKEN_INTERNO" --profile interno zapsterapi message send --recipient 5511 --text "Para o cliente" zapsterapi --profile interno message send --recipient 5511 --text "Para o time" ``` ## Pedir pra um agente fazer Veja o [Quickstart para Agentes](/pt-BR/v1/cli/agent-quickstart) — copie o prompt, cole no Claude Code, e o agente faz tudo. # Instalação Source: https://developer.zapsterapi.com/pt-BR/v1/cli/installation Como instalar e atualizar a CLI da Zapster. ## Pré-requisitos * **Node.js 22 LTS+** (Node 18 e 20 já passaram do EOL ou estão prestes). * **npm 10+** (vem com Node 22). ## Instalação global ```bash theme={null} npm i -g @zapsterapi/cli ``` Confirme: ```bash theme={null} zapsterapi --version ``` ## Atualização ```bash theme={null} npm update -g @zapsterapi/cli ``` ## Desinstalação ```bash theme={null} npm uninstall -g @zapsterapi/cli rm -rf ~/.zapsterapi ``` ## Outras formas de instalação Em v1, apenas o npm registry é suportado. **Homebrew, binário standalone e Docker** estão no roadmap pós-v1. ## Solução de problemas ### `EACCES: permission denied` Seu npm global está em diretório que precisa de root. Use [nvm](https://github.com/nvm-sh/nvm) para gerenciar Node sem sudo, ou: ```bash theme={null} sudo npm i -g @zapsterapi/cli ``` ### `command not found: zapsterapi` O diretório do npm global não está no `$PATH`. Verifique com: ```bash theme={null} npm config get prefix ``` Adicione `/bin` ao seu `$PATH`. # CLI Zapster Source: https://developer.zapsterapi.com/pt-BR/v1/cli/introduction Envie mensagens, gerencie instâncias e automatize fluxos da Zapster direto do shell. A CLI Zapster (`zapsterapi`) é um wrapper fino sobre nossa REST API. Ela existe para dois cenários onde a UI do dashboard atrapalha: * **Agentes de IA** que executam comandos shell (Claude Code, Codex, OpenClaw): copia o prompt, o agente instala e configura, e você manda mensagem em segundos. * **Pipelines de CI** que precisam disparar mensagens, listar instâncias ou validar tokens sem abrir um browser. A CLI **não substitui** a API nem o MCP server — ela é um atalho otimizado para shell e agentes. Cole um prompt no Claude Code / Codex e o agente instala e configura tudo. `npm i -g @zapsterapi/cli`. Requer Node.js 22+. Estilo aws-cli — `--profile` e `ZAPSTERAPI_PROFILE` para alternar contas. Toda saída em JSON estruturado com `--json`. Pronto para `jq` e agentes. ## Atalhos para agentes de IA Índice da documentação no formato [llmstxt.org](https://llmstxt.org/). Cole como contexto inicial num agente — ele já fica orientado sobre toda a CLI sem precisar navegar página por página. Skill no formato Claude Code, com botão de copy-paste pronto. Cole em `~/.claude/skills/zapsterapi-cli/SKILL.md` e o agente passa a usar a CLI sem prompts adicionais. ## O que está em v1 | Comando | O que faz | | ---------------------------- | -------------------------------------------------- | | `zapsterapi auth login` | Salva um token em `~/.zapsterapi/credentials`. | | `zapsterapi auth logout` | Remove credenciais do perfil. | | `zapsterapi auth whoami` | Verifica o token e mostra o perfil ativo. | | `zapsterapi instance list` | Lista as instâncias da conta. | | `zapsterapi message send` | Envia uma mensagem de texto. | | `zapsterapi recipient fetch` | Verifica se um número está disponível no WhatsApp. | Ficou de fora propositalmente: webhook tunneling estilo Stripe `listen`, mock server local, device-flow auth e plugins. Esses estão no roadmap pós-v1. ## Privacidade e telemetria A CLI envia eventos privacy-safe ao PostHog (nome do comando, sucesso/falha, código de erro, versão do Node, OS). **Nenhum token, número ou conteúdo de mensagem é coletado**. Para opt-out: ```bash theme={null} export ZAPSTERAPI_DISABLE_TELEMETRY=1 ``` # Saída JSON Source: https://developer.zapsterapi.com/pt-BR/v1/cli/json-output Como ler a saída estruturada da CLI da Zapster a partir de scripts e agentes. Toda invocação da CLI aceita a flag global `--json`. Com ela, a saída é um único objeto JSON por linha, no envelope: ```json theme={null} { "ok": true, "data": } ``` ou, em erro: ```json theme={null} { "ok": false, "error": { "code": "invalid_token", "message": "Token is invalid", "hint": "Run `zapsterapi auth login --token ` first.", "details": { "status": 401 } } } ``` A saída de sucesso vai para `stdout`. A saída de erro vai para `stderr` e o exit code é `1` (ou `2` para erros de uso, como flag desconhecida). ## Exemplos ### Listar instâncias e pegar o primeiro ID ```bash theme={null} zapsterapi instance list --json | jq -r '.data.rows[0].id' ``` ### Validar token em script ```bash theme={null} if zapsterapi auth whoami --json > /dev/null 2>&1; then echo "ok" else echo "token inválido" fi ``` ### Reagir ao código de erro ```bash theme={null} output=$(zapsterapi message send --recipient 5511999999999 --text "oi" --json 2>&1) code=$(echo "$output" | jq -r '.error.code // empty') case "$code" in invalid_token) echo "renove o token";; invalid_recipient) echo "número errado";; "") echo "enviado";; *) echo "erro: $code";; esac ``` ## Garantias de estabilidade * O envelope (`ok`, `data`, `error`) é estável dentro da major version `0.x` da CLI. * Os campos dentro de `data` espelham a resposta da REST API correspondente — se a API adicionar um campo, ele aparece no JSON da CLI também. * Os `error.code` são estáveis. Mensagens podem mudar entre versões. # Múltiplos perfis Source: https://developer.zapsterapi.com/pt-BR/v1/cli/profiles Gerencie várias contas Zapster com perfis no estilo aws-cli. A CLI suporta múltiplos perfis para alternar entre contas Zapster (cliente, work, staging) sem reautenticar a cada comando. ## Como funciona As credenciais ficam em `~/.zapsterapi/credentials` (formato INI), uma seção por perfil: ```ini theme={null} [default] token=tok_principal [work] token=tok_da_empresa base_url=https://api.zapsterapi.com ``` ## Criando perfis ```bash theme={null} # Perfil padrão (default) zapsterapi auth login --token tok_principal # Um segundo perfil zapsterapi auth login --token tok_da_empresa --profile work ``` ## Usando um perfil A CLI resolve o perfil ativo nesta ordem: 1. Flag `--profile ` na invocação. 2. Variável de ambiente `ZAPSTERAPI_PROFILE`. 3. Perfil `default`. ```bash theme={null} # Forma 1: flag por comando zapsterapi --profile work instance list # Forma 2: ambiente para a sessão export ZAPSTERAPI_PROFILE=work zapsterapi instance list ``` ## Removendo um perfil ```bash theme={null} zapsterapi auth logout --profile work ``` Se for o único perfil, o arquivo `~/.zapsterapi/credentials` é removido. ## Permissões do arquivo A CLI cria `~/.zapsterapi/` com `0700` e o arquivo de credenciais com `0600` (somente leitura/escrita para o seu usuário). Em Windows essa proteção depende da ACL do filesystem. # auth Source: https://developer.zapsterapi.com/pt-BR/v1/cli/reference/auth Autenticação e gerenciamento de credenciais. ## `zapsterapi auth login` Salva um token Zapster em `~/.zapsterapi/credentials` para o perfil ativo. ```bash theme={null} zapsterapi auth login --token [--profile ] [--base-url ] ``` | Flag | Obrigatório | Descrição | | ------------------ | ----------- | ---------------------------------------------------------------------------------------------- | | `--token ` | Sim | Token de API gerado em [https://app.zapsterapi.com/tokens](https://app.zapsterapi.com/tokens). | | `--profile ` | Não | Nome do perfil. Default: `default`. | | `--base-url ` | Não | API base URL alternativa (raramente necessária). | **Validação**: a CLI faz `GET /v1/wa/instances?per_page=1` antes de gravar o token. Se a API responder 401, o token não é persistido. **Exemplo**: ```bash theme={null} zapsterapi auth login --token zap_live_xxxxx --profile work ``` ## `zapsterapi auth logout` Remove credenciais do perfil ativo. ```bash theme={null} zapsterapi auth logout [--profile ] ``` Se for o único perfil no arquivo, o arquivo é apagado. ## `zapsterapi auth whoami` Verifica o token do perfil ativo e mostra resumo. ```bash theme={null} zapsterapi auth whoami [--profile ] [--json] ``` Saída JSON: ```json theme={null} { "ok": true, "data": { "profile": "default", "base_url": "https://api.zapsterapi.com", "token_suffix": "abcd", "valid": true } } ``` # instance Source: https://developer.zapsterapi.com/pt-BR/v1/cli/reference/instance Listagem e inspeção de instâncias WhatsApp. ## `zapsterapi instance list` Lista as instâncias WhatsApp disponíveis para o perfil ativo. ```bash theme={null} zapsterapi instance list \ [--status ] \ [--lookup-key ] \ [--page ] \ [--per-page ] \ [-q, --query ] \ [--profile ] \ [--json] ``` | Flag | Obrigatório | Descrição | | ---------------------- | ----------- | ---------------------------------------------------------------------- | | `--status ` | Não | Filtra por status: `connected`, `offline` ou `disconnected`. | | `--lookup-key ` | Não | Filtra por `lookup_key` (chave externa que você associou à instância). | | `--page ` | Não | Página (default: 1). | | `--per-page ` | Não | Itens por página (default: 15, máx: 100). | | `-q, --query ` | Não | Busca textual no nome da instância. | Saída texto (default): ``` ID NAME STATUS CONNECTION i_01HABC... Atendimento connected unofficial i_01HXYZ... Marketing offline unofficial ``` Saída JSON: ```json theme={null} { "ok": true, "data": { "instances": [ { "id": "i_01HABC...", "name": "Atendimento", "status": "connected", "connection_type": "unofficial" } ], "total": 1 } } ``` ### Exemplos ```bash theme={null} # Apenas instâncias conectadas zapsterapi instance list --status connected # Procurar por nome zapsterapi instance list -q "atendimento" # Paginação zapsterapi instance list --page 2 --per-page 50 ``` A v1 da CLI cobre apenas a listagem. Para criar, atualizar ou desligar instâncias, use a [REST API](/pt-BR/v1/api-reference/instance/list-instances). # message Source: https://developer.zapsterapi.com/pt-BR/v1/cli/reference/message Envio, agendamento, cancelamento e listagem de mensagens WhatsApp via CLI. ## `zapsterapi message send` Envia uma mensagem para um destinatário no WhatsApp. Suporta texto, mídia, templates WABA, botões interativos, mentions, agendamento, reply, view-once e mais. ```bash theme={null} zapsterapi message send \ --recipient \ [--text ] \ [--instance-id ] \ [--send-at ] \ [--media-url | --media-base64 ] \ [--media-caption ] [--media-filename ] \ [--ptt | --ptv | --sticker] \ [--template-name --template-language [--template-components ]] \ [--button '' ...] [--buttons-mode ] \ [--mention ... | --mention-everyone] \ [--reply-to ] \ [--view-once] [--no-link-preview] [--auto-mention] \ [--profile ] [--json] ``` ### Flags principais | Flag | Obrigatório | Descrição | | ---------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `--recipient ` | Sim | Telefone E.164 sem `+` (ex: `5511999999999`) ou ID de grupo (`group:`). | | `--text ` | Condicional | Conteúdo da mensagem. Pelo menos um de `--text`, `--media-*` ou `--template-*` é necessário. | | `--instance-id ` | Não | Instância específica de envio. Se omitido e você só tiver **uma instância conectada**, a CLI usa ela; com mais de uma, a flag é obrigatória. | | `--send-at ` | Não | Agenda a mensagem (ISO 8601, ex: `2026-05-15T10:00:00Z`). Mínimo 1 minuto no futuro. | ### Mídia (`--media-*`) Use `--media-url` sempre que possível — `--media-base64` carrega o arquivo no payload e fica reservado pra quando a URL não estiver acessível. | Flag | Descrição | | ------------------------- | ----------------------------------------------------- | | `--media-url ` | URL pública/assinada do arquivo. **Preferida.** | | `--media-base64 ` | Conteúdo em base64. Use só quando URL não for viável. | | `--media-caption ` | Legenda (imagem/vídeo) ou texto sob o documento. | | `--media-filename ` | Nome de arquivo (ex: `relatorio.pdf`). | | `--ptt` | Marca o áudio como push-to-talk (mensagem de voz). | | `--ptv` | Marca o vídeo como push-to-view (efêmero). | | `--sticker` | Envia a mídia como figurinha. | Exatamente um de `--media-url` ou `--media-base64` é necessário quando enviar mídia. ### Templates WABA (`--template-*`) Templates são exclusivos de instâncias WABA (Meta oficial). **Mutuamente exclusivos com `--text` e `--media-*`.** | Flag | Descrição | | ------------------------------ | -------------------------------------------------------------------- | | `--template-name ` | Nome do template aprovado na Meta. | | `--template-language ` | Código de idioma (ex: `en_US`, `pt_BR`). | | `--template-components ` | Array JSON de components com `header`/`body`/`buttons` e parâmetros. | ### Botões interativos (`--button`, `--buttons-mode`) Repita `--button` até **3 vezes**. Cada valor é JSON com `type` + campos do tipo. Veja o [guia de botões](/pt-BR/v1/guides/messages-with-buttons) pra detalhes de cada tipo. ```bash theme={null} --button '{"type":"reply","label":"Sim"}' \ --button '{"type":"call","label":"Ligar","phone_number":"+5511999999999"}' \ --button '{"type":"url","label":"Abrir","url":"https://exemplo.com"}' ``` | Tipo | Campos obrigatórios | | ---------- | --------------------------------------- | | `reply` | `label` | | `call` | `label`, `phone_number` (E.164 com `+`) | | `url` | `label`, `url` | | `copyable` | `label`, `copy_code` | `--buttons-mode ` é opcional (default `auto`). Use `interactive` pra forçar visual rico mesmo só com `reply`. ### Mentions (`--mention`, `--mention-everyone`) | Flag | Descrição | | -------------------- | -------------------------------------------- | | `--mention ` | Telefone/JID a ser mencionado. Pode repetir. | | `--mention-everyone` | Menciona @todos no grupo. | `--mention` e `--mention-everyone` são **mutuamente exclusivos**. ### Outros | Flag | Descrição | | ------------------------- | ----------------------------------------------------- | | `--reply-to ` | Responde a uma mensagem existente (limite de 7 dias). | | `--view-once` | Marca como mensagem efêmera (some após visualizar). | | `--no-link-preview` | Desativa o preview de URLs (default: ativo). | | `--auto-mention` | Habilita auto-mention. | ### Saída — envio imediato (não-oficial / Baileys) ```json theme={null} { "ok": true, "data": { "kind": "immediate", "message_id": "3EB0C68ECE69F2DF660423", "message_trace_id": "x7jCGxw83jXOjaXR60qJQ24Zkrs4MacA" } } ``` ### Saída — envio imediato (oficial / WABA) ```json theme={null} { "ok": true, "data": { "kind": "immediate", "message_id": "wamid.HBgMNTU4NzgxMTcwMjYxFQIAERgSNDQyQzhBOTY1OEI5Mzk2MzI3AA==" } } ``` ### Saída — agendamento (`--send-at` definido) ```json theme={null} { "ok": true, "data": { "kind": "scheduled", "message_id": "msg_4rajud08wyl9pmi6bhnwr", "send_at": "2026-05-15T10:00:00.000Z", "status": "scheduled" } } ``` ### Exemplos **Texto simples:** ```bash theme={null} zapsterapi message send --recipient 5511999999999 --text "olá" ``` **Texto + mídia + caption:** ```bash theme={null} zapsterapi message send --recipient 5511999999999 \ --text "veja o relatório anexo" \ --media-url https://exemplo.com/relatorio.pdf \ --media-filename relatorio.pdf ``` **Botões interativos:** ```bash theme={null} zapsterapi message send --recipient 5511999999999 \ --text "Como podemos te ajudar?" \ --button '{"type":"reply","label":"Suporte"}' \ --button '{"type":"reply","label":"Vendas"}' \ --buttons-mode interactive ``` **Template WABA:** ```bash theme={null} zapsterapi message send --recipient 5511999999999 \ --template-name welcome --template-language pt_BR ``` **Mensagem em grupo com mention everyone:** ```bash theme={null} zapsterapi message send --recipient group:120363021234567890 \ --text "Reunião em 10min @everyone" --mention-everyone ``` **Reply + view-once:** ```bash theme={null} zapsterapi message send --recipient 5511999999999 \ --text "Aqui está a senha temporária" \ --reply-to msg_4rajud08wyl9pmi6bhnwr \ --view-once ``` **Agendamento:** ```bash theme={null} zapsterapi message send --recipient 5511999999999 \ --text "Bom dia!" --send-at 2026-05-16T08:00:00-03:00 ``` *** ## `zapsterapi message cancel ` Cancela uma mensagem **agendada** (`scheduled`) ou **pendente** (`pending`). Mensagens já enviadas não podem ser canceladas. ```bash theme={null} zapsterapi message cancel msg_4rajud08wyl9pmi6bhnwr [--profile ] [--json] ``` ### Saída ```json theme={null} { "ok": true, "data": { "id": "msg_4rajud08wyl9pmi6bhnwr", "status": "canceled" } } ``` ### Códigos de erro específicos | `error.code` | Causa | | ----------------------- | ------------------------------------------------- | | `message_not_found` | ID não existe ou não pertence à conta. | | `message_cannot_cancel` | Mensagem já saiu do estado `scheduled`/`pending`. | *** ## `zapsterapi message list` Lista mensagens dentro de uma janela de tempo. Útil pra auditoria, dashboards e agentes que precisam reconciliar status. ```bash theme={null} zapsterapi message list \ --from \ --to \ [--status ] \ [--instance-id ] \ [--recipient ] \ [--message-id ] \ [--connection-type ] \ [--limit ] \ [--after ] \ [--profile ] \ [--json] ``` | Flag | Obrigatório | Descrição | | -------------------------- | ----------- | ------------------------------------------------------------ | | `--from ` | Sim | Início da janela. ISO 8601. | | `--to ` | Sim | Fim da janela. ISO 8601. **Janela máxima de 90 dias.** | | `--status ` | Não | Filtra por status. | | `--instance-id ` | Não | Filtra por instância. | | `--recipient ` | Não | Filtra por destinatário. | | `--message-id ` | Não | Filtra por ID de mensagem (wamid ou `msg_...`). | | `--connection-type ` | Não | `waba` ou `unofficial`. | | `--limit ` | Não | 1–100, default 20. | | `--after ` | Não | Cursor de paginação (`meta.next_cursor` da página anterior). | ### Saída ```json theme={null} { "ok": true, "data": { "data": [ { "id": "msg_01HABC...", "message_id": "wamid.HBg...", "recipient": "5511999999999", "status": "sent", "connection_type": "waba", "instance_id": "i_xyz1234abc", "send_at": null, "sent_at": "2026-05-08T10:00:00Z", "canceled_at": null, "errors": null } ], "meta": { "next_cursor": null, "has_more": false, "limit": 20 } } } ``` ### Códigos de erro específicos | `error.code` | Causa | | ------------------ | ----------------------------------------- | | `from_after_to` | `--from` igual ou maior que `--to`. | | `window_too_large` | Janela excede 90 dias. | | `invalid_argument` | `--from` ou `--to` não é ISO 8601 válido. | *** ## Códigos de erro comuns | `error.code` | Causa | | ----------------------- | ------------------------------------------------------------------------------------ | | `missing_recipient` | Flag `--recipient` ausente ou vazia. | | `missing_text` | Nenhum de `--text`, `--media-*` ou `--template-*` foi informado. | | `incompatible_content` | `--template-*` combinado com `--text` ou `--media-*` (são mutuamente exclusivos). | | `invalid_media` | `--media-url` e `--media-base64` ambos presentes, ou nenhum dos dois. | | `invalid_button` | JSON inválido em `--button`, mais de 3 botões, ou campo obrigatório do tipo ausente. | | `invalid_template` | `--template-name` ou `--template-language` ausente. | | `invalid_mentions` | `--mention` e `--mention-everyone` foram passados juntos. | | `invalid_send_at` | `--send-at` não é ISO 8601 válido. | | `invalid_token` | Token inválido ou expirado. | | `invalid_recipient` | Número não é WhatsApp válido. | | `instance_not_found` | `--instance-id` não pertence à conta. | | `no_connected_instance` | `--instance-id` omitido e nenhuma instância está conectada. | | `instance_id_required` | `--instance-id` omitido e há múltiplas instâncias conectadas. | Para a referência completa do payload (todos os campos, validações cross-field, edge cases), veja a [REST API](/pt-BR/v1/api-reference/messages/sending) e o [guia de botões](/pt-BR/v1/guides/messages-with-buttons). # recipient Source: https://developer.zapsterapi.com/pt-BR/v1/cli/reference/recipient Verifica disponibilidade de números no WhatsApp. ## `zapsterapi recipient fetch` Verifica se um número de telefone está disponível no WhatsApp e devolve o ID interno (jid/lid). ```bash theme={null} zapsterapi recipient fetch \ --recipient \ [--instance-id ] \ [--profile ] \ [--json] ``` Se você só tiver uma instância ativa, a CLI auto-resolve o `--instance-id`. Com mais de uma, a flag é obrigatória. Saída JSON: ```json theme={null} { "ok": true, "data": { "exists": true, "id": "5511999999999@s.whatsapp.net", "lid": null, "original": "5511999999999" } } ``` | `error.code` | Causa | | ---------------------- | ------------------------------------------------------------ | | `missing_recipient` | `--recipient` ausente. | | `instance_id_required` | Mais de uma instância e nenhuma `--instance-id` foi passada. | | `invalid_token` | Token inválido. | # SKILL para agentes Source: https://developer.zapsterapi.com/pt-BR/v1/cli/skill Skill copy-paste para Claude Code, Codex e qualquer agente compatível com o formato Claude — depois de instalada, o agente passa a usar a CLI sem prompts adicionais. ## O que é A SKILL é um arquivo no formato [Claude Code Skills](https://docs.claude.com/) que descreve, em prosa otimizada para agentes, **quando** usar a CLI da Zapster, **como** se autenticar, **quais** comandos chamar para cada cenário e **como** interpretar os erros. Quando colada na pasta de skills do agente, ela vira parte do contexto permanente — o agente não precisa mais de prompt explicando como usar a `zapsterapi`. Funciona com Claude Code (CLI ou IDE), Codex CLI e qualquer agente que respeite o formato `~/.claude/skills//SKILL.md`. ## Como instalar ```bash theme={null} mkdir -p ~/.claude/skills/zapsterapi-cli ``` Use o botão "Copy" do bloco mais abaixo e salve em `~/.claude/skills/zapsterapi-cli/SKILL.md`. Skills são carregadas no startup. Da próxima conversa, o agente já reconhece a CLI da Zapster e segue as convenções abaixo automaticamente. Pré-requisito: a CLI precisa estar instalada (`npm i -g @zapsterapi/cli`). A skill assume Node.js 22+ e que o token está em `~/.zapsterapi/credentials` (use `zapsterapi auth login --token ` antes do primeiro uso). ## Conteúdo da skill ```markdown theme={null} --- name: zapsterapi-cli description: Send and schedule WhatsApp messages, cancel and list scheduled messages, check whether a phone number is on WhatsApp, and list instances using the @zapsterapi/cli command-line tool. Use when the user asks to send a WhatsApp message via Zapster, schedule a message for later, cancel a scheduled message, check if a phone number is on WhatsApp, or list Zapster instances from a shell or agent loop. --- # zapsterapi-cli ## Pre-flight Run these once per machine, in order: 1. Verify Node.js >= 22: `node --version`. Abort if older. 2. Check install: `zapsterapi --version`. If the binary is missing, install: `npm install -g @zapsterapi/cli`. 3. Authenticate. Ask the user for their token from https://app.zapsterapi.com/tokens. Do NOT echo the token back into chat or logs. Run: `zapsterapi auth login --token [--profile ]`. Login validates by calling `GET /v1/wa/instances?per_page=1`; a 401 aborts without persisting. 4. Confirm: `zapsterapi auth whoami --json`. Expect `data.valid: true`. ## Output discipline Always pass `--json` when parsing programmatically. Every command returns the same envelope: - Success: `{ "ok": true, "data": }` - Failure: `{ "ok": false, "error": { "code": "", "message": "", "hint": "" } }` Branch on `ok`. Never grep stdout text; the table format is for humans. ## Command catalog ### auth | Command | Use when | |---|---| | `zapsterapi auth login --token [--profile ] [--base-url ]` | Persist credentials for a profile. | | `zapsterapi auth logout [--profile ]` | Drop credentials for the active profile. Deletes the file if it was the last profile. | | `zapsterapi auth whoami [--profile ] [--json]` | Verify the active token and print profile/base-url/token-suffix. | ### message | Command | Use when | |---|---| | `zapsterapi message send --recipient --text [--instance-id ] [--send-at ] [--profile ] [--json]` | Send a text message now, or schedule for later by passing `--send-at`. `--instance-id` is auto-resolved when exactly one instance is connected. | | `zapsterapi message cancel [--profile ] [--json]` | Cancel a `scheduled` or `pending` message by ID. Already-sent messages cannot be canceled. | | `zapsterapi message list --from --to [--status ] [--instance-id ] [--recipient ] [--message-id ] [--connection-type ] [--limit ] [--after ] [--profile ] [--json]` | Audit/reconcile messages in a window. Window <= 90 days. Default `--limit` 20, max 100. Page with `--after meta.next_cursor`. | Send response shapes: - Immediate (Baileys): `data.kind = "immediate"`, `message_id`, `message_trace_id`. - Immediate (WABA): `data.kind = "immediate"`, `message_id` (`wamid....`). - Scheduled: `data.kind = "scheduled"`, `message_id` (`msg_...`), `send_at`, `status: "scheduled"`. v1 covers text only. For media, buttons, or templates use the REST API. ### recipient | Command | Use when | |---|---| | `zapsterapi recipient fetch --recipient [--instance-id ] [--profile ] [--json]` | Check whether a phone number is on WhatsApp before sending. Returns `{ id, lid, is_business, profile_picture }`. Auto-resolves `--instance-id` when exactly one instance is active. A 404 surfaces as `recipient_not_found`. | ### instance | Command | Use when | |---|---| | `zapsterapi instance list [--status ] [--lookup-key ] [--page ] [--per-page ] [-q, --query ] [--profile ] [--json]` | Discover instance IDs, filter by status/lookup_key, or search by name. `--per-page` max 100. Create/update/delete are not in v1 — use the REST API. | ## Error code map | `error.code` | Meaning | Agent action | |---|---|---| | `unauthorized` / `invalid_token` | Missing or expired token. | Re-run `auth login`; do not retry blindly. | | `no_connected_instance` | `--instance-id` omitted and zero instances connected. | Run `instance list --status connected --json`; surface to user if empty. | | `instance_id_required` | `--instance-id` omitted and >1 connected instance. | Pick one via `instance list` and pass `--instance-id` explicitly. | | `instance_not_found` | ID does not belong to the account. | Re-list and confirm. | | `missing_recipient` | `--recipient` empty/absent. | Stop and ask user. | | `missing_text` | `--text` empty/absent. | Stop and ask user. | | `invalid_recipient` / `recipient_not_found` | Number is not on WhatsApp. | Verify via `recipient fetch` first. | | `invalid_send_at` | `--send-at` is not valid ISO 8601. | Reformat (e.g. `2026-05-15T10:00:00Z`). | | `invalid_button` | Button payload rejected. | Drop to REST API; CLI v1 is text only. | | `invalid_mentions` | Mentions payload rejected. | Drop to REST API. | | `from_after_to` | `--from >= --to` on `message list`. | Swap or fix the bounds. | | `window_too_large` | `message list` window > 90 days. | Page over multiple <=90d windows. | | `message_not_found` | Cancel target does not exist on this account. | Verify ID; check profile. | | `message_cannot_cancel` | Message already left `scheduled`/`pending`. | Surface to user; do not retry. | | `network_error` | Transport-level failure. | Retry with backoff (max 3); then surface. | ## Multi-profile Pass `--profile ` on any command, or set `ZAPSTERAPI_PROFILE=`. Default is `default`. Credentials live in `~/.zapsterapi/credentials`. ## Privacy Never echo tokens, recipient phone numbers, or message bodies back into the chat or log files. When reporting results, redact (e.g. `5511******999`). Disable telemetry with `ZAPSTERAPI_DISABLE_TELEMETRY=1`. ``` ## Próximos passos * [Quickstart para agentes](/pt-BR/v1/cli/agent-quickstart) — prompt copy-paste pra primeira instalação automatizada. * [Referência de comandos](/pt-BR/v1/cli/reference/auth) — detalhes de flags por comando. * [Saída JSON](/pt-BR/v1/cli/json-output) — formato do envelope `{ ok, data, error }`. # Instâncias Source: https://developer.zapsterapi.com/pt-BR/v1/concepts/instances O que é, o que faz e como funcionam ### O que é uma Instância? Uma **Instância** é um conceito fundamental dentro das APIs para WhatsApp, representando uma unidade individual de conexão ao serviço do WhatsApp. Essencialmente, uma instância é como um "cliente" que se comunica com a API do WhatsApp, permitindo que você envie e receba mensagens, gerencie contatos, e realize outras operações relacionadas. ### O que uma Instância faz? A Instância é responsável por gerenciar a comunicação entre a sua aplicação e o WhatsApp. Algumas das principais funcionalidades de uma instância incluem: * **Envio e Recebimento de Mensagens**: Uma instância pode enviar e receber mensagens de texto, imagens, áudios, e outros tipos de mídia através da API. * **Webhooks**: A instância também pode ser configurada para disparar webhooks, notificando a sua aplicação sobre eventos como o recebimento de novas mensagens ou mudanças de status. ### Como as Instâncias Funcionam? Cada instância é autenticada com um número de telefone único e, uma vez conectada, ela mantém uma sessão ativa com os servidores do WhatsApp. Esta sessão é essencial para garantir que a instância possa enviar e receber mensagens em tempo real. 1. **Autenticação**: Para iniciar, a instância precisa ser autenticada com o WhatsApp. Isso geralmente envolve o escaneamento de um código QR ou o uso de credenciais específicas. 2. **Manutenção da Conexão**: Após a autenticação, a instância estabelece uma conexão persistente com o WhatsApp. Essa conexão deve ser mantida ativa para que a instância continue a funcionar corretamente. 3. **Interação com a API**: Uma vez conectada, a instância pode interagir com a API do WhatsApp para executar diversas operações, como o envio de mensagens, verificação do status dos contatos, e muito mais. 4. **Webhooks e Eventos**: A instância pode ser configurada para enviar notificações para a sua aplicação via webhooks quando certos eventos ocorrem, como a chegada de uma nova mensagem ou a mudança no status de um contato. ### Conclusão Instâncias são essenciais para qualquer aplicação que precisa interagir com o WhatsApp através de uma API. Elas não só facilitam a comunicação bidirecional em tempo real, mas também oferecem ferramentas para gerenciar eventos de forma eficaz. Compreender como configurar e manter uma instância é crucial para o sucesso na integração com o WhatsApp. ## Tipos de conexão Na Zapster, uma instância pode se conectar ao WhatsApp de duas formas: via QR code (não oficial) ou via API oficial da Meta (WABA). Ambas usam os mesmos endpoints para enviar e receber mensagens. ### Não oficial (QR code) É o modo padrão. Você cria a instância, escaneia o QR code (ou usa o código de pareamento) e o número fica conectado. Por trás, a Zapster mantém uma conexão persistente com os servidores do WhatsApp. Funciona bem para a maioria dos casos: automações, chatbots, envio de notificações. O risco é que, por não ser a API oficial, envios em volume muito alto ou práticas inadequadas podem gerar restrições no número. ### Oficial (WABA) Usa a Cloud API da Meta, o canal oficial do WhatsApp para empresas. A conexão é feita via OAuth (Embedded Signup) ou token manual. Não precisa de QR code nem de dispositivo conectado. As vantagens: estabilidade garantida pela Meta, sem risco de banimento por uso da API, suporte a templates de mensagem, e status de entrega/leitura confiáveis. As limitações: a Meta cobra por conversa (o preço varia por categoria e país), não suporta envio para grupos, e o setup inicial é mais envolvido. ### Comparativo | Característica | Não oficial (QR code) | Oficial (WABA) | | ------------------ | ----------------------------------- | -------------------------------------------- | | Conexão | QR code ou código de pareamento | OAuth com Meta ou token manual | | Estabilidade | Boa | Alta (garantida pela Meta) | | Risco de banimento | Existe, se usar de forma inadequada | Baixo (uso aprovado pela Meta) | | Templates | Não suporta | Suporta (marketing, utility, authentication) | | Custo por mensagem | Sem custo adicional | Meta cobra por conversa | | Grupos | Suporta | Não suporta | | Setup | Escanear QR code | Conectar conta Meta ou inserir token | ### Sobre migração Atualmente não é possível converter uma instância não oficial para WABA (ou vice-versa). Esse recurso está em desenvolvimento. Quando disponível, a migração será transparente: seus webhooks e integrações continuarão funcionando sem alterações. Para saber qual tipo escolher e como conectar uma instância WABA, veja o [guia de conexão WABA](/pt-BR/v1/guides/connect-waba-instance) e o [comparativo detalhado](/pt-BR/v1/guides/waba-vs-unofficial). # Ciclo de Vida das Mensagens Source: https://developer.zapsterapi.com/pt-BR/v1/concepts/message-lifecycle Entenda os status e timestamps que acompanham cada mensagem ## Status de uma mensagem Cada mensagem enviada pela Zapster passa por um ciclo de vida com status bem definidos. A tabela abaixo mostra todos os status possíveis: | Status | Descrição | Quando acontece | | ----------- | --------------------------------------- | -------------------------------------- | | `pending` | Mensagem imediata sendo processada | Ao enviar sem `send_at` | | `scheduled` | Agendada, aguardando o horário de envio | Ao enviar com `send_at` | | `sent` | Enviada com sucesso ao WhatsApp | Envio confirmado pela instância | | `failed` | Falha no envio | Após 3 tentativas sem sucesso | | `canceled` | Cancelada pelo usuário | Ao chamar `DELETE /v1/wa/messages/:id` | Quando uma mensagem chega em `sent`, `failed` ou `canceled`, o status é final. Não muda mais. ## Timestamps Além do status, cada mensagem carrega timestamps que indicam o que aconteceu e quando. Nem todos são preenchidos em todas as mensagens — depende do tipo de envio e do que aconteceu depois. | Campo | Preenchido quando | Observação | | -------------- | ------------------------------ | -------------------------------------------------- | | `created_at` | A mensagem é criada | Sempre preenchido | | `send_at` | Definido pelo usuário | Só existe em mensagens agendadas | | `sent_at` | Mensagem enviada com sucesso | Preenchido quando o WhatsApp aceita o envio | | `delivered_at` | Entrega confirmada no aparelho | Quando a mensagem chega no celular do destinatário | | `read_at` | Destinatário leu a mensagem | Quando o destinatário abre a conversa | | `canceled_at` | Mensagem cancelada | Quando o usuário chama `DELETE` antes do envio | ## Sobre entrega e leitura Os campos `delivered_at` e `read_at` dependem de fatores que estão fora do controle da Zapster: * Se o destinatário desativou a confirmação de leitura nas configurações do WhatsApp, `read_at` nunca vai ser preenchido * Se o celular do destinatário está sem internet, `delivered_at` pode demorar até ele ficar online de novo * Esses campos não alteram o status da mensagem. Uma vez que o status é `sent`, ele permanece `sent`. Entrega e leitura são informações extras, registradas apenas nos timestamps Na prática, você pode usar `delivered_at` e `read_at` para montar relatórios de entrega e leitura, mas não conte com eles para fluxos críticos. Nem todo destinatário vai gerar esses eventos. ## Quando uma mensagem falha Se o status de uma mensagem é `failed`, o campo `errors` traz uma lista com os detalhes do que deu errado. A estrutura do erro varia um pouco dependendo do tipo de instância. Para **instâncias oficiais (WABA)**, o `errors` inclui o código de erro da Meta e um link direto para a documentação deles: ```json theme={null} { "errors": [ { "code": 131047, "title": "Re-engagement message", "details": "Mais de 24 horas se passaram desde a última resposta do destinatário.", "href": "https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/" } ] } ``` Alguns erros comuns em instâncias WABA: * **131047**: Janela de 24 horas expirada. O destinatário não respondeu nas últimas 24h e você tentou enviar uma mensagem que não é template. * **132000**: Template não encontrado. Verifique se o nome e o idioma do template estão corretos. * **131026**: Número não está no WhatsApp ou é inválido. Para **instâncias não oficiais (QR code)**, os erros mais comuns são: * Instância offline (o celular perdeu conexão ou a sessão expirou) * Número bloqueado ou inexistente no WhatsApp * Limite de envio atingido pelo WhatsApp Em todos os casos, o campo `errors` traz informações suficientes para você entender o que aconteceu e decidir se vale tentar novamente. # Cobrança de mensagens no WhatsApp oficial (WABA) Source: https://developer.zapsterapi.com/pt-BR/v1/concepts/message-pricing Como funciona a cobrança da Meta no canal oficial do WhatsApp (WABA), o que muda em 2026 nas mensagens de serviço e onde entram os templates de marketing, utilidade e autenticação O canal oficial do WhatsApp (WABA) é pago, e quem cobra pelas mensagens é a **Meta**, não a Zapster. São duas coisas separadas: a assinatura da sua instância na Zapster, de um lado, e o que a Meta cobra pelo envio das mensagens, do outro. A cobrança da Meta é feita através da conta de WhatsApp Business ligada ao seu número. Esta cobrança vale **apenas para o canal oficial (WABA)**. Instâncias não oficiais (QR code) não passam por essa cobrança da Meta. Se você ainda está decidindo entre os dois tipos, veja o comparativo em [WABA vs não oficial](/pt-BR/v1/guides/waba-vs-unofficial). ## Os dois grupos de mensagens No canal oficial, o que a Meta cobra depende do tipo de mensagem que você envia: * **Mensagens livres (de serviço)**: texto, mídia ou botões enviados em resposta a um cliente, dentro da janela de conversa de 24 horas. Pela definição da Meta, é qualquer mensagem que não seja um template. * **Templates**: mensagens aprovadas pela Meta, usadas para iniciar conversa ou enviar fora da janela de 24 horas. Cada categoria de template (marketing, utilidade e autenticação) tem cobrança própria, que varia conforme o país do destinatário. Consulte a [tabela de preços da Meta](https://developers.facebook.com/docs/whatsapp/pricing) para os valores por categoria. O restante desta página foca nas mensagens livres (de serviço), que são as que mais mudam em 2026. ## O que é uma "mensagem de serviço" É toda mensagem livre (texto, mídia ou botões) que você envia em resposta a um cliente, dentro da janela de 24 horas. Pela definição da Meta, é qualquer mensagem que não seja um template. Até hoje elas são **gratuitas**, assim desde novembro de 2024. ## O que muda em 2026 e quando | Data | O que passa a ser cobrado | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **1 de agosto de 2026** | Mensagens do **Meta Business Agent** (respostas geradas pela inteligência artificial da Meta) passam a ser cobradas por token: US\$ 2,00 a cada 1 milhão de tokens, o que dá por volta de 4 a 5 centavos de dólar por mensagem. Isso só vale se você usar o agente de IA da Meta. | | **1 de outubro de 2026** | As **mensagens de serviço** (as mensagens livres) passam a ser cobradas por mensagem enviada. Elas eram gratuitas desde novembro de 2024. No mesmo dia, mensagens de utilidade enviadas dentro da janela de 24 horas, gratuitas desde 1 de julho de 2025, também passam a ser cobradas. | ## Quanto vai custar A Meta ainda não publicou os valores. Segundo a documentação, os preços que entram em vigor em 1 de outubro de 2026 serão anunciados **até 1 de setembro de 2026**. O preço de uma mensagem de serviço vai acompanhar o preço já usado para mensagens de utilidade e autenticação, que **varia conforme o país** do cliente. A Meta informou que não haverá desconto por volume para mensagens de serviço. ## O que continua igual * A regra da janela de 24 horas não muda: mensagens livres só saem com a janela aberta. O que muda é que elas deixam de ser gratuitas. * Os templates já eram pagos e seguem com a cobrança própria deles. * Essa cobrança é só do canal **oficial** (WABA). Instâncias não oficiais (QR code) não passam por essa cobrança da Meta. ## Em resumo Hoje, responder um cliente dentro da janela de 24 horas não custa nada. A partir de **1 de outubro de 2026**, cada uma dessas respostas passa a ter um custo cobrado pela Meta. Se você envia muitas mensagens de atendimento, vale acompanhar o anúncio de preços de setembro de 2026 e já incluir esse custo no seu planejamento. Fonte: [WhatsApp Business Platform: Non-Template Messages Pricing](https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing/non-template-messages), documentação oficial da Meta. As datas e os valores são definidos pela Meta e podem mudar. # Rate limit Source: https://developer.zapsterapi.com/pt-BR/v1/concepts/rate-limit Limite de requisições da API da Zapster (3 req/s por token), headers RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset, resposta 429 rate_limited e como tratar com backoff e fila. O **rate limit** (limite de requisições) controla quantas chamadas você pode fazer à API da Zapster em um intervalo de tempo. Quando você passa desse limite, a API responde com o status `429 Too Many Requests` em vez de processar a requisição. ## Por que o rate limit existe O limite protege duas coisas ao mesmo tempo: * **A plataforma**, mantendo a API estável e justa para todos os clientes, sem que um único integrador consiga sobrecarregar o serviço. * **O seu número de WhatsApp**, evitando rajadas de envio que o WhatsApp interpreta como comportamento de spam. Enviar devagar e de forma constante é mais seguro para a saúde do seu número do que disparar tudo de uma vez. ## Limite padrão O limite padrão é de **3 requisições por segundo**, contadas por token de acesso (ou seja, por conta). Como toda chamada à API é autenticada, a cota é sempre atrelada ao seu token. O limite é aplicado a **todas** as rotas da API, não só ao envio de mensagens. As chamadas de listagem, consulta de destinatário e gestão de instância também consomem a mesma cota. ### Flexibilidade por plano O limite padrão atende à maioria das integrações. Se o seu volume exige mais, a cota da sua conta pode ser ampliada de acordo com o seu plano ou mediante contratação. [Fale com o suporte](https://wa.me/5587999079455?text=Olá,%20preciso%20aumentar%20o%20rate%20limit%20da%20minha%20conta) para avaliarmos o limite ideal e a melhor condição para o seu caso. Antes de pedir um limite maior, trate o rate limit no seu lado. O jeito mais robusto de integrar é **nunca depender do 429**: controle o ritmo de envio no seu código (fila e throttling) para se manter dentro do limite. Um limite maior ajuda em picos, mas não substitui o controle de ritmo no cliente. ## Headers de rate limit Toda resposta da API traz headers de rate limit que informam o estado atual da sua cota. Eles seguem o padrão de cabeçalhos [RateLimit para HTTP](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/) proposto pela IETF. | Header | Descrição | | --------------------- | -------------------------------------------------------------------------------------------------- | | `RateLimit-Limit` | Número máximo de requisições permitidas na janela atual (ex.: `3`). | | `RateLimit-Remaining` | Quantas requisições ainda restam na janela atual. | | `RateLimit-Reset` | Quantos **segundos** faltam até a janela reiniciar (é um tempo relativo, não um timestamp). | | `RateLimit-Policy` | A política aplicada, no formato `limite;w=janela` (ex.: `3;w=1` = 3 requisições a cada 1 segundo). | | `Retry-After` | Presente **apenas na resposta 429**. Quantos **segundos** esperar antes de tentar de novo. | ### Exemplo de resposta 200 Uma requisição bem-sucedida, ainda dentro da cota: ```http theme={null} HTTP/1.1 200 OK Content-Type: application/json RateLimit-Policy: 3;w=1 RateLimit-Limit: 3 RateLimit-Remaining: 2 RateLimit-Reset: 1 ``` ### Exemplo de resposta 429 Quando você passa do limite, a API responde: ```http theme={null} HTTP/1.1 429 Too Many Requests Content-Type: application/json RateLimit-Policy: 3;w=1 RateLimit-Limit: 3 RateLimit-Remaining: 0 RateLimit-Reset: 1 Retry-After: 1 { "errors": [ { "code": "rate_limited", "messages": "You can only make 3 requests every 1 seconds." } ] } ``` O campo `code` é sempre `rate_limited`, e o texto de `messages` reflete o limite atual da sua conta e o tamanho da janela. ## Como tratar o rate limit A estratégia recomendada tem duas camadas. **1. Controle o ritmo antes de enviar (preferencial).** Para integrações de alto volume (disparos em lote, filas de mensagens), limite o seu próprio ritmo de saída para nunca ultrapassar o limite. Use uma fila com throttling (por exemplo, `bottleneck` ou `p-queue` no Node.js, ou `rate.Limiter` em Go) configurada para o mesmo teto da sua conta. Assim você não depende de receber um 429. **2. Trate o 429 quando ele acontecer (rede de segurança).** Mesmo com throttling, mantenha um retry: * Ao receber `429`, espere o número de segundos indicado em `Retry-After` (ou, na falta dele, em `RateLimit-Reset`) e tente de novo. * Se não houver header, use um **backoff exponencial** com jitter (espere 1s, depois 2s, 4s, e assim por diante, com um teto). * Antes de enviar, você também pode olhar `RateLimit-Remaining`: se estiver perto de zero, aguarde `RateLimit-Reset` segundos antes da próxima chamada. ## Exemplos de código Os exemplos abaixo enviam uma mensagem e tratam o `429` respeitando os headers. Troque `YOUR_API_TOKEN` pelo seu token e `YOUR_INSTANCE_ID` pelo ID da instância. ```javascript Node.js (cliente, fetch + backoff) theme={null} // Cliente resiliente: respeita Retry-After/RateLimit-Reset e faz backoff. async function sendWithRetry(payload, { maxRetries = 5 } = {}) { for (let attempt = 0; attempt <= maxRetries; attempt++) { const res = await fetch('https://api.zapsterapi.com/v1/wa/messages', { body: JSON.stringify(payload), headers: { Authorization: 'Bearer YOUR_API_TOKEN', 'Content-Type': 'application/json', 'X-Instance-ID': 'YOUR_INSTANCE_ID', }, method: 'POST', }) if (res.status !== 429) return res // Retry-After e RateLimit-Reset vêm em segundos. const hint = Number(res.headers.get('retry-after')) || Number(res.headers.get('ratelimit-reset')) || 0 // Se o servidor não mandar dica, usa backoff exponencial (teto de 30s). const backoff = Math.min(2 ** attempt, 30) const delayMs = (Math.max(hint, backoff) + Math.random() * 0.25) * 1000 await new Promise((resolve) => setTimeout(resolve, delayMs)) } throw new Error('Rate limit: tentativas esgotadas') } const res = await sendWithRetry({ recipient: '5511999999999', text: 'Olá! Seu pedido foi confirmado.', }) console.log((await res.json()).message_id) ``` ```javascript Node.js (servidor, fila com throttling) theme={null} // Para disparos em lote: limite a saída a 3 req/s com bottleneck, // assim você nunca chega no 429. const Bottleneck = require('bottleneck') const limiter = new Bottleneck({ maxConcurrent: 3, minTime: 334, // ~1 requisição a cada 334ms = 3 por segundo reservoir: 3, reservoirRefreshAmount: 3, reservoirRefreshInterval: 1000, }) async function sendMessage(payload) { const res = await fetch('https://api.zapsterapi.com/v1/wa/messages', { body: JSON.stringify(payload), headers: { Authorization: 'Bearer YOUR_API_TOKEN', 'Content-Type': 'application/json', 'X-Instance-ID': 'YOUR_INSTANCE_ID', }, method: 'POST', }) if (!res.ok) throw new Error(`HTTP ${res.status}`) return res.json() } // Toda chamada passa pela fila, respeitando o teto de 3 req/s. const send = limiter.wrap(sendMessage) const recipients = ['5511999999999', '5511888888888', '5511777777777'] await Promise.all(recipients.map((r) => send({ recipient: r, text: 'Olá!' }))) ``` ```python Python (requests + backoff) theme={null} import random import time import requests URL = "https://api.zapsterapi.com/v1/wa/messages" HEADERS = { "Authorization": "Bearer YOUR_API_TOKEN", "X-Instance-ID": "YOUR_INSTANCE_ID", } def send_with_retry(payload, max_retries=5): delay = 1.0 # backoff usado só se o servidor não mandar dica for _ in range(max_retries + 1): res = requests.post(URL, headers=HEADERS, json=payload, timeout=30) if res.status_code != 429: res.raise_for_status() return res.json() # Retry-After e RateLimit-Reset vêm em segundos. hint = res.headers.get("Retry-After") or res.headers.get("RateLimit-Reset") sleep_for = float(hint) if hint else delay time.sleep(sleep_for + random.uniform(0, 0.25)) delay = min(delay * 2, 30) raise RuntimeError("Rate limit: tentativas esgotadas") print(send_with_retry({"recipient": "5511999999999", "text": "Olá!"})) ``` ```go Go (rate.Limiter + backoff) theme={null} package main import ( "bytes" "context" "fmt" "net/http" "strconv" "time" "golang.org/x/time/rate" ) // 3 requisições por segundo, igual ao limite padrão da conta. var limiter = rate.NewLimiter(rate.Every(time.Second/3), 3) func send(ctx context.Context, payload []byte) (*http.Response, error) { const maxRetries = 5 for attempt := 0; attempt <= maxRetries; attempt++ { // Segura o ritmo antes de sair: mantém 3 req/s. if err := limiter.Wait(ctx); err != nil { return nil, err } req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.zapsterapi.com/v1/wa/messages", bytes.NewReader(payload)) req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN") req.Header.Set("X-Instance-ID", "YOUR_INSTANCE_ID") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { return nil, err } if res.StatusCode != http.StatusTooManyRequests { return res, nil } res.Body.Close() // Retry-After e RateLimit-Reset vêm em segundos. wait := time.Second if v := res.Header.Get("Retry-After"); v != "" { if s, convErr := strconv.Atoi(v); convErr == nil { wait = time.Duration(s) * time.Second } } select { case <-time.After(wait): case <-ctx.Done(): return nil, ctx.Err() } } return nil, fmt.Errorf("rate limited: tentativas esgotadas") } func main() { payload := []byte(`{"recipient":"5511999999999","text":"Olá!"}`) res, err := send(context.Background(), payload) if err != nil { panic(err) } defer res.Body.Close() fmt.Println(res.Status) } ``` ```bash cURL / bash theme={null} # Opção A: curl repete sozinho e respeita o Retry-After do 429. curl --retry 5 --retry-all-errors --retry-delay 1 \ -X POST https://api.zapsterapi.com/v1/wa/messages \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "X-Instance-ID: YOUR_INSTANCE_ID" \ -H "Content-Type: application/json" \ -d '{"recipient":"5511999999999","text":"Olá!"}' # Opção B: controle manual lendo os headers e dormindo até a janela reiniciar. url="https://api.zapsterapi.com/v1/wa/messages" for attempt in $(seq 1 5); do # -D salva os headers; -w retorna o status HTTP. status=$(curl -sS -o response.json -D headers.txt -w '%{http_code}' \ -X POST "$url" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "X-Instance-ID: YOUR_INSTANCE_ID" \ -H "Content-Type: application/json" \ -d '{"recipient":"5511999999999","text":"Olá!"}') if [ "$status" != "429" ]; then cat response.json break fi # RateLimit-Reset é o número de segundos até a janela reiniciar. reset=$(grep -i '^ratelimit-reset:' headers.txt | tr -d '\r' | awk '{print $2}') echo "Rate limited. Aguardando ${reset:-1}s..." sleep "${reset:-1}" done ``` ## Em resumo * O padrão é **3 requisições por segundo** por token de acesso, válido para toda a API. * Toda resposta traz `RateLimit-Limit`, `RateLimit-Remaining` e `RateLimit-Reset`; o 429 traz também `Retry-After`. `RateLimit-Reset` e `Retry-After` são contados em segundos. * O 429 devolve `{ "errors": [{ "code": "rate_limited", ... }] }`. * Controle o ritmo no seu código (fila e throttling) para não depender do 429, e mantenha um retry com backoff como rede de segurança. * Precisa de mais volume? [Fale com o suporte](https://wa.me/5587999079455?text=Olá,%20preciso%20aumentar%20o%20rate%20limit%20da%20minha%20conta) para avaliar um limite maior. # Mensagens Agendadas Source: https://developer.zapsterapi.com/pt-BR/v1/concepts/scheduled-messages Como agendar o envio de mensagens do WhatsApp para uma data e hora futura ## O que são mensagens agendadas Mensagens agendadas permitem que você envie uma mensagem via API hoje, mas ela só será entregue ao destinatário em uma data e hora que você escolher. Basta incluir o campo `send_at` na requisição de envio e a Zapster cuida do resto. Isso funciona para qualquer tipo de mensagem: texto, mídia (imagem, áudio, vídeo, documento), templates e mensagens com botões. Você não precisa montar cron jobs, filas ou timers no seu sistema. A Zapster armazena a mensagem e garante o envio no horário certo. Na prática, é como deixar uma mensagem programada no WhatsApp, só que via API e com controle total sobre o que acontece em cada etapa. ## Casos de uso * **Lembretes de pagamento**: agendar uma cobrança para o dia do vencimento da fatura * **Follow-ups de venda**: mandar uma mensagem 2 dias após o primeiro contato com um lead * **Campanhas com horário definido**: preparar tudo na segunda-feira e distribuir os envios ao longo da semana * **Confirmações de consulta**: lembrar o paciente 1 dia antes do agendamento * **Onboarding de clientes**: enviar dicas de uso nos primeiros dias após o cadastro * **Pesquisas de satisfação**: disparar NPS alguns dias depois de uma compra ou atendimento ## Como funciona O fluxo é direto: 1. Você envia um `POST /v1/wa/messages` com o campo `send_at` preenchido com a data e hora desejada 2. A Zapster armazena a mensagem e cria um agendamento interno 3. No horário definido, a mensagem é enviada automaticamente pela instância configurada Se o envio falhar (instância offline, número bloqueado, problema de rede), o sistema faz até 3 tentativas. Se todas falharem, o status da mensagem vai para `failed` e o campo `errors` traz os detalhes do problema. ## Ciclo de vida da mensagem Toda mensagem agendada passa pelos seguintes estados: | De | Para | Quando acontece | | ----------- | ----------- | ------------------------------------------------------- | | — | `scheduled` | Mensagem criada via API com `send_at` | | `scheduled` | `sent` | Enviada com sucesso no horário agendado | | `scheduled` | `failed` | Falha no envio após 3 tentativas | | `scheduled` | `canceled` | Cancelada pelo usuário via `DELETE /v1/wa/messages/:id` | Depois que a mensagem atinge o status `sent`, o sistema também registra os timestamps `delivered_at` (entrega confirmada no aparelho) e `read_at` (destinatário leu a mensagem). Mas o status continua como `sent` — entrega e leitura são rastreadas apenas por timestamps. ## Limites por plano | | Essential | Pro | Enterprise | | --------------------- | --------- | ------- | ---------- | | Agendadas simultâneas | 10 | 500 | 10.000 | | Antecedência máxima | 7 dias | 1 ano | Sem limite | | Retenção do histórico | 24 horas | 30 dias | 180 dias | Explicando cada linha: * **Agendadas simultâneas**: é o número de mensagens com status `scheduled` que podem existir ao mesmo tempo na sua conta. Se você tem 10 mensagens pendentes no plano Essential, precisa esperar uma ser enviada (ou cancelar alguma) antes de agendar outra. * **Antecedência máxima**: é o quão longe no futuro você pode agendar. No plano Essential, até 7 dias. No Pro, até 1 ano. * **Retenção do histórico**: por quanto tempo você consegue consultar mensagens já enviadas. No Essential, apenas as últimas 24 horas ficam disponíveis para consulta. ## Compatibilidade Funciona com instâncias oficiais (WABA) e não oficiais (QR code). Não precisa mudar nada na configuração da sua instância para usar agendamento. ## Sobre timezones O campo `send_at` aceita datas no formato ISO 8601, que inclui informação de timezone. Na prática, você tem duas opções: * Enviar com timezone explícito: `"2026-03-30T09:00:00-03:00"` (horário de Brasília) * Enviar em UTC: `"2026-03-30T12:00:00Z"` Os dois exemplos acima representam o mesmo momento. A diferença é só a forma de escrever. A resposta da API sempre retorna datas em UTC, independente do timezone que você usou no envio. Se você está no Brasil e não quer fazer conta de fuso horário, envie a data no horário local com `-03:00` no final. Exemplo: `"2026-03-30T09:00:00-03:00"` para enviar às 9h da manhã no horário de Brasília. # Tokens Source: https://developer.zapsterapi.com/pt-BR/v1/concepts/tokens ### O que são Tokens? **Tokens** são unidades de dados que representam uma permissão ou um acesso. No contexto da **Zapster API**, tokens são utilizados como uma maneira segura de autenticar e autorizar o acesso a recursos protegidos. Eles são uma alternativa aos métodos tradicionais de autenticação, como o uso de nome de usuário e senha, oferecendo uma forma mais eficiente e segura de gerenciar o acesso à **Zapster API**. ### O que os Tokens fazem? Os tokens desempenham um papel crucial na autenticação e autorização dentro da **Zapster API**. Algumas das principais funcionalidades dos tokens incluem: * **Autenticação**: Tokens são usados para verificar a identidade de um cliente da **Zapster API**. Após a autenticação inicial, um token é gerado e pode ser utilizado para acessar recursos protegidos da API sem a necessidade de reenviar credenciais sensíveis como senhas. * **Autorização**: Além de autenticar, tokens também contêm informações sobre os direitos e permissões de um cliente, determinando quais recursos ou operações ele pode acessar ou executar na **Zapster API**. * **Sessões Sem Estado (Stateless)**: Como os tokens contêm todas as informações necessárias para autenticação e autorização, o servidor da **Zapster API** pode validar as requisições sem precisar manter o estado da sessão do cliente, o que melhora a escalabilidade da API. ### Como os Tokens funcionam na Zapster API? 1. **Geração do Token (JWT)**: O cliente da **Zapster API** gera um token JWT (JSON Web Token) através do painel de gestão de tokens disponível em [https://app.zapsterapi.com/tokens](https://app.zapsterapi.com/tokens). 2. **Uso do Token**: O cliente armazena o token e o envia junto com todas as requisições subsequentes para acessar recursos protegidos da **Zapster API**. 3. **Validação do Token**: A cada requisição recebida, o servidor da **Zapster API** valida o token JWT. Se o token for válido e não expirou, o servidor processa a requisição e retorna os dados solicitados. Se o token for inválido ou expirou, o acesso é negado. ```mermaid theme={null} sequenceDiagram participant Cliente as Cliente da Zapster API participant ZapsterAPI as Zapster API Cliente->>ZapsterAPI: Gera Token JWT Cliente->>ZapsterAPI: Requisição com Token JWT ZapsterAPI-->>Cliente: Valida e Retorna Dados ``` # Webhooks Source: https://developer.zapsterapi.com/pt-BR/v1/concepts/webhooks ### O que são Webhooks? **Webhooks** são uma maneira eficiente e automatizada de uma aplicação enviar dados em tempo real para outra aplicação. Eles permitem que sistemas diferentes comuniquem eventos específicos sem a necessidade de uma solicitação ativa da aplicação receptora. Em vez disso, a aplicação que gera o evento envia uma notificação, normalmente na forma de uma solicitação HTTP POST, para uma URL previamente configurada pela aplicação receptora. ### O que os Webhooks fazem? Os Webhooks são usados para notificar sua aplicação sobre eventos que ocorrem em outra aplicação ou serviço. Algumas das tarefas comuns realizadas por Webhooks incluem: * **Notificações em Tempo Real**: A aplicação receptora é imediatamente notificada quando algo acontece, como a criação de um novo pedido, uma mudança de status, ou uma nova mensagem. * **Automação de Processos**: Permitem automatizar respostas ou ações em sua aplicação quando certos eventos ocorrem, sem a necessidade de consultas constantes à API. * **Integração entre Sistemas**: Facilitam a integração entre sistemas diferentes, permitindo que eventos em um sistema desencadeiem ações automáticas em outro. ### Como os Webhooks funcionam? 1. **Configuração**: Primeiro, a aplicação receptora deve configurar um endpoint (uma URL pública) que estará pronto para receber as notificações via Webhook. 2. **Registro do Webhook**: A aplicação emissora precisa ser configurada para enviar notificações para o endpoint do Webhook sempre que um evento específico ocorrer. 3. **Envio do Evento**: Quando o evento configurado ocorre, a aplicação emissora envia uma solicitação HTTP POST para o endpoint do Webhook, incluindo no corpo da requisição os dados relevantes sobre o evento. 4. **Processamento do Evento**: A aplicação receptora processa a informação recebida e pode executar várias ações em resposta ao evento, como atualizar um banco de dados, enviar um e-mail, ou disparar outro processo interno. ```mermaid theme={null} sequenceDiagram participant Zapster as Zapster API participant Webhook as Endpoint Webhook Zapster->>Webhook: Envia Evento (HTTP POST) Webhook-->>Zapster: Resposta (200 OK / Erro >= 400) Zapster->>Webhook: Reenvio em caso de erro (até 5 vezes) ``` ### Um webhook, várias instâncias Um webhook na Zapster é um destino que você cadastra uma vez (a URL que vai receber os eventos) e reaproveita em quantas instâncias quiser. Cada instância decide, por conta própria, quais eventos quer receber naquele destino. Vale entender dois conceitos que trabalham juntos: * **O webhook (na sua conta)**: guarda a URL, um nome e o status (ligado ou desligado). É o endereço para onde as notificações são enviadas. Ele pertence à sua conta, não a uma instância específica. * **A associação com cada instância**: toda instância que usa o webhook ganha sua própria associação. É nela que ficam os eventos assinados e os ajustes daquela instância (ativar/desativar, modo de teste). Um mesmo webhook pode estar associado a várias instâncias ao mesmo tempo. ```mermaid theme={null} flowchart LR W["Webhook
(URL + nome + status)"] W --> A["Instância A
eventos: message.received"] W --> B["Instância B
eventos: message.sent, message.read"] W --> C["Instância C
eventos: instance.connected"] ``` #### Por que compartilhar um webhook O principal motivo é manutenção centralizada. Se você tem dezenas de instâncias enviando eventos para o mesmo sistema, cadastrar a URL uma única vez e reutilizá-la evita repetir configuração. Quando a URL precisar mudar (troca de servidor, novo domínio, ajuste de rota), você altera em um só lugar e a mudança vale para todas as instâncias que usam aquele webhook. ```mermaid theme={null} flowchart LR E["Você edita a URL
uma única vez"] --> W["Webhook"] W --> A["Instância A"] W --> B["Instância B"] W --> C["Instância C"] ``` #### Como fazer na prática O mesmo endpoint (`POST /wa/instances/:id/webhooks`) cobre os dois caminhos. A diferença está no corpo da requisição: * Envie **`url`** para criar um webhook novo e já associá-lo à instância. Use na primeira instância. * Envie **`webhook_id`** para reutilizar um webhook que já existe. Use nas demais instâncias. O corpo aceita **`url`** OU **`webhook_id`**, nunca os dois juntos. Se ambos forem enviados, a Zapster prioriza o `webhook_id`. O campo `events` é sempre obrigatório e precisa ter pelo menos um evento. **1. Primeira instância: crie o webhook com `url`** ```bash cURL theme={null} curl -X POST https://api.zapsterapi.com/v1/wa/instances/PRIMEIRA_INSTANCIA/webhooks \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://sua-app.com/webhook", "name": "Webhook de produção", "events": ["message.received", "message.sent"] }' ``` ```javascript JavaScript (client) theme={null} const response = await fetch( 'https://api.zapsterapi.com/v1/wa/instances/PRIMEIRA_INSTANCIA/webhooks', { method: 'POST', headers: { 'Authorization': 'Bearer SEU_TOKEN', 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://sua-app.com/webhook', name: 'Webhook de produção', events: ['message.received', 'message.sent'], }), }, ); const data = await response.json(); console.log(data); // A resposta traz o id do webhook criado, que você reaproveita nas próximas instâncias. // { "webhook_id": "2nenz69l0xbf0m3uu9tfo", ... } ``` ```javascript JavaScript (server) theme={null} // Node.js 18+ (fetch nativo). Guarde o token em variável de ambiente. const response = await fetch( 'https://api.zapsterapi.com/v1/wa/instances/PRIMEIRA_INSTANCIA/webhooks', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.ZAPSTER_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://sua-app.com/webhook', name: 'Webhook de produção', events: ['message.received', 'message.sent'], }), }, ); const { webhook_id } = await response.json(); console.log('Webhook criado:', webhook_id); ``` ```php PHP theme={null} true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer SEU_TOKEN', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'url' => 'https://sua-app.com/webhook', 'name' => 'Webhook de produção', 'events' => ['message.received', 'message.sent'], ]), ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['webhook_id']; ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) func main() { body, _ := json.Marshal(map[string]any{ "url": "https://sua-app.com/webhook", "name": "Webhook de produção", "events": []string{"message.received", "message.sent"}, }) req, _ := http.NewRequest( http.MethodPost, "https://api.zapsterapi.com/v1/wa/instances/PRIMEIRA_INSTANCIA/webhooks", bytes.NewReader(body), ) req.Header.Set("Authorization", "Bearer SEU_TOKEN") req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) fmt.Println(string(data)) } ``` ```python Python theme={null} import requests response = requests.post( "https://api.zapsterapi.com/v1/wa/instances/PRIMEIRA_INSTANCIA/webhooks", headers={"Authorization": "Bearer SEU_TOKEN"}, json={ "url": "https://sua-app.com/webhook", "name": "Webhook de produção", "events": ["message.received", "message.sent"], }, ) data = response.json() print(data["webhook_id"]) ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class CreateWebhook { public static void main(String[] args) throws Exception { String body = """ { "url": "https://sua-app.com/webhook", "name": "Webhook de produção", "events": ["message.received", "message.sent"] } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.zapsterapi.com/v1/wa/instances/PRIMEIRA_INSTANCIA/webhooks")) .header("Authorization", "Bearer SEU_TOKEN") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={null} require "net/http" require "json" require "uri" uri = URI("https://api.zapsterapi.com/v1/wa/instances/PRIMEIRA_INSTANCIA/webhooks") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri) request["Authorization"] = "Bearer SEU_TOKEN" request["Content-Type"] = "application/json" request.body = { url: "https://sua-app.com/webhook", name: "Webhook de produção", events: ["message.received", "message.sent"] }.to_json response = http.request(request) data = JSON.parse(response.body) puts data["webhook_id"] ``` **2. Demais instâncias: reutilize com `webhook_id`** Use o `webhook_id` devolvido no passo anterior para associar o mesmo webhook a outra instância. Note que os eventos podem ser diferentes: cada instância assina o que faz sentido para ela. ```bash cURL theme={null} curl -X POST https://api.zapsterapi.com/v1/wa/instances/SEGUNDA_INSTANCIA/webhooks \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "webhook_id": "2nenz69l0xbf0m3uu9tfo", "events": ["message.received"] }' ``` ```javascript JavaScript (client) theme={null} const response = await fetch( 'https://api.zapsterapi.com/v1/wa/instances/SEGUNDA_INSTANCIA/webhooks', { method: 'POST', headers: { 'Authorization': 'Bearer SEU_TOKEN', 'Content-Type': 'application/json', }, body: JSON.stringify({ webhook_id: '2nenz69l0xbf0m3uu9tfo', events: ['message.received'], }), }, ); const data = await response.json(); console.log(data); ``` ```javascript JavaScript (server) theme={null} // Node.js 18+ (fetch nativo). const response = await fetch( 'https://api.zapsterapi.com/v1/wa/instances/SEGUNDA_INSTANCIA/webhooks', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.ZAPSTER_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ webhook_id: '2nenz69l0xbf0m3uu9tfo', events: ['message.received'], }), }, ); console.log(await response.json()); ``` ```php PHP theme={null} true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer SEU_TOKEN', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'webhook_id' => '2nenz69l0xbf0m3uu9tfo', 'events' => ['message.received'], ]), ]); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) func main() { body, _ := json.Marshal(map[string]any{ "webhook_id": "2nenz69l0xbf0m3uu9tfo", "events": []string{"message.received"}, }) req, _ := http.NewRequest( http.MethodPost, "https://api.zapsterapi.com/v1/wa/instances/SEGUNDA_INSTANCIA/webhooks", bytes.NewReader(body), ) req.Header.Set("Authorization", "Bearer SEU_TOKEN") req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) fmt.Println(string(data)) } ``` ```python Python theme={null} import requests response = requests.post( "https://api.zapsterapi.com/v1/wa/instances/SEGUNDA_INSTANCIA/webhooks", headers={"Authorization": "Bearer SEU_TOKEN"}, json={ "webhook_id": "2nenz69l0xbf0m3uu9tfo", "events": ["message.received"], }, ) print(response.json()) ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class ReuseWebhook { public static void main(String[] args) throws Exception { String body = """ { "webhook_id": "2nenz69l0xbf0m3uu9tfo", "events": ["message.received"] } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.zapsterapi.com/v1/wa/instances/SEGUNDA_INSTANCIA/webhooks")) .header("Authorization", "Bearer SEU_TOKEN") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={null} require "net/http" require "json" require "uri" uri = URI("https://api.zapsterapi.com/v1/wa/instances/SEGUNDA_INSTANCIA/webhooks") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri) request["Authorization"] = "Bearer SEU_TOKEN" request["Content-Type"] = "application/json" request.body = { webhook_id: "2nenz69l0xbf0m3uu9tfo", events: ["message.received"] }.to_json puts http.request(request).body ``` #### Editar e remover: o que muda Como o webhook e a associação são coisas distintas, editar ou remover tem escopos diferentes. Vale conhecer cada operação antes de aplicar mudanças em produção. | Operação | Endpoint | Escopo | Efeito | | --------------------- | -------------------------------------------------------------------- | --------- | ---------------------------------------------------- | | Criar + associar novo | `POST /wa/instances/:id/webhooks` com `url` | Instância | Cria um webhook novo e associa à instância | | Reusar existente | `POST /wa/instances/:id/webhooks` com `webhook_id` | Instância | Associa um webhook já existente a mais uma instância | | Editar o webhook | `PATCH /webhooks/:id` (url/name/enabled) | Conta | Propaga para TODAS as instâncias associadas | | Editar a associação | `PATCH /wa/instances/:id/webhooks/:whId` (events/enabled/test\_mode) | Instância | Muda só a assinatura daquela instância | | Desassociar | `DELETE /wa/instances/:id/webhooks/:whId` | Instância | Só desliga daquela instância; segue ativo nas demais | | Excluir | `DELETE /webhooks/:id` | Conta | Remove de TODAS as instâncias | **Editar o webhook propaga para todo mundo.** Alterar a URL ou o nome pelo endpoint de conta (`PATCH /webhooks/:id`) afeta todas as instâncias associadas. Se você precisa de uma URL diferente para apenas uma instância, crie um webhook novo em vez de editar o existente. **Desassociar não é o mesmo que excluir.** `DELETE /wa/instances/:id/webhooks/:whId` apenas desliga o webhook daquela instância; ele continua ativo nas outras e na sua conta. Para apagar o webhook de vez, use `DELETE /webhooks/:id`. #### Como diferenciar a origem no receptor Cada instância entrega os eventos de forma independente, mesmo quando compartilham o mesmo webhook. Para saber de onde veio cada notificação, use os cabeçalhos HTTP: * **`X-Instance-ID`**: identifica a instância que gerou o evento (a origem real). * **`X-Webhook-ID`**: identifica o webhook compartilhado que entregou a notificação. Como os eventos assinados podem ser diferentes em cada instância, o mesmo webhook pode receber `message.received` de uma instância e `message.sent` de outra. Sempre olhe o `X-Instance-ID` para rotear ou registrar o evento corretamente. #### Quando compartilhar e quando separar Compartilhar um webhook faz sentido quando: * Todas as instâncias entregam para o mesmo sistema (um CRM, uma fila, um endpoint central). * Você quer trocar a URL de destino em um só lugar no futuro. * O processamento no receptor já usa o `X-Instance-ID` para separar as origens. Webhooks distintos por instância fazem mais sentido quando: * Cada instância pertence a um cliente ou produto diferente, com URL própria. * Você precisa ligar ou desligar o destino de uma instância sem tocar nas outras. * Ambientes separados (produção e homologação) não devem se misturar. #### Boas práticas * Guarde o `webhook_id` retornado na criação; é ele que você reutiliza nas próximas instâncias. * Use o `name` do webhook para deixar claro o propósito (por exemplo, "CRM produção"). * No receptor, trate `X-Instance-ID` como a fonte da verdade sobre a origem do evento. * Antes de editar a URL de um webhook compartilhado, confirme quais instâncias serão afetadas com o endpoint de [listagem de webhooks](/pt-BR/v1/api-reference/webhooks/list-webhooks). #### Perguntas frequentes Não pelo endpoint de edição do webhook, que é de conta e propaga para todas as instâncias associadas. Para uma URL exclusiva, crie um webhook novo enviando `url` na criação e associe apenas à instância desejada. Não. `DELETE /wa/instances/:id/webhooks/:whId` só desliga o webhook daquela instância. Ele continua ativo nas demais e na sua conta. Para apagar de vez, use `DELETE /webhooks/:id`. Não. Os eventos são definidos por instância, na associação. Uma pode assinar `message.received` e outra `message.sent`, mesmo apontando para o mesmo webhook. Pelo cabeçalho `X-Instance-ID`, presente em toda notificação. O `X-Webhook-ID` indica o webhook que fez a entrega. Envie apenas um dos dois. Se ambos forem informados, a Zapster prioriza o `webhook_id` e ignora a `url`. Para os detalhes de cada endpoint, consulte a referência da API: [criar/associar webhook](/pt-BR/v1/api-reference/instance/create-webhook), [editar associação da instância](/pt-BR/v1/api-reference/instance/update-webhook), [desassociar da instância](/pt-BR/v1/api-reference/instance/delete-webhook), [editar o webhook](/pt-BR/v1/api-reference/webhooks/update-webhook) e [excluir o webhook](/pt-BR/v1/api-reference/webhooks/delete-webhook). ### Tratamento de Falhas e Retentativas Em um cenário ideal, a aplicação receptora recebe e processa a solicitação do Webhook sem problemas. No entanto, falhas podem ocorrer devido a vários fatores, como indisponibilidade do servidor, problemas de rede, ou erros de processamento. Para garantir que as notificações importantes não sejam perdidas, implementamos um mecanismo de **retentativa**. Se a aplicação receptora responder com um código de status HTTP maior que 400 (indicando um erro), a aplicação emissora tentará reenviar a notificação do Webhook até **5 vezes**. #### Detalhes da Retentativa * **Critério de Falha**: Qualquer resposta com código de status HTTP maior ou igual que 400. * **Número de Retentativas**: Até 5 tentativas. * **Intervalo entre Retentativas**: O intervalo entre cada retentativa aumenta progressivamente usando um fator de 2,5. Os intervalos em segundos são os seguintes: * **1ª Tentativa:** 2,5 segundos (`2.5^1`) * **2ª Tentativa:** \~6 segundos (`2.5^2`) * **3ª Tentativa:** \~15 segundos (`2.5^3`) * **4ª Tentativa:** \~39 segundos (`2.5^4`) * **5ª Tentativa:** \~97 segundos (`2.5^5`) Cada intervalo é calculado como `2,5^n`, onde `n` é o número da tentativa. ### Cabeçalhos HTTP Personalizados Todas as notificações de webhook enviadas pela Zapster API incluem cabeçalhos HTTP personalizados que identificam a origem e o contexto do evento. Esses cabeçalhos são úteis para validação, logging e configuração de regras de firewall. | Cabeçalho | Tipo | Descrição | Exemplo | Presente em | | ----------------- | ------ | ---------------------------------- | ------------------ | ------------------------------ | | `X-Instance-ID` | string | ID da instância que gerou o evento | `inst_abc123` | Todas as notificações | | `X-Message-ID` | string | ID único da notificação | `msg_xyz789` | Todas as notificações | | `X-Webhook-ID` | string | ID do webhook registrado | `whk_def456` | Quando webhook está registrado | | `X-Attempt-Count` | number | Número da tentativa (1-5) | `1` | Todas as notificações | | `User-Agent` | string | Identificador do emissor | `Zapsterapi/1.2.3` | Todas as notificações | ### Validação e Allowlisting Você pode utilizar os cabeçalhos HTTP personalizados para validar a origem das notificações recebidas. Como o webhook é entregue via `POST` no seu servidor, os exemplos abaixo mostram o endpoint receptor em cada linguagem (cURL e código de navegador não recebem webhooks, por isso não aparecem aqui): ```javascript JavaScript (server) theme={null} // Node.js + Express app.post('/webhook', (req, res) => { const instanceId = req.headers['x-instance-id'] const messageId = req.headers['x-message-id'] const attemptCount = req.headers['x-attempt-count'] console.log(`Notificação recebida da instância ${instanceId}`) console.log(`ID da mensagem: ${messageId}, tentativa: ${attemptCount}`) // Valide a origem antes de processar if (!instanceId) { return res.status(400).json({ error: 'Cabeçalho X-Instance-ID ausente' }) } // Processe o evento const event = req.body console.log(`Evento: ${event.type}`, event.data) res.status(200).json({ received: true }) }) ``` ```php PHP theme={null} 'Cabeçalho X-Instance-ID ausente']); exit; } // Processe o evento $event = json_decode(file_get_contents('php://input'), true); error_log("Evento: {$event['type']}"); http_response_code(200); echo json_encode(['received' => true]); ``` ```go Go theme={null} package main import ( "encoding/json" "log" "net/http" ) func webhookHandler(w http.ResponseWriter, r *http.Request) { instanceID := r.Header.Get("X-Instance-ID") messageID := r.Header.Get("X-Message-ID") attemptCount := r.Header.Get("X-Attempt-Count") log.Printf("Notificação recebida da instância %s", instanceID) log.Printf("ID da mensagem: %s, tentativa: %s", messageID, attemptCount) // Valide a origem antes de processar if instanceID == "" { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "Cabeçalho X-Instance-ID ausente"}) return } // Processe o evento var event map[string]any json.NewDecoder(r.Body).Decode(&event) log.Printf("Evento: %v", event["type"]) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]bool{"received": true}) } ``` ```python Python theme={null} from flask import Flask, request, jsonify app = Flask(__name__) @app.post("/webhook") def webhook(): instance_id = request.headers.get("X-Instance-ID") message_id = request.headers.get("X-Message-ID") attempt_count = request.headers.get("X-Attempt-Count") print(f"Notificação recebida da instância {instance_id}") print(f"ID da mensagem: {message_id}, tentativa: {attempt_count}") # Valide a origem antes de processar if not instance_id: return jsonify(error="Cabeçalho X-Instance-ID ausente"), 400 # Processe o evento event = request.get_json() print(f"Evento: {event['type']}", event["data"]) return jsonify(received=True), 200 ``` ```java Java theme={null} import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.Map; @RestController public class WebhookController { @PostMapping("/webhook") public ResponseEntity receive( @RequestHeader(value = "X-Instance-ID", required = false) String instanceId, @RequestHeader(value = "X-Message-ID", required = false) String messageId, @RequestHeader(value = "X-Attempt-Count", required = false) String attemptCount, @RequestBody Map event) { System.out.printf("Notificação recebida da instância %s%n", instanceId); System.out.printf("ID da mensagem: %s, tentativa: %s%n", messageId, attemptCount); // Valide a origem antes de processar if (instanceId == null) { return ResponseEntity.badRequest().body(Map.of("error", "Cabeçalho X-Instance-ID ausente")); } // Processe o evento System.out.println("Evento: " + event.get("type")); return ResponseEntity.ok(Map.of("received", true)); } } ``` ```ruby Ruby theme={null} require "sinatra" require "json" post "/webhook" do instance_id = request.env["HTTP_X_INSTANCE_ID"] message_id = request.env["HTTP_X_MESSAGE_ID"] attempt_count = request.env["HTTP_X_ATTEMPT_COUNT"] puts "Notificação recebida da instância #{instance_id}" puts "ID da mensagem: #{message_id}, tentativa: #{attempt_count}" # Valide a origem antes de processar halt 400, { error: "Cabeçalho X-Instance-ID ausente" }.to_json if instance_id.nil? # Processe o evento event = JSON.parse(request.body.read) puts "Evento: #{event['type']}" content_type :json { received: true }.to_json end ``` **Allowlisting em WAF/Firewall:** Se você utiliza um Web Application Firewall (WAF) ou regras de firewall, configure-o para aceitar requisições `POST` que contenham os cabeçalhos `X-Instance-ID`, `X-Message-ID`, `X-Attempt-Count` e `User-Agent` com o prefixo `Zapsterapi/`. # Saúde do número: boas práticas e restrições Source: https://developer.zapsterapi.com/pt-BR/v1/guides/best-practices Como manter seu número saudável e evitar restrições no WhatsApp O WhatsApp usa algoritmos para detectar comportamento automatizado. Quando o sistema entende que um número está enviando mensagens de forma não natural, ele pode restringir ou banir o número. As boas práticas abaixo ajudam a manter seu número funcionando por mais tempo. A lógica é simples: quanto mais o seu uso se parecer com o de uma pessoa real, menor o risco de restrição. Se você caiu aqui procurando por que seu WhatsApp Business bloqueou ou como evitar banimento no WhatsApp, comece pelo modelo mental do WhatsApp Web na próxima seção. É o que mais reduz banimento na prática. Estas recomendações se aplicam principalmente a instâncias não oficiais (QR code). Instâncias WABA (oficiais) seguem as políticas da Meta diretamente e têm risco muito menor de banimento. Ainda assim, boas práticas de conteúdo e opt-in valem para os dois tipos. ## Perfil completo Preencha o perfil do WhatsApp Business com o máximo de informações possíveis: * Foto de perfil (logotipo ou foto profissional) * Nome comercial * Descrição do negócio * Endereço * Horário de funcionamento * E-mail de contato * Site Um perfil completo aumenta a confiança do algoritmo no número. Perfis vazios ou incompletos são um sinal de conta descartável. Ative o PIN de segurança (verificação em duas etapas) no WhatsApp. Além de proteger contra clonagem, indica para o sistema que é uma conta legítima que se preocupa com segurança. ## Pense como o WhatsApp Web: múltiplas conexões por número A maior parte dos problemas de banimento não vem da API. Vem de tentar fazer um único número se comportar como dezenas de pessoas ao mesmo tempo. Para evitar isso, vale entender como o próprio WhatsApp foi desenhado. O WhatsApp permite vincular **até 4 dispositivos** a um mesmo número, além do celular principal. É o mecanismo do WhatsApp Web e do WhatsApp Desktop: você abre o mesmo número no notebook do trabalho, no computador de casa e no tablet, e todos conversam pela mesma conta. Isso é uso legítimo e esperado. Cada conexão dessas é uma **instância** do ponto de vista da rede do WhatsApp. Quando você cria uma instância na Zapster, está ocupando um desses espaços de dispositivo vinculado. O problema aparece quando uma única instância tenta atender muita gente. Para o algoritmo, uma instância respondendo 400 conversas por dia parece **uma pessoa sobrecarregada de forma impossível**, e isso é um sinal clássico de automação abusiva. Ninguém digita para 400 contatos sozinho num dia sem parar. A boa prática é distribuir. Abra de 2 a 4 instâncias do mesmo número na Zapster e divida as conversas entre elas. Cada instância passa a funcionar como um "agente" virtual: * ❌ **1 instância** atendendo sozinha 400 conversas/dia → parece um robô. * ✅ **4 instâncias** do mesmo número atendendo 100 conversas/dia cada → parece uma equipe de 4 atendentes humanos usando o WhatsApp Web. O volume total é o mesmo. O que muda é a distribuição: em vez de um humano impossível, o padrão fica parecido com vários humanos plausíveis. Para automatizar esse balanceamento, está em desenvolvimento o **Smart Sending Mode** com pool de instâncias, que distribui as conversas entre as instâncias do mesmo número de forma automática. Enquanto ele não chega, você consegue o mesmo efeito criando as instâncias manualmente e roteando no seu código. Veja [Instâncias](/pt-BR/v1/concepts/instances) para entender os tipos de conexão. ## O erro mais comum: conectar e já sair enviando Este é o erro que mais gera restrição em número novo, e quase ninguém enxerga como erro: criar a instância, ler o QR code e, no mesmo minuto, disparar mensagens para uma lista de contatos que nunca falaram com você. Do seu ponto de vista, o número "está funcionando". Do ponto de vista do WhatsApp, um número que acabou de aparecer já saiu abordando dezenas de estranhos, que é exatamente o padrão de uma conta criada para spam. ### Responder é seguro, iniciar conversa nova é onde mora o risco O WhatsApp trata duas coisas de formas muito diferentes: * **Responder quem te procurou** — alguém te mandou mensagem e você respondeu. Risco baixíssimo. É o uso mais natural que existe. * **Iniciar conversa com contato novo** (o "reach-out") — você aborda alguém que nunca te escreveu. É aqui que o algoritmo presta atenção, porque é o que um spammer faz. Quando um número está "sob suspeita" — conexão recém-criada, histórico de restrição, ou um volume estranho de conversas novas em pouco tempo — o WhatsApp aplica uma **trava temporal**: o número continua conseguindo responder conversas **já abertas**, mas **iniciar conversa nova fica bloqueado** por um período. Esse período vai de algumas horas a **vários dias** — restrições de 7 ou 14 dias são comuns. Essa trava costuma atingir **apenas a conexão da API** (o dispositivo vinculado), não o celular. Por isso você vê uma cena confusa: o número manda mensagem normalmente pelo aplicativo no celular, mas a instância falha ao enviar. **Não é bug da API** — é a restrição agindo especificamente sobre a conexão que estava disparando para contatos novos. ### Aquecimento (warmup): os primeiros dias definem o resto Número novo é como conta nova em qualquer lugar: precisa construir reputação antes de pedir confiança. Trate as primeiras horas e dias como um período de aquecimento. **Nas primeiras horas / primeiro dia:** * Priorize **responder** mensagens que chegam (inbound). Todo início de conversa que parte do outro lado conta a seu favor. * Evite iniciar conversas frias em lote. Se precisar iniciar alguma, que sejam poucas e espaçadas. * Nada de **criar grupo, adicionar participantes em massa ou disparo em lote** — são as ações de maior risco logo após conectar. **Ao longo da primeira semana:** * Suba o volume **gradualmente**, não de zero para o volume-alvo de uma vez. * Mantenha uma **proporção saudável** entre conversas que você recebe e conversas que você inicia. Um número que só inicia e nunca recebe é suspeito; um número que conversa nas duas direções parece real. A tática mais eficaz de aquecimento é fazer o número **receber** conversas reais antes de começar a disparar. Coloque o número na bio, no site, num anúncio de clique-para-WhatsApp, no rodapé do e-mail. Cada pessoa que te procura primeiro gera exatamente o tipo de interação (inbound → resposta) que amadurece o número mais rápido e com menos risco. ### Reconexão não é a mesma coisa que número novo Importante não confundir os dois casos: * **Só reiniciou a instância** (reconectou o mesmo número que já vinha operando saudável) → **não precisa reaquecer do zero**. O histórico do número continua valendo; siga o ritmo normal. * **Número novo** ou que **acabou de sair de uma restrição** → aí sim o cuidado de aquecimento vale integralmente. Número recém-saído de restrição volta "sensível" e uma recaída no mesmo padrão costuma trazer uma trava mais longa. ## Peça para ser adicionado nos contatos Sempre que possível, peça ao destinatário que adicione o número da instância na agenda de contatos do celular. Quando alguém salva seu número, o WhatsApp entende que existe um relacionamento real entre vocês. Isso eleva o "score" do número e reduz a chance de que suas mensagens sejam marcadas como spam. Na prática, você pode incluir uma frase como: > "Para garantir que nossas mensagens cheguem sempre, salve este número nos seus contatos." ## Cadência entre mensagens Não envie mensagens em rajada. O WhatsApp detecta envios em massa com facilidade. **Recomendações de intervalo:** | Cenário | Intervalo mínimo | Ideal | | ------------------------------- | ---------------- | ---------------- | | Envio para lista de contatos | 30 segundos | 45 a 60 segundos | | Follow-up após interação | 10 segundos | 20 a 30 segundos | | Respostas automáticas (chatbot) | 3 a 5 segundos | 5 a 10 segundos | Quanto maior o intervalo, melhor. Se você precisa enviar para 100 contatos, espere pelo menos 30 segundos entre cada envio. Isso significa que o lote leva cerca de 50 minutos pra finalizar. Parece lento, mas é o que mantém o número vivo. Enviar mais de 1 mensagem por segundo para destinatários diferentes é um dos comportamentos que mais gera restrição. Evite sempre. ## Janela humana: 1 conversa por vez por instância Repare em como uma pessoa real usa o WhatsApp Web. Ela foca em um contato, troca algumas mensagens, resolve aquele assunto e só então passa para o próximo. Ela não responde 5 conversas em paralelo, palavra por palavra, ao mesmo tempo. Esse foco sequencial é a assinatura de um humano de verdade. A boa prática é reproduzir isso por instância: trate uma conversa por vez, dentro de uma "janela" ativa. Um padrão simples que funciona bem é o **debounce por contato**: uma janela de cerca de 1 minuto dedicada a um contato antes de seguir para o próximo. Quando outro contato escreve no meio dessa janela, você não atende em paralelo. Responde algo curto como "já te respondo, um instante" e coloca aquele contato na fila. O fluxo, em palavras: 1. Chega uma mensagem. 2. Já existe uma janela ativa nessa instância? * **Sim** → enfileira o contato e responde com uma mensagem de espera. * **Não** → abre a janela para esse contato e responde normalmente. 3. Ao fim da janela (ou quando o assunto se resolve), pega o próximo da fila. O ponto importante: isso **reduz banimento sem reduzir o volume total**. Você continua atendendo a mesma quantidade de gente. Só muda a distribuição no tempo, o que evita o padrão de um robô falando com todo mundo ao mesmo tempo. O **Smart Sending Mode** (em desenvolvimento) vai cuidar dessa fila e do debounce por contato de forma nativa, junto com o pool de instâncias. ## Presença em paralelo é veneno Antes de enviar uma mensagem, use o [endpoint de atualização de presença](/pt-BR/v1/api-reference/utils/presence-update) para simular o comportamento humano: ```bash theme={null} # Simular "Digitando..." por 5 segundos antes de enviar curl -X PATCH https://api.zapsterapi.com/v1/wa/instances/SUA_INSTANCIA/presence \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "recipient": "5511999999999", "status": "typing", "duration_strategy": "maximum_duration", "max_duration": 5 }' ``` O destinatário vai ver "Digitando..." por 5 segundos antes de receber a mensagem. Isso faz o envio parecer mais natural, tanto para quem recebe quanto para o algoritmo. Para áudios, use `"status": "recording"` para mostrar "Gravando áudio...". Você também pode usar `"duration_strategy": "until_next_message"` para manter o status ativo até que a mensagem seja enviada de fato. Isso funciona bem quando o tempo de processamento varia (como em respostas de IA). ### Por que paralelizar presença entrega o bot Aqui está o erro que mais derruba número: disparar `presence: typing` (ou `recording`) para dois destinatários ao mesmo tempo na mesma instância. Pense no que isso representa: uma única pessoa "Digitando..." para o João e para a Maria no mesmo segundo. Nenhum humano digita para duas pessoas simultaneamente. É um sinal de robô que não tem como disfarçar. A solução é **serializar a presença por instância**: processar a fila um por vez, com a sua presença, e só disparar o próximo quando o anterior terminar. **Anti-padrão** (presença em paralelo, todos ao mesmo tempo): ```javascript theme={null} // ❌ NÃO faça isso: dispara "Digitando..." para todos de uma vez await Promise.all( contacts.map(async (contact) => { await setPresence(contact.phone, 'typing'); // todos "digitando" juntos await sendMessage(contact.phone, contact.text); }), ); ``` **Correto** (uma conversa por vez, presença serializada): ```javascript theme={null} // ✅ Serializa: só começa a "digitar" para o próximo quando o atual termina async function setPresence(phone, status) { await fetch(`https://api.zapsterapi.com/v1/wa/instances/SUA_INSTANCIA/presence`, { method: 'PATCH', headers: { Authorization: 'Bearer SEU_TOKEN', 'Content-Type': 'application/json', }, body: JSON.stringify({ recipient: phone, status, // mantém "Digitando..." até a mensagem sair de fato duration_strategy: 'until_next_message', }), }); } for (const contact of contacts) { await setPresence(contact.phone, 'typing'); await sendMessage(contact.phone, contact.text); await new Promise((r) => setTimeout(r, 45000)); // intervalo entre conversas } ``` O `duration_strategy: 'until_next_message'` é um aliado aqui: ele segura o "Digitando..." pelo tempo que o seu processamento levar e o encerra no envio, sem você ter que cronometrar nada. Combinado com a serialização, a presença fica naturalmente alinhada com uma conversa de cada vez. ## Use o dispositivo original regularmente A Meta desconecta dispositivos vinculados que ficam inativos por mais de 14 dias. Isso significa que se ninguém abrir o aplicativo do WhatsApp no celular original durante 14 dias, a conexão da instância pode cair. **Recomendação:** pelo menos uma vez por semana, abra o WhatsApp no celular onde o número está registrado. Não precisa fazer nada complexo: * Abrir o app * Navegar pelas conversas * Enviar uma mensagem para alguém (pode ser para você mesmo em outro número) * Verificar se tem atualizações pendentes do app Esse uso periódico mantém o vínculo ativo e evita desconexões inesperadas. Referência: [Sobre os dispositivos associados no WhatsApp Business](https://faq.whatsapp.com/647349420360876/?locale=pt_PT) ## Personalize as mensagens Mensagens idênticas enviadas para muitas pessoas são um dos sinais mais fortes de automação. O WhatsApp compara o conteúdo das mensagens enviadas por um número e detecta padrões repetitivos. **Ruim:** ``` Olá! Temos uma promoção especial para você. Acesse nosso site. ``` (mesma mensagem para 200 pessoas) **Bom:** ``` Olá João! Vi que você se interessou pelo plano Pro na semana passada. Ainda está avaliando? Posso te ajudar com alguma dúvida. ``` (mensagem personalizada com nome e contexto) Na API da Zapster, você monta a mensagem dinamicamente no seu código antes de enviar. Cada request ao `POST /v1/wa/messages` pode ter um texto diferente: ```javascript theme={null} const contacts = [ { phone: '5511999999999', name: 'João', interest: 'plano Pro' }, { phone: '5511888888888', name: 'Maria', interest: 'integração N8n' }, ]; for (const contact of contacts) { await fetch('https://api.zapsterapi.com/v1/wa/messages', { method: 'POST', headers: { 'Authorization': 'Bearer SEU_TOKEN', 'X-Instance-ID': 'SUA_INSTANCIA', 'Content-Type': 'application/json', }, body: JSON.stringify({ recipient: contact.phone, text: `Olá ${contact.name}! Vi que você se interessou por ${contact.interest}. Posso te ajudar?`, }), }); // Esperar entre envios await new Promise(r => setTimeout(r, 45000)); // 45 segundos } ``` ## Opt-in e opt-out Envie mensagens apenas para quem deu consentimento. Pessoas que não esperam receber suas mensagens vão denunciar como spam, e isso derruba o score do número rapidamente. **Opt-in:** Tenha alguma forma de consentimento antes de enviar. Pode ser um formulário no site, uma confirmação por e-mail, ou uma interação prévia no próprio WhatsApp. **Opt-out:** Sempre dê a opção de parar de receber mensagens. Inclua algo como: > "Se não quiser mais receber nossas mensagens, responda SAIR." E respeite quando alguém pedir para sair. Continuar enviando para quem pediu para parar é o caminho mais rápido para restrição. ## Higienização de contatos Antes de enviar para uma lista, verifique se os números são válidos: * Use o [endpoint de verificação de destinatário](/pt-BR/v1/api-reference/utils/fetch-recipient) para checar se o número tem WhatsApp * Remova números que não respondem há meses * Remova números que pediram opt-out * Remova duplicatas Enviar para números inválidos ou inativos gera falhas silenciosas que o WhatsApp contabiliza negativamente. ## Horários de envio Enviar mensagens de madrugada ou em horários estranhos gera mais denúncias. Prefira horários comerciais: | Horário | Recomendação | | ---------------- | ---------------------------------------------------- | | 8h às 12h | Bom | | 12h às 14h | Aceitável (horário de almoço, taxa de leitura menor) | | 14h às 18h | Bom | | 18h às 20h | Aceitável | | 20h às 8h | Evitar | | Finais de semana | Evitar para mensagens comerciais | Com o recurso de [mensagens agendadas](/pt-BR/v1/concepts/scheduled-messages), você pode preparar o envio fora do horário e deixar a Zapster disparar no momento certo. ## O que fazer se receber restrição Se o número for restrito temporariamente: 1. **Pare imediatamente** de enviar mensagens automatizadas 2. **Espere** o período de restrição passar. A duração varia bastante: pode ser de algumas horas a **7 ou 14 dias**, dependendo do histórico do número e do tipo de comportamento que disparou a trava (restrições de conversa nova costumam ser as mais longas) 3. **Volte devagar** com volume reduzido e intervalos maiores — e, se o número era novo ou reincidente, retome o aquecimento (veja *"O erro mais comum: conectar e já sair enviando"*) priorizando responder inbound antes de iniciar conversas 4. **Revise** suas práticas antes de retomar o volume anterior Se o número for banido permanentemente: 1. O número não pode ser recuperado na maioria dos casos 2. Considere migrar para uma [instância WABA (oficial)](/pt-BR/v1/guides/waba-vs-unofficial) que não tem esse risco 3. Se precisar de um novo número não oficial, comece com volume baixo e siga todas as práticas acima desde o início ## Auto-auditoria do workflow (regra 80/20) Antes de concluir que "a API está banindo meu número", audite o seu próprio fluxo. Na prática, a causa quase sempre está no desenho do workflow, não na ferramenta. Vale aplicar a regra 80/20 aqui: 80% do trabalho é planejamento e análise do fluxo, 20% é a execução do código. Quem inverte essa proporção (joga código em produção e só depois investiga) costuma queimar números no processo. Passe o seu fluxo por estas perguntas: * Quantas instâncias eu mantenho por número? Estou usando o modelo do WhatsApp Web (até 4 dispositivos) ou sobrecarregando uma só? * Meu agente/bot responde várias conversas em paralelo? Ou trata uma de cada vez? * Tenho controle de concorrência (fila, lock, debounce por contato)? Ou tudo dispara ao mesmo tempo? * Onde eu envio `presence: typing`? Está serializado por instância ou paralelo? * Qual o intervalo médio entre envios para o **mesmo** contato? * Meu volume diário bate com "X atendentes humanos trabalhando das 9h às 18h"? Ou só faz sentido se fosse um robô? Esse tipo de auditoria é uma ótima tarefa para delegar a um LLM (Claude, ChatGPT). Descreva o seu workflow atual (quantas instâncias, como o bot enfileira, onde dispara presença) e peça para o modelo apontar onde o padrão se afasta de um humano usando o WhatsApp Web. Vale o posicionamento honesto: **você é dono do desenho do seu sistema**. A Zapster entrega as ferramentas (instâncias, presença, agendamento, fila), não a estratégia de como você as combina. Um workflow bem desenhado mantém o número saudável com as mesmas ferramentas que um workflow mal desenhado usa para queimá-lo. ## Checklist rápido Use esta lista antes de iniciar um envio em volume: * [ ] O perfil do WhatsApp está completo (foto, descrição, endereço)? * [ ] Se a instância é nova (ou acabou de sair de restrição), ela já foi aquecida antes de disparar em volume? * [ ] Nas primeiras horas, priorizei **responder** inbound em vez de iniciar conversas frias? * [ ] Evitei criar grupo, adicionar em massa e disparo em lote logo após conectar? * [ ] Os destinatários deram consentimento para receber mensagens? * [ ] As mensagens estão personalizadas com nome ou contexto? * [ ] O intervalo entre envios é de pelo menos 30 segundos? * [ ] Estou usando o endpoint de presença ("Digitando...")? * [ ] Tenho múltiplas instâncias por número para distribuir as conversas? * [ ] A presença ("Digitando...") está serializada (uma por vez), nunca em paralelo? * [ ] Tenho debounce por contato (uma janela por vez, sem atender tudo simultaneamente)? * [ ] Os números da lista são válidos e ativos? * [ ] O horário de envio é dentro do horário comercial? * [ ] Tem opção de opt-out na mensagem? * [ ] Usei o app do WhatsApp no celular original esta semana? ## Referências * [Sobre os dispositivos associados no WhatsApp Business](https://faq.whatsapp.com/647349420360876/?locale=pt_PT) * [Como vincular vários dispositivos a um número (recurso multidispositivo)](https://faq.whatsapp.com/378279804439436/?locale=pt_BR) * [Sobre contas banidas no WhatsApp](https://faq.whatsapp.com/361005896189245?helpref=faq_content) * [Sobre restrições temporárias no WhatsApp](https://faq.whatsapp.com/465883178708358?helpref=faq_content) * [Sobre mensagens de segurança no WhatsApp](https://faq.whatsapp.com/717472490411581/?helpref=faq_content) # Conectando uma instância WABA Source: https://developer.zapsterapi.com/pt-BR/v1/guides/connect-waba-instance Como criar e conectar uma instância usando a API oficial do WhatsApp Existem duas formas de conectar uma instância WABA na Zapster: pelo dashboard (Embedded Signup) ou pela API (token manual). O Embedded Signup é o método recomendado para a maioria dos usuários. ## Método 1: Embedded Signup (recomendado) O Embedded Signup é o jeito mais rápido de conectar. Você faz login na sua conta do Facebook, seleciona o número e pronto. No dashboard da Zapster, clique em **Criar instância** e selecione a opção **WhatsApp Business (Oficial)**. Tela do dashboard com a opção WhatsApp Business (Oficial) selecionada Clique no botão de conexão. Uma janela do Facebook vai abrir pedindo login. Popup do Facebook Login Escolha a conta WhatsApp Business que deseja conectar. Se você não tem uma, pode criar durante o processo. Seleção da conta WhatsApp Business Escolha o número que será conectado à instância. O número precisa estar verificado na Meta. Seleção do número de telefone Confirme a autorização. A Zapster vai configurar tudo automaticamente: webhook, credenciais, e a instância já estará pronta para uso. Instância WABA conectada no dashboard Depois de conectar, você já pode enviar mensagens pelo mesmo endpoint `POST /v1/wa/messages` que usa para instâncias não oficiais. Não precisa mudar nada na integração. ## Método 2: Token manual (avançado) Se você já tem um System User Token da Meta e prefere criar a instância via API, pode usar o endpoint de criação diretamente. ### O que você vai precisar Antes de começar, você precisa de três informações do [Meta Business Manager](https://business.facebook.com/settings). Abaixo mostramos onde encontrar cada uma. O System User Token precisa ter as permissões `whatsapp_business_management` e `whatsapp_business_messaging`. Sem elas, a criação vai falhar. #### System User Token O token é gerado na área de Usuários do sistema dentro das Configurações do Meta Business Manager. 1. No menu lateral, clique em **Usuários** e depois em **Usuários do sistema** 2. Selecione o usuário que vai se conectar (ou crie um novo clicando em **Adicionar**) 3. Clique no usuário e gere um novo token com as permissões necessárias Onde encontrar os Usuários do sistema no Meta Business Manager #### Phone Number ID O Phone Number ID é o identificador interno que a Meta usa para o seu número. Não é o número de telefone em si. 1. No menu lateral, clique em **Contas de WhatsApp** 2. Selecione a conta que contém o número desejado 3. Clique no número de telefone para abrir os detalhes. O **Phone Number ID** aparece no painel lateral direito, abaixo do nome de exibição Onde encontrar o Phone Number ID #### WABA ID O WABA ID é o identificador da conta WhatsApp Business como um todo (não do número individual). 1. No menu lateral, clique em **Contas de WhatsApp** 2. Selecione a conta desejada 3. Clique na aba **Phone Numbers** (ou **Números de telefone**). O **WABA ID** aparece no topo da página, ao lado do nome da conta Onde encontrar o WABA ID ### Criando a instância via API ```bash cURL theme={null} curl -X POST https://api.zapsterapi.com/v1/wa/instances \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "connection_type": "waba", "name": "Meu WhatsApp Oficial", "waba": { "access_token": "EAAxxxxxxx...", "phone_number_id": "1016102021584086", "waba_id": "419378847918255" } }' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.zapsterapi.com/v1/wa/instances', { method: 'POST', headers: { 'Authorization': 'Bearer SEU_TOKEN', 'Content-Type': 'application/json', }, body: JSON.stringify({ connection_type: 'waba', name: 'Meu WhatsApp Oficial', waba: { access_token: 'EAAxxxxxxx...', phone_number_id: '1016102021584086', waba_id: '419378847918255', }, }), }); const instance = await response.json(); console.log(instance); ``` Se tudo der certo, a resposta inclui a instância criada com status `connected`. A Zapster registra o webhook na Meta automaticamente. #### Usando o seu próprio app Meta (BYO-app) Se o seu System User Token pertence ao **seu próprio app Meta** (e não ao app da Zapster), os webhooks de entrada são assinados com o **App Secret do seu app**. Para que a Zapster verifique a autenticidade desses eventos, informe os campos opcionais abaixo no objeto `waba`: * `app_secret`: App Secret do seu app Meta. Quando informado, a assinatura HMAC (`X-Hub-Signature-256`) dos webhooks é validada contra ele, garantindo a verificação completa de autenticidade dos eventos. É armazenado **criptografado em repouso** (AES-256), igual ao `access_token`. * `app_id`: ID do seu app Meta. Usado apenas para identificação/auditoria — não é segredo e não participa da validação de assinatura. * `webhook_verify_token`: token de verificação do webhook. Se omitido, a Zapster gera um aleatório. Também armazenado criptografado em repouso. Sempre que o seu System User Token pertencer ao seu próprio app Meta, informe o `app_secret`. Como os webhooks são assinados com o segredo do seu app, é ele que permite à Zapster fazer a verificação completa da assinatura HMAC e garantir a autenticidade de cada evento recebido. ## Segurança dos dados Seus dados sensíveis são protegidos em todas as etapas: * **Token de acesso**: armazenado com criptografia AES-256-CBC, o mesmo padrão usado por bancos e fintechs. O token original nunca é salvo em texto puro. * **Sem exposição**: o token não aparece em logs, webhooks, respostas da API nem no dashboard. Nem a equipe da Zapster tem acesso ao valor original. * **Webhooks da Meta**: todos os eventos recebidos da Meta são validados por assinatura HMAC-SHA256 antes de serem processados. Eventos com assinatura inválida são descartados. Se você precisar trocar o token (por exemplo, se o anterior expirou), crie uma nova instância WABA. O token antigo é removido junto com a instância. ## O que muda no uso da API? Nada. Depois que a instância WABA está criada, os endpoints são os mesmos: * `POST /v1/wa/messages` para enviar mensagens * `DELETE /v1/wa/messages/:id` para cancelar agendadas * `GET /v1/wa/messages` para listar histórico A Zapster detecta automaticamente o tipo da instância e roteia para a Cloud API da Meta ou para a conexão não oficial. A única diferença é que instâncias WABA suportam **templates de mensagem** (campo `template` no body) e **não suportam envio para grupos**. ## Próximos passos * [Entenda as diferenças entre WABA e não oficial](/pt-BR/v1/guides/waba-vs-unofficial) * [Cobrança de mensagens no WhatsApp oficial (WABA)](/pt-BR/v1/concepts/message-pricing) * [Veja como enviar mensagens](/pt-BR/v1/api-reference/messages/sending) * [Configure webhooks para receber eventos](/pt-BR/v1/webhooks/setting-up-webhook) * [Erro ao criar template de autenticação (code 10, subcode 2388185)](/pt-BR/v1/guides/meta-auth-template-error) # Mensagem com botões Source: https://developer.zapsterapi.com/pt-BR/v1/guides/messages-with-buttons Aprenda como enviar mensagens com botões interativos usando a API do ZapsterAPI ## Pré-requisitos Para utilizar a funcionalidade de botões em mensagens, é necessário que: 1. **Você esteja inscrito no programa beta** - [Acesse aqui](https://app.zapsterapi.com/settings/beta-program) para se inscrever. 2. **A opção "Mensagens com botões" esteja ativa** - Vá em [Recursos em beta](https://app.zapsterapi.com/settings/beta-features) e ative o recurso. A funcionalidade de botões está disponível apenas para usuários inscritos no programa beta. Caso não tenha acesso, entre em contato com nossa equipe de suporte. **OBSERVAÇÃO:** Atualmente, ao enviar os três tipos de botões simultaneamente, o WhatsApp Web gera um erro, que também ocorre ao usar a própria API da Meta. Uma alternativa é enviar apenas os botões CALL, URL e COPYABLE juntos, e sempre enviar o botão REPLY separadamente. ## Visão Geral Os botões permitem criar mensagens interativas no WhatsApp, oferecendo aos usuários opções de resposta rápida. Você pode adicionar até 3 botões por mensagem, cada um com diferentes tipos de ação. ## Tipos de Botões Permite que o usuário responda com um texto pré-definido. ```json theme={null} { "label": "Sim, quero!", "type": "reply" } ``` Inicia uma chamada para um número de telefone específico. ```json theme={null} { "label": "Ligar Agora", "type": "call", "phone_number": "+5511999999999" } ``` Abre um link no navegador do usuário. ```json theme={null} { "label": "Ver Produto", "type": "url", "url": "https://exemplo.com/produto" } ``` Permite que o usuário copie um texto específico. ```json theme={null} { "label": "Copiar Código", "type": "copyable", "copy_code": "PROMO2024" } ``` ## Suporte por tipo de conexão O suporte a botões varia conforme o tipo de conexão da instância. Em instâncias oficiais (WABA), os botões são enviados como mensagens interativas da Cloud API, que seguem regras próprias da Meta. A assinatura da requisição é a mesma nos dois tipos de conexão. A coluna "Oficial (WABA)" abaixo descreve **mensagens de sessão**: mensagens interativas enviadas dentro da janela de conversa de 24 horas. **Templates** têm registro e aprovação separados na Meta e seguem regras próprias. Botões de ligação e de copiar código existem em templates, mesmo sem suporte em mensagem de sessão. | Recurso | Não oficial (QR code) | Oficial (WABA) | | -------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------- | | Botões `reply` | Até 3 | Até 3 | | Botão `url` | Sim | Sim, exatamente 1 por mensagem | | Botão `call` | Sim | Não em mensagem de sessão. Use um template com botão de ligação (`PHONE_NUMBER`) | | Botão `copyable` | Sim | Não em mensagem de sessão. Disponível via template de Marketing ou Authentication | | Misturar `url` com `reply` | Sim | Não | | Mais de 1 botão `url` | Sim | Não | | Mídia junto com botões | Sim | Sim. Imagem, vídeo ou documento viram o cabeçalho da mensagem interativa. Áudio não é suportado | | `caption` da mídia | Vira a legenda da mídia | Vira o corpo da mensagem interativa, com prioridade sobre `text` quando os dois são enviados. Só `text` também vira o corpo | Em instâncias WABA, combinações não suportadas retornam erro `400` imediato com os códigos `waba_feature_not_supported` ou `waba_invalid_button_combination`. Nenhum campo é descartado silenciosamente. Veja exemplos completos de envio em [Enviar mensagens com WABA](/pt-BR/v1/guides/send-waba-messages) e entenda a [cobrança de mensagens no WhatsApp oficial (WABA)](/pt-BR/v1/concepts/message-pricing). ## Modos de Botões Os modos de botões (`buttons_mode`) definem como sua mensagem será enviada no WhatsApp. Existem dois modos disponíveis: ### Modo Padrão (`auto`) * Usado para botões de resposta rápida simples * Ideal quando você quer apenas receber respostas de texto do usuário * O WhatsApp exibe os botões de forma básica ### Modo Interativo (`interactive`) * Usado para botões mais avançados como ligações, links e textos copiáveis * Permite uma experiência mais rica com diferentes tipos de ação * O WhatsApp exibe os botões com mais destaque visual ### Como o Modo é Escolhido Automaticamente Se você não especificar o `buttons_mode`, o sistema escolhe automaticamente: * **Modo Interativo**: Será usado quando pelo menos um botão for do tipo `call`, `url` ou `copyable` * **Modo Padrão**: Será usado quando todos os botões forem do tipo `reply` **Dica importante**: Mesmo com todos os botões sendo `reply`, você pode forçar o modo interativo definindo `"buttons_mode": "interactive"` para ter uma apresentação visual melhor. ### Parâmetros dos Botões Array de botões. Máximo 3 botões permitidos. Modo de exibição dos botões. Valores aceitos: `auto` ou `interactive`. ### Estrutura de um Botão Texto do botão. Máximo 20 caracteres. Tipo do botão. Valores aceitos: `reply`, `call`, `url`, `copyable`. ID único do botão. Máximo 256 caracteres. Número para botão de ligação. Deve estar no formato internacional. URL para botão de link. Deve ser uma URL válida. Texto para copiar. Aceita qualquer texto. ```bash Botões de Resposta Simples theme={null} curl -X POST https://api.zapsterapi.com/v1/wa/messages \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "X-Instance-ID: YOUR_INSTANCE_ID" \ -d '{ "recipient": "5511999999999", "text": "Gostaria de receber nossas ofertas exclusivas?", "buttons": [ { "label": "Sim, quero!", "type": "reply" }, { "label": "Não, obrigado", "type": "reply" } ], "buttons_mode": "interactive" }' ``` ```bash Botões com Diferentes Tipos theme={null} curl -X POST https://api.zapsterapi.com/messages/send \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "X-Instance-ID: YOUR_INSTANCE_ID" \ -d '{ "recipient": "5511999999999", "text": "Como posso te ajudar hoje?", "buttons": [ { "label": "Falar com Atendente", "type": "call", "phone_number": "+5511888888888" }, { "label": "Ver Catálogo", "type": "url", "url": "https://minhaloja.com/catalogo" }, { "label": "Copiar Cupom", "type": "copyable", "copy_code": "DESCONTO10" } ], "buttons_mode": "auto" }' ``` ```bash Botões com Media theme={null} curl -X POST https://api.zapsterapi.com/messages/send \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "X-Instance-ID: YOUR_INSTANCE_ID" \ -d '{ "recipient": "5511999999999", "text": "Confira nosso novo produto!", "media": { "url": "https://exemplo.com/imagem.jpg", "caption": "Produto incrível com desconto especial" }, "buttons": [ { "label": "Comprar Agora", "type": "url", "url": "https://minhaloja.com/comprar" }, { "label": "Ver Detalhes", "type": "url", "url": "https://minhaloja.com/detalhes" } ], "buttons_mode": "interactive" }' ``` # Erro ao criar template de autenticação no WhatsApp (code 10, subcode 2388185) Source: https://developer.zapsterapi.com/pt-BR/v1/guides/meta-auth-template-error Por que a Meta recusa o template de autenticação/OTP com "Esta conta do WhatsApp Business não tem permissão para criar um modelo de mensagem" (code 10, error_subcode 2388185), como diagnosticar e como liberar a categoria AUTHENTICATION. Ao criar um template de **categoria AUTHENTICATION** (OTP, o botão de "copiar código") no WhatsApp Manager ou pela Graph API, a Meta pode recusar com um erro genérico e enganoso: **"Esta conta do WhatsApp Business não tem permissão para criar um modelo de mensagem"**. Em inglês, o mesmo erro aparece como *"WhatsApp Business account doesn't have permission to create message template"* e, na API, como *"Application does not have permission for this action"* com `code: 10` e `error_subcode: 2388185`. A mensagem sugere um problema na sua conta ou no conteúdo do template, mas quase sempre não é nenhum dos dois. A categoria AUTHENTICATION tem um requisito de elegibilidade próprio que UTILITY e MARKETING não têm. Este guia mostra como confirmar o diagnóstico e como destravar a categoria. ## Qual é o erro exato? O mesmo erro aparece de duas formas, dependendo de onde você tenta criar o template. **No WhatsApp Manager (UI):** > Não é possível criar o modelo de mensagem > > Esta conta do WhatsApp Business não tem permissão para criar um modelo de mensagem. **Na Graph API** (`POST /{WABA_ID}/message_templates` com `category: "AUTHENTICATION"`): ```json theme={null} { "error": { "message": "Application does not have permission for this action", "type": "OAuthException", "code": 10, "error_subcode": 2388185, "is_transient": false, "fbtrace_id": "AbCdEf123..." } } ``` A UI apenas reexibe esse erro da API. O `is_transient: false` confirma que repetir a chamada não adianta, e o `fbtrace_id` é o identificador que a Meta pede quando você abre um ticket de suporte. Termos em inglês para busca: *whatsapp authentication template not allowed*, *code 10 authentication template*, *error\_subcode 2388185*, *can create utility but not authentication template whatsapp*. É o mesmo problema descrito aqui. ## Por que isso acontece? Porque a categoria **AUTHENTICATION é restrita** e tem um gate de elegibilidade separado. Além do que UTILITY e MARKETING já exigem, um template de autenticação só é liberado quando a conta cumpre dois requisitos: 1. **Verificação de negócio por um dos caminhos oficiais de escala** (as "scaling paths" da Meta): verificação de negócio direta (Business Verification), verificação via parceiro Meta (partner-led) ou o programa Quality Messaging. 2. **Volume/tier mínimo de mensageria.** Contas em tier inicial não conseguem enviar mensagens de autenticação. A Meta cita, como referência, algo em torno de **2.000 mensagens entregues a usuários únicos em 30 dias** (fora da janela de 24h, com templates de boa qualidade). Não existe um botão de "habilitar autenticação" nem um opt-in de termos. O caminho é verificar o negócio e escalar o tier. Desde o fim de 2025, a Meta passou a bloquear já na criação do template, em vez de bloquear só no envio. Vale reforçar o que **não** é a causa: * **Não é o conteúdo do template.** O mesmo texto, corpo e botão em outra categoria passa; só AUTHENTICATION é recusada. * **Não é um bug ou estado da sua conta específica.** O erro reproduz em contas WABA diferentes, sempre só na categoria AUTHENTICATION. * **Não é restrição do Brasil.** O gate vale para qualquer país; o `authentication_international` que aparece em alguns lugares é uma faixa de tarifação, não um bloqueio. O sinal que fecha o diagnóstico é simples: **templates de UTILITY e MARKETING são criados normalmente na mesma conta, e só o de AUTHENTICATION falha.** ## Como diagnosticar? A forma mais direta de isolar o gate é criar o mesmo template em duas categorias e comparar o retorno. Se `category: "UTILITY"` passa e `category: "AUTHENTICATION"` volta com `code: 10 / error_subcode: 2388185`, o problema é a categoria, não o conteúdo nem a conta. Os comandos abaixo falam direto com a Graph API da Meta e exigem um **token de acesso da WABA**. Se você integra pela Zapster, na maioria das contas esse token fica do nosso lado (você usa a API da Zapster sem manipular o token da WABA), então provavelmente não conseguirá rodar estes `curl` por conta própria. Nesse caso, [fale com o suporte](https://wa.me/5587999079455?text=Olá,%20quero%20verificar%20a%20elegibilidade%20da%20minha%20conta%20para%20templates%20de%20autenticação%20no%20WhatsApp) que fazemos a checagem de elegibilidade da sua conta para você. Os exemplos a seguir servem para quem tem acesso direto ao token. Este `POST` **cria o template de verdade** se a conta tiver permissão. Use apenas para diagnóstico manual pontual e apague o template de teste depois. Para uma checagem sem efeito colateral, prefira os sinais somente-leitura descritos mais abaixo. ```bash theme={null} curl -X POST "https://graph.facebook.com/v25.0/{WABA_ID}/message_templates" \ -H "Authorization: Bearer {ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "otp_test", "language": "pt_BR", "category": "AUTHENTICATION", "message_send_ttl_seconds": 60, "components": [ { "type": "BODY", "add_security_recommendation": true }, { "type": "FOOTER", "code_expiration_minutes": 5 }, { "type": "BUTTONS", "buttons": [{ "type": "OTP", "otp_type": "COPY_CODE" }] } ] }' ``` Troque `category` para `"UTILITY"` (com um corpo simples, sem botão OTP) e rode de novo. Se UTILITY cria e AUTHENTICATION devolve `2388185`, o gate está confirmado. ### Como checar a elegibilidade sem criar nada? Nenhum endpoint devolve um booleano definitivo de "pode criar autenticação", mas há sinais somente-leitura que funcionam como proxy forte: * **Tier de mensagens (o sinal mais limpo).** É a mesma escada que a UI da Meta mostra: **250 → 2.000 → 10.000 → 100.000 → ilimitado**. Tier 250 (inicial) costuma significar autenticação bloqueada; a partir de 2.000 a conta muito provavelmente já é elegível. Não existe um tier de "1.000" (esse número, comum em blogs de parceiros, está desatualizado). * **`health_status`.** `GET /{PHONE_NUMBER_ID}?fields=health_status` retorna `can_send_message` por entidade (`PHONE_NUMBER`, `WABA`, `BUSINESS`, `APP`) como `AVAILABLE`, `LIMITED` ou `BLOCKED`. Um `BUSINESS` em `LIMITED` ou `BLOCKED` costuma indicar verificação de negócio pendente. * **Templates de autenticação já existentes.** `GET /{WABA_ID}/message_templates?category=AUTHENTICATION&fields=name,status`. Se já existe algum, a conta consegue criar. A lista vazia não prova o contrário, então só vale como sinal quando há resultado. ## Como resolver? O caminho é destravar a categoria AUTHENTICATION cumprindo os requisitos oficiais da Meta. Não há atalho de configuração. 1. **Conclua a verificação de negócio** por um dos três caminhos de escala: Business Verification direta, verificação via parceiro Meta ou o programa Quality Messaging. Sem verificação de negócio, a autenticação continua bloqueada. 2. **Suba o tier de mensagens.** O gate real é o volume. Enquanto a conta estiver no tier inicial (250), a categoria tende a permanecer bloqueada. A referência da Meta gira em torno de 2.000 mensagens entregues a usuários únicos em 30 dias, com boa qualidade e fora da janela de 24h. 3. **Aguarde a liberação automática.** Quando a conta se torna elegível, a categoria costuma liberar em cerca de **6 horas**, sem nenhuma ação manual. Prazos e limites são definidos pela Meta e podem mudar. Este guia descreve o comportamento observado, não uma garantia da Meta. Se você já concluiu a verificação de negócio, está em um tier acima do inicial e o erro **persiste**, aí sim vale abrir um ticket com o suporte da Meta. Inclua o `error_subcode: 2388185` e o `fbtrace_id` da resposta da API, que é o que agiliza a análise do lado deles. Enquanto a categoria AUTHENTICATION não libera, um stopgap comum é enviar o OTP por outro canal (SMS ou e-mail) e migrar para o template de autenticação quando a conta ficar elegível. ## O que NÃO resolve o erro? Duas tentativas parecem óbvias e não funcionam: * **Editar o conteúdo do template.** Como o bloqueio é de categoria, e não de conteúdo, mudar o texto, o corpo ou o botão não muda o resultado. O erro `2388185` continua. * **Entregar o OTP como template UTILITY com botão de copiar código.** É proibido e tende a ser rejeitado. O botão `OTP` (copy-code / one-tap) é exclusivo de AUTHENTICATION. O botão `COPY_CODE` que existe em UTILITY e MARKETING é o de **cupom** (copiar código de desconto), não de OTP, e a Meta recusa a combinação com `code: 100 / error_subcode: 2388180`. A própria documentação de categorização é explícita: *"Only authentication templates can be used to send a one-time passcode for identity verification. Marketing and utility templates cannot be used for this purpose."* Mesmo colocando o código inline no corpo, sem botão, o classificador de conteúdo da Meta reconhece o padrão de OTP e marca o template como `REJECTED`. Nenhum ajuste de texto contorna isso. O único caminho definitivo é habilitar a categoria AUTHENTICATION. ## Como a Zapster ajuda? A Zapster traduz esse erro opaco da Meta. Na tela de gestão de templates, o `code: 10 / error_subcode: 2388185` vira uma mensagem clara: explica que templates de autenticação exigem verificação de negócio concluída e volume mínimo de mensagens, e que UTILITY e MARKETING não têm essa exigência. Você entende o motivo real sem precisar decifrar o retorno cru da API. A liberação da categoria depende inteiramente da Meta (verificação de negócio e tier), então nem a Zapster nem qualquer outra plataforma consegue garantir aprovação ou prazo. O que fazemos é deixar o diagnóstico claro e apoiar você no processo, inclusive verificando a elegibilidade da sua conta quando você não tem acesso direto ao token da WABA. Se estiver travado nesse erro, [fale com o suporte](https://wa.me/5587999079455?text=Olá,%20estou%20com%20o%20erro%20de%20template%20de%20autenticação%20\(subcode%202388185\)%20no%20WhatsApp). ## Perguntas frequentes Porque só a categoria AUTHENTICATION tem gate de elegibilidade próprio (verificação de negócio e tier de mensagens). UTILITY e MARKETING não têm essa exigência, então criam normalmente na mesma conta. É o sinal clássico de que o bloqueio é de categoria, não de conteúdo nem da conta. É o código específico da Graph API para "a conta não tem permissão para criar template de autenticação". Vem junto com `code: 10` e `type: OAuthException`. Na UI, o mesmo erro aparece como "Esta conta do WhatsApp Business não tem permissão para criar um modelo de mensagem". Não. O gate da categoria AUTHENTICATION vale para qualquer país. O `authentication_international` que aparece em alguns painéis é uma faixa de tarifação, não um bloqueio de criação. Não. O bloqueio é de categoria, não de conteúdo. Mudar texto, corpo ou botão mantém o mesmo `error_subcode: 2388185`. Não. O botão OTP é exclusivo de AUTHENTICATION, e a documentação da Meta proíbe usar templates de utilidade ou marketing para enviar código de verificação. Mesmo com o código inline no corpo, o template acaba rejeitado. O único caminho é habilitar a categoria AUTHENTICATION. Quando a conta cumpre a verificação de negócio e sobe o tier, a categoria costuma liberar em cerca de 6 horas, de forma automática. Prazos são definidos pela Meta e podem mudar. Cheque o tier de mensagens (250 → 2.000 → 10.000 → 100.000 → ilimitado): no tier inicial a autenticação tende a permanecer bloqueada. Se a verificação está concluída e o tier já subiu, abra um ticket com o suporte da Meta informando o `error_subcode: 2388185` e o `fbtrace_id` da resposta. ## Fontes oficiais da Meta * [Limites de mensagens (messaging limits)](https://developers.facebook.com/docs/whatsapp/messaging-limits), a escada de tiers que funciona como gate real. * [Categorização de templates (template categorization)](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/template-categorization), com a regra de que só templates de autenticação enviam OTP. * [Thread da comunidade Meta sobre o erro 2388185](https://developers.facebook.com/community/threads/1372007877817645/), com vários relatos do mesmo `code: 10 / error_subcode: 2388185`. ## Próximos passos * [Conectando uma instância WABA](/pt-BR/v1/guides/connect-waba-instance) * [Enviando mensagens WABA](/pt-BR/v1/guides/send-waba-messages) * [Número 555 da Meta e o erro 131037](/pt-BR/v1/numero-555-meta)