whatsapp-bsp / api / v1 / docs
Voltar ao Dashboard

Documentação da API

Bem-vindo à API CoverCut. Nossa API permite integrar o WhatsApp Business Cloud diretamente no seu sistema.

Requisitos no Facebook

Antes de conectar à API oficial do WhatsApp (Meta), é necessário completar algumas configurações na sua conta do Facebook Business. Siga os passos abaixo na ordem indicada.

Antes de começar: Verifique se não há nenhuma restrição na sua conta do Business Manager. Uma conta restrita impede a conexão e o envio de mensagens até que a pendência seja resolvida diretamente com a Meta.

business.facebook.com/business-support-home

Passo 1 — Verificação da Empresa

Acesse a Central de Segurança do Business Manager e inicie o processo de verificação da sua empresa. Isso é fundamental para aumentar seus limites de envio de mensagens.

1
Inicie a Verificação na Central de Segurança

Clique no link abaixo para acessar a Central de Segurança da sua conta.

business.facebook.com/latest/settings/security_center

Passo 2 — Preencher Dados da Empresa

Acesse as configurações do Business Manager e preencha as informações da sua empresa. Isso é obrigatório para a Meta liberar o envio de mensagens em escala.

2
Acesse o link abaixo e preencha todos os campos solicitados

Razão social, CNPJ, endereço e telefone comercial.

business.facebook.com/latest/settings/business_info
Conta Pessoal (sem CNPJ)
Se você não possui empresa registrada, preencha o seu nome completo no campo "Razão social da empresa". Os demais campos podem ser preenchidos com seus dados pessoais normalmente.

Passo 3 — Cadastrar Cartão de Crédito

O envio de templates (mensagens ativas) é cobrado diretamente pela Meta. Para habilitar o envio, é necessário cadastrar um método de pagamento na sua conta Business.

3
Acesse o Hub de Faturamento

Clique no link abaixo para ir direto para a página de pagamentos.

business.facebook.com/latest/billing_hub/accounts
2
Selecione "Contas do WhatsApp Business"

Na tela de faturamento, localize e clique na opção Contas do WhatsApp Business para adicionar seu cartão de crédito vinculado especificamente ao número do WhatsApp que será utilizado na API.

Importante: Sem cartão cadastrado, o envio de templates de marketing e utilidade ficará bloqueado pela Meta. Mensagens dentro da janela de 24h (respostas) não são cobradas.

Custos de Envio de Templates

A Meta cobra por conversa iniciada via template, e não por mensagem individual. Os preços variam de acordo com a categoria do template:

Marketing
$0,06
por conversa iniciada
Templates promocionais, ofertas, campanhas
Utilidade
$0,006
por conversa iniciada
Confirmações, lembretes, notificações transacionais
Dica: Prefira templates de Utilidade para notificações automáticas (agendamentos, cobranças, confirmações). Além de custar 10x menos, eles têm taxas de entrega mais altas pois não são filtrados como spam.

Janelas de Conversa

A Meta trabalha com o conceito de janela de conversa: um período em que você pode trocar mensagens livremente sem custo adicional após o template inicial ter sido enviado.

24h
Conversa Padrão

Quando um cliente envia uma mensagem para o seu número, uma janela de 24 horas é aberta. Durante esse período, você pode responder livremente com qualquer tipo de mensagem, sem custo de template.

72h
Lead de Anúncios (Click-to-WhatsApp)

Quando o lead chega ao WhatsApp a partir de um anúncio Click-to-WhatsApp do Facebook/Instagram, a janela de conversa é ampliada para 72 horas, permitindo mais tempo para o atendimento sem custo adicional.

Fora da janela: Se a janela expirar e você precisar retomar o contato, será necessário enviar um template (marketing ou utilidade) para reabrir a conversa, e o custo correspondente será cobrado.

Nome de Exibição (Display Name)

Quando sua empresa utiliza a API Oficial do WhatsApp Business (Meta) para falar com clientes, o nome que aparece no perfil é o seu Nome de Exibição (ou Display Name). Ele funciona como a identidade e o cartão de visitas da sua empresa no aplicativo.

Ter um nome aprovado é essencial para transmitir confiança, ajudar no reconhecimento imediato da marca e, futuramente, para a obtenção do selo verde de Conta Comercial Oficial (OBA).

1. Diretrizes de Aprovação da Meta

A Meta possui regras estritas para a aprovação do Nome de Exibição. Ele deve refletir de forma consistente a sua marca externa e seguir as regras de formatação abaixo:

Regras de Formatação e Escrita

  • Tamanho Mínimo: O nome deve conter no mínimo 3 caracteres.
  • Gramática e Capitalização: Use maiúsculas e minúsculas corretamente (ex: Padaria Delícia). Não utilize todo o texto em caixa alta (ALL CAPS) (ex: PADARIA DELÍCIA), a menos que a sua marca seja registrada ou conhecida dessa forma (ex: IKEA, BMW).
  • Espaçamento: Não insira espaços extras entre as letras (ex: P a d a r i a será rejeitado).
  • Caracteres Especiais: Emojis, símbolos de marca registrada (como ®, ™), caracteres especiais desnecessários ou pontuação excessiva são estritamente proibidos (ex: Padaria Delícia! ✨).

Regras de Conteúdo e Associação

  • Sem Termos Genéricos: Nomes compostos apenas por palavras comuns como Moda, Roupas, Suporte ou apenas localizações como São Paulo, Brasil serão reprovados. O nome deve conter um identificador único da sua empresa (ex: Moda Estilo, Padaria Delícia São Paulo).
  • Consistência de Branding: O nome deve corresponder exatamente a como a empresa é conhecida no mercado (no site institucional, redes sociais, logotipo e documentos fiscais/CNPJ). A Meta confere essas informações durante a análise.
  • Associação com a Conta (WABA): Se o nome de exibição for de uma sub-marca ou departamento específico, a relação com a empresa principal deve ser clara (ex: Atendimento - Padaria Delícia).
  • Proibição de Nomes Individuais: Não use o nome completo de uma pessoa física sozinho (ex: João Silva será rejeitado, use João Silva Advocacia ou Consultório Dr. João Silva).
  • Termos Oficiais/Verificados: Não use palavras como "Oficial", "Verificado", "Official" ou "Verified" no nome, a menos que a conta já possua o selo de verificação oficial da Meta.
  • Sem Menção à Meta/WhatsApp: Não inclua os termos WhatsApp, Meta, Facebook ou Instagram no nome (ex: Suporte WhatsApp da Minha Empresa é proibido).
  • Evite URLs Completas: Não use formatos de sites com www. ou e-mails (ex: www.padariadelicia.com é rejeitado, mas PadariaDelicia.com é aceito se for o nome comercial da marca).
  • Proibido Promoções: O nome não pode conter mensagens promocionais, cupons ou frases de efeito (ex: Padaria Delícia - 20% OFF ou A Melhor Padaria da Cidade serão rejeitados).
Importante: O WhatsApp verifica ativamente a consistência do nome. Se a sua empresa se chama "Padaria Delícia", mas o site cadastrado ou as redes sociais utilizam outro nome sem qualquer ligação óbvia, o nome será rejeitado até que a associação seja comprovada ou o nome ajustado.

2. Exemplos de Nomes Aceitos vs. Rejeitados

O Que Pode (Aprovado) O Que Não Pode (Rejeitado) Motivo da Rejeição
Padaria Delícia PADARIA DELÍCIA Uso incorreto de caixa alta (ALL CAPS).
Loja Estilo São Paulo São Paulo Nome geográfico genérico.
Informática Silva Computadores Nome comercial genérico.
Atendimento - Padaria Delícia Suporte Termo de departamento sem o nome da marca.
PadariaDelicia.com www.padariadelicia.com Formato de URL completa (evitar phishing).
Padaria Delícia Padaria Delícia - Promoção Contém texto promocional no nome.
Minha Empresa Minha Empresa no WhatsApp Uso não autorizado de marcas registradas da Meta.
Clínica de Estética Bella Estética Bella Oficial Sugere verificação oficial sem possuir o selo.
Ana Souza Nutrição Ana Souza Nome de pessoa física sem identificador comercial.

3. O Que Fazer se Seu Nome For Rejeitado?

Se o seu Nome de Exibição for rejeitado, o processo de ajuste e reanálise é rápido e simples:

1
Revisar e Ajustar

Edite o nome no Painel do WhatsApp Manager na Meta de acordo com as diretrizes acima.

2
Comprovar Presença Digital (Branding)

Certifique-se de que o nome comercial desejado está claramente visível no site institucional e nas redes sociais da empresa vinculadas ao portfólio de negócios.

3
Enviar para Nova Análise

Submeta o novo nome. O WhatsApp costuma analisar novas solicitações dentro de um prazo que varia de **alguns minutos a até 24 horas**.

Boas Práticas & Prevenção de Banimento (2026)

Em 2026, a Meta intensificou significativamente as políticas de segurança e a inteligência dos seus algoritmos para proteger a experiência dos usuários no WhatsApp. Este guia apresenta os principais fatores que determinam a saúde do seu número e as diretrizes recomendadas para manter sua operação segura na API Oficial e no Modo de Coexistência.

1. O Cenário de Banimentos no WhatsApp

Os algoritmos de detecção da Meta evoluíram. Hoje, o foco de monitoramento está predominantemente no comportamento das contas comerciais e na qualidade das interações, e não puramente no volume bruto de mensagens enviadas.

Principais Fatores que Levam ao Banimento

  • Falta de Consentimento (Opt-in): Enviar mensagens ativas para contatos que não autorizaram a comunicação é o gatilho mais rápido para restrições. Listas frias ou bases compradas resultam em bloqueios quase instantâneos.
  • Comportamento Automatizado Não Oficial (Pirata): A utilização de ferramentas piratas, extensões de navegadores e APIs não oficiais que simulam interações humanas de forma fria é facilmente detectada pelos ritmos e padrões previsíveis dos envios.
  • Disparos em Massa Abruptos: Iniciar volumes elevados de mensagens repentinamente, sem aquecimento prévio ou respeitando os limites dos Messaging Tiers da Meta.
  • Alta Taxa de Denúncias (O Fator Mais Crítico): O botão "Denunciar Spam" ou "Bloquear" clicado pelos clientes finais é o fator com maior peso. Se o Quality Rating (Índice de Qualidade) do número cair para a cor vermelha (Low Quality), o número sofrerá sanções imediatas.
Mitos Comuns: O que NÃO causa banimento
A Meta não penaliza contas legítimas por:
  • Receber um alto volume de mensagens recebidas (Inbound).
  • Responder a milhares de clientes simultaneamente de forma legítima e contextual.
  • Apresentar crescimento orgânico de interações ou usar o WhatsApp Business App original manualmente.

2. O Uso Manual como "Trust Signal"

Ao contrário das ferramentas piratas, o uso manual e autêntico do WhatsApp é considerado o maior sinal de confiança (Trust Signal) para os algoritmos de segurança da Meta.

Por que o atendimento manual protege o seu número?
  • Humanização Natural: Padrões de digitação, pausas naturais, tempo de resposta variável e uso de vocabulários diversificados geram um comportamento 100% orgânico e seguro contra os robôs anti-spam.
  • Redução Drástica de Denúncias: O atendimento prestado por um agente humano tende a ser muito mais assertivo, resolvendo o problema do cliente de imediato, o que diminui a probabilidade dele clicar em "Denunciar Spam".
  • Frequência de Manutenção: A única exigência operacional do modo de coexistência é abrir o WhatsApp Business App no celular pelo menos uma vez a cada 13 dias para manter a chave da API ativa. O uso contínuo manual é benéfico e saudável para a conta.

3. Limites e Quantidades de Envio

Existe uma diferença conceitual e prática fundamental entre iniciar conversas através do WhatsApp Business App (celular) e através da Cloud API (automação):

Característica Pelo App de Celular (Manual) Pela Cloud API (Automação)
Necessidade de Templates Não (Mensagem livre) Sim (Obrigatoriamente aprovado pela Meta)
Recomendação de Limites 20 a 50 contatos frios/dia (sem histórico ou número salvo na agenda). Muito maior se os contatos forem clientes fidelizados. Controlado estritamente por Messaging Tiers.
Messaging Tiers (Volume) Não aplicável. Tier 0: 250 destinatários únicos/dia (Não verificado)
Tier 1: 1.000 destinatários únicos/dia (Verificado)
Tier 2: 10.000 destinatários únicos/dia
Tier 3: 100.000 destinatários únicos/dia
Janela de Conversa Livre Sempre livre. Livre por 24h após a última mensagem recebida do cliente.
Regra de Ouro dos Tiers: Para aumentar seu Tier na API (de 1.000 para 10.000, por exemplo), você deve enviar pelo menos a metade do seu limite diário atual em mensagens com alta qualidade em um período de 7 dias. O upgrade é automático pela Meta.

4. Boas Práticas Operacionais

No Modo de Coexistência (App + API)

  • Intervenção Humana Transparente: Utilize o aplicativo móvel para assumir conversas complexas em que o robô não está conseguindo guiar o cliente de forma assertiva.
  • Sincronização de Estado: Assegure-se de pausar a automação do robô quando a conversa estiver sob atendimento manual para evitar o envio de respostas simultâneas e incoerentes.
  • Manutenção de Conexão: Abra o aplicativo móvel pelo menos uma vez a cada 13 dias para evitar que a Meta desvincule a Cloud API por inatividade do app companheiro.
  • Conexões Suportadas: Lembre-se de que ao ativar a Coexistência, conexões antigas do WhatsApp Web ou aplicativos para desktop do Windows serão desconectados, pois o sistema aceita apenas dispositivos companheiros compatíveis com o espelhamento de eventos.
  • Desconexão do Número: Para desconectar um número em modo de coexistência da plataforma BSP e liberar a exclusão no Facebook Business Manager, o processo correto deve ser feito pelo app no celular — chamadas de API (deregister, subscribed_apps) não funcionam para esse tipo de conexão. Veja o passo a passo abaixo.
Como desconectar um número em Coexistência da plataforma BSP:
  1. Primeiro remova o número pela plataforma (botão Desconectar no dashboard)
  2. No celular, abra o WhatsApp Business
  3. Acesse Configurações → Conta → Plataforma Business
  4. Toque em Desconectar

Somente após esse passo a lixeira no Facebook Business Manager será liberada para excluir o número. Tentativas de desconectar via API (DELETE /subscribed_apps ou POST /deregister) retornam erro pois o vínculo de coexistência é gerenciado pelo app do celular, não pela Cloud API.

Na API Oficial Direta (Alta Performance)

  • Implemente o Double Opt-in: Sempre confirme se o usuário realmente deseja receber comunicações automáticas da sua marca antes de iniciar os fluxos transacionais ou promocionais.
  • Facilite o Opt-out (Cancelamento): Sempre dê a opção para o cliente parar de receber mensagens (ex: um botão de resposta escrito "Sair" ou "Cancelar"). É muito melhor o cliente sair da sua lista voluntariamente do que ele clicar no botão "Denunciar Spam" da Meta.
  • Aquecimento de Números Novos (Warm-up): Nunca inicie disparando 1.000 mensagens no primeiro dia de um número novo. Comece com 50 mensagens para clientes altamente engajados (que com certeza irão interagir), e dobre o volume a cada 2 dias se o Quality Rating se mantiver verde.
  • Preserve o Propósito do Template: Nunca mude a intenção de um template aprovado. Injetar links promocionais ou propagandas dentro de um template aprovado para "Atualizações Transacionais" causará a suspensão permanente do seu template e prejudicará o número.
  • Evite Loops Infinitos de Automação: Garanta que o fluxo do robô sempre possibilite o transbordo para atendimento humano ou a saída do menu para não frustrar o usuário.

Autenticação

Todas as requisições devem incluir seus tokens de acesso nos cabeçalhos HTTP. Você pode gerar esses tokens em seu dashboard.

Headers Requeridos
X-API-Key: sua_chave_de_api
X-API-Secret: seu_segredo_de_api

Início Rápido

Base URL

Endpoint Principal
https://api.covercut.com.br/api/v1

Webhooks

Nossos webhooks notificam seu sistema em tempo real sobre novas mensagens e mudanças de status.

Configuração

Acesse Configurações > Webhook no dashboard e informe a URL onde deseja receber os dados.

Configurar via API

Você também pode definir a URL de webhook por número diretamente pela API, sem acessar o painel — ideal para automatizar o provisionamento de números na sua aplicação ou em fluxos SaaS. A configuração é aplicada ao número identificado pelo phone_number_id no campo from.

Definir / atualizar o webhook

POST POST /api/v1/numbers/webhook
POST https://api.covercut.com.br/api/v1/numbers/webhook
Headers:
  X-API-Key: sua_chave
  X-API-Secret: seu_segredo
  Content-Type: application/json

Body:
{
  "from": "123456789012345",              // phone_number_id do número
  "webhook_url": "https://seu-sistema.com/webhook",
  "enabled": true,                         // opcional (default: true)
  "ignore_groups": false,                  // opcional
  "echo_enabled": false,                   // opcional (notificar msgs enviadas)
  "mirror_url": "https://backup.seu.com/hook", // opcional (webhook espelho)
  "mirror_enabled": false                  // opcional
}
Exemplo de Resposta — 200 OK
{
  "success": true,
  "message": "Webhook configurado com sucesso.",
  "webhook": {
    "phone_number_id": "123456789012345",
    "display_phone_number": "5547999999999",
    "webhook_url": "https://seu-sistema.com/webhook",
    "webhook_secret": "a1b2c3d4e5f6...",   // use para validar X-BSP-Signature
    "webhook_enabled": true,
    "ignore_groups": false,
    "echo_enabled": false,
    "mirror_url": null,
    "mirror_secret": null,
    "mirror_enabled": false,
    "using_fallback": false
  }
}

Consultar a configuração atual

GET GET /api/v1/numbers/webhook?from={phone_number_id}
GET https://api.covercut.com.br/api/v1/numbers/webhook?from=123456789012345
Headers:
  X-API-Key: sua_chave
  X-API-Secret: seu_segredo

Remover o webhook (voltar ao fallback geral)

Envie webhook_url vazio ou {"action":"delete"}. O número volta a usar a Configuração Geral da conta.

POST https://api.covercut.com.br/api/v1/numbers/webhook
{
  "from": "123456789012345",
  "action": "delete"
}

Campos

CampoTipoDescrição
fromstringObrigatório. O phone_number_id do número (veja em /api/v1/numbers/list).
webhook_urlstringURL de destino. Obrigatório, exceto ao remover.
enabledboolAtiva/desativa o envio. Default true.
ignore_groupsboolDescarta mensagens de grupos.
echo_enabledboolNotifica mensagens enviadas pela API (echo).
mirror_urlstringWebhook espelho — recebe o mesmo payload em paralelo.
mirror_enabledboolAtiva o webhook espelho.
actionstringUse "delete" para remover a configuração do número.
Nota: o webhook_secret é gerado automaticamente na primeira configuração e preservado nas atualizações seguintes — a resposta sempre traz o valor atual para você validar a assinatura X-BSP-Signature. Campos omitidos mantêm o valor atual do número.

Validar Assinatura (Segurança)

Cada envio contém o header X-BSP-Signature. Use-o para garantir que a mensagem veio de nós.

PHP — Validação de Assinatura
$signature = hash_hmac('sha256', $payload_bruto, $webhook_secret);
if (hash_equals($_SERVER['HTTP_X_BSP_SIGNATURE'], $signature)) {
    // Autêntico
}

Eventos de Mensagem

Exemplo de Payload — Nova Mensagem
{
  "event": "message",
  "direction": "inbound",
  "contact": {
    "wa_id": "5547999999999",
    "user_id": "BR.13491208655302741918",
    "name": "João Silva"
  },
  "message": {
    "id": "wamid...",
    "type": "text",
    "text": "Oi!"
  }
}
Exemplo Completo (Headers + Body)
{
  "headers": {
    "content-type": "application/json",
    "x-bsp-signature": "28a2a4200aaa6c457bfecca27d97f6a0b1dfef32f1bfa64b6aa8bfff2de4c429",
    "x-bsp-timestamp": "1778884885"
  },
  "body": {
    "event": "message",
    "from_number_id": "950147584848138",
    "contact": {
      "wa_id": "554799999999",
      "name": "Sandro"
    },
    "message": {
      "id": "wamid.HBgMNTU0N...",
      "type": "text",
      "text": "Ola"
    }
  }
}

Mensagens Enviadas (Echo) — Quem respondeu?

Toda mensagem enviada pelo seu número é repassada ao seu webhook como um evento de echo (event: "echo", direction: "outbound"). Isso permite acompanhar respostas independente de onde foram originadas.

Use o campo echo_source para saber a origem:

echo_sourceOrigem
apiEnviada pela Cloud API — inclui respostas digitadas por um atendente humano no Chatwoot e mensagens enviadas pela sua integração/IA via /api/v1/messages/send.
phoneEnviada diretamente do celular / app WhatsApp Business (modo Coexistência).

Para distinguir humano vs. IA dentro de echo_source: "api", use o campo opcional message.agent_name:

  • Respostas de um atendente no Chatwoot carregam o nome do atendente em agent_name.
  • Sua IA/integração define o próprio agent_name ao enviar (campo opcional no corpo de /messages/send) — ex.: "agent_name": "IA".
  • Convenção sugerida: trate nomes de pessoas como humano e o identificador da sua IA como automático.
Exemplo — Echo de resposta de um atendente
{
  "event": "echo",
  "direction": "outbound",
  "echo_source": "api",
  "from_number_id": "950147584848138",
  "contact": { "wa_id": "5547999999999", "name": "João Silva" },
  "message": {
    "id": "wamid...",
    "type": "text",
    "text": "Olá! Já vou te ajudar.",
    "agent_name": "Maria Atendente"
  }
}
Nota: agent_name é aditivo — só aparece quando há um nome associado ao envio. Mensagens antigas ou envios sem nome não trazem o campo.

Mensagens Não Suportadas (type: "unsupported")

A Meta envia type: "unsupported" quando o contato manda algo que a Cloud API não consegue entregar/renderizar (enquetes, mensagens de pagamento/pedido, recursos novos do app etc.). O conteúdo dessas mensagens não é recuperável via API — a mensagem real existe apenas no WhatsApp.

Quando disponível, o motivo vem no array message.errors (tipicamente código 131051):

Exemplo — Mensagem não suportada
{
  "event": "message",
  "direction": "inbound",
  "contact": { "wa_id": "5547999999999", "name": "João Silva" },
  "message": {
    "id": "wamid...",
    "type": "unsupported",
    "unsupported": { "type": "unknown" },
    "errors": [
      { "code": 131051, "title": "Message type is not currently supported" }
    ]
  }
}

Validar no n8n (Sem Código)

A forma mais simples de validar a segurança no n8n é:

  • Adicione um nó Crypto (Action: Hmac).
  • No campo Value, use: {{ JSON.stringify($json.body) }}.
  • No campo Secret, use seu Webhook Secret do Dashboard.
  • No campo Algorithm, use sha256.
  • Compare o resultado com o header x-bsp-signature.

Sincronização e Coexistência

Quando seu cliente conecta o número mantendo o aplicativo **WhatsApp Business** móvel ativo (Modo Coexistence), a Meta disponibiliza a sincronização do histórico das conversas e contatos salvos no celular. Nossa API recebe esses pacotes e os repassa de forma estruturada para o seu webhook.

Importante: Processamento Assíncrono

O volume de dados enviado no histórico de conversas dos últimos 6 meses pode ser extremamente grande (vários megabytes divididos em pacotes). É fundamental que o seu webhook processe esse evento de forma assíncrona (colocando em uma fila de background) e retorne imediatamente um status 200 OK para evitar que a API sofra timeout.

1. Webhook de Histórico de Conversas (history)

Este webhook é disparado automaticamente pela Meta logo após a conexão inicial do número no modo Coexistence, trazendo todo o histórico de conversas e mensagens dos últimos 6 meses segmentado por threads (conversas de cada contato).

Payload do Webhook — Evento history
{
  "event": "history",
  "from_number_id": "123456789012345",
  "history": {
    "metadata": {
      "phase": 1,
      "chunk_order": 1,
      "progress": 100
    },
    "threads": [
      {
        "id": "5547999999999",
        "context": {
          "wa_id": "5547999999999",
          "username": "sandro_dev",
          "user_id": "US.ENT.11815799212886844830"
        },
        "messages": [
          {
            "from": "5547999999999",
            "to": "5547888888888",
            "id": "wamid.HBgLNTUxMTk5OTk5...",
            "timestamp": "1715802120",
            "type": "text",
            "text": {
              "body": "Olá! Gostaria de saber mais sobre o plano SaaS."
            },
            "history_context": {
              "status": "read"
            }
          },
          {
            "from": "5547888888888",
            "to": "5547999999999",
            "id": "wamid.HBgLNTUxMTk5OTk5X2VjaG8...",
            "timestamp": "1715802180",
            "type": "text",
            "text": {
              "body": "Com certeza! Segue nosso catálogo de recursos."
            },
            "history_context": {
              "status": "sent"
            }
          }
        ]
      }
    ]
  }
}

Campos de Controle e Paginação do Histórico

CampoTipoDescrição
metadata.phaseinteger1: Mensagens mais recentes (entrega rápida);
2: Histórico profundo dos últimos 6 meses.
metadata.chunk_orderintegerOrdenação do pacote recebido (1, 2, 3...). Útil para rastrear a ordem de importação.
metadata.progressintegerPercentual de progresso da sincronização de 0 a 100%.
threads[].idstringIdentificador do contato dono do chat (Telefone ou BSUID).
threads[].contextobjectIdentificadores de privacidade adicionais da Meta (introduzidos em 2026).
messages[].history_context.statusstringO último status conhecido daquela mensagem histórica: sent, delivered, read, ou failed.

2. Webhook de Contatos do Celular (smb_app_state_sync)

Recebido após a sincronização inicial, repassando a agenda telefônica completa do aplicativo WhatsApp Business móvel do cliente.

Payload do Webhook — Evento smb_app_state_sync
{
  "event": "smb_app_state_sync",
  "from_number_id": "123456789012345",
  "smb_app_state_sync": {
    "contacts": [
      {
        "full_name": "Sandro Dev",
        "first_name": "Sandro",
        "phone_number": "5547999999999",
        "username": "sandro_dev",
        "user_id": "US.ENT.11815799212886844830"
      },
      {
        "full_name": "Maria Design",
        "first_name": "Maria",
        "phone_number": "5547988888888",
        "username": "maria_dsgn",
        "user_id": "US.ENT.99815799212886844112"
      }
    ]
  }
}

3. Eventos de Conexão e Offboarding (account_update)

Recebido quando um cliente do modelo Coexistence desinstala, limpa os dados ou re-registra o aplicativo do WhatsApp Business no celular. Esses eventos são cruciais para pausar campanhas de mensagens quando a Cloud API cai e retomá-las automaticamente quando ela volta.

Payload do Webhook — Eventos de account_update
{
  "event": "account_update",
  "timestamp": "2026-05-20T10:00:00-03:00",
  "from_number_id": "123456789012345",
  "account_update": {
    "phone_number": "5547999999999",
    "event": "ACCOUNT_OFFBOARDED" // Pode ser ACCOUNT_OFFBOARDED ou ACCOUNT_RECONNECTED
  }
}

Como Forçar a Sincronização via API

Se você desenvolve um SaaS e deseja forçar o envio imediato desses webhooks pela Meta (útil para acelerar a sincronização logo após a conexão do cliente no seu painel), faça uma requisição para o nosso endpoint dedicado:

POST POST /api/v1/numbers/sync_coexistence
POST https://api.covercut.com.br/api/v1/numbers/sync_coexistence
Headers:
  X-API-Key: sua_chave
  X-API-Secret: seu_segredo
  Content-Type: application/json

Body:
{
  "from": "123456789012345" // phone_number_id dono do número
}
Exemplo de Resposta — 200 OK
{
  "success": true,
  "message": "Sincronização iniciada com sucesso. Os dados serão transmitidos assincronamente em pacotes via Webhook.",
  "results": {
    "contacts": "Iniciado (contacts_sync_req_01)",
    "history": "Iniciado (history_sync_req_02)"
  }
}

4. Webhook de Status com Precificação (v24.0+)

Com as novas regras da Meta (Maio/2026), o webhook de status agora inclui detalhes sobre a cobrança de conversas individuais e de grupos (modelo PMP — Per-Message Pricing).

Payload do Webhook — Evento status
{
  "event": "status",
  "timestamp": "2026-05-19T14:15:00-03:00",
  "from_number_id": "123456789012345",
  "status": {
    "id": "wamid.HBgLNTUxM...",
    "status": "delivered",
    "recipient": "5547999999999",
    "pricing": {
      "billable": true,
      "pricing_model": "PMP",
      "type": "regular",
      "category": "group_marketing"
    }
  }
}

Os campos de precificação são mapeados da seguinte forma:

  • pricing.pricing_model: O modelo de cobrança. Para grupos, sempre retornará PMP (Per-Message Pricing).
  • pricing.type: Pode ser regular (cobrado) ou free_group_customer_service (grátis dentro da janela de serviço do grupo).
  • pricing.category: Categoria da mensagem. Exemplo: group_marketing, group_utility, ou group_service.

5. Webhooks da API de Grupos

Para gerenciar fluxos de grupos, você receberá notificações estruturadas de ciclo de vida, participantes e configurações.

Evento: group_lifecycle_update

Disparado ao criar ou deletar grupos. Contém o link de convite na criação.

{
  "event": "group_lifecycle_update",
  "timestamp": "2026-05-19T14:16:00-03:00",
  "from_number_id": "123456789012345",
  "from_number": "5547999999999",
  "data": {
    "event": "create",
    "group_id": "120363214587@g.us",
    "invite_link": "https://chat.whatsapp.com/L1M2N3O4P5Q6"
  }
}

Evento: group_participants_update

Disparado quando participantes entram ou saem do grupo.

{
  "event": "group_participants_update",
  "timestamp": "2026-05-19T14:17:00-03:00",
  "from_number_id": "123456789012345",
  "from_number": "5547999999999",
  "data": {
    "event": "join",
    "group_id": "120363214587@g.us",
    "participants": [
      {
        "user_id": "BR.874136928504",
        "wa_id": "5547888888888"
      }
    ]
  }
}

Business-Scoped User IDs (BSUID) — Junho 2026

ℹ️ Importante: A Meta adiciona o from_user_id (BSUID) aos webhooks — ela não descontinua o número de telefone. O número continua presente quando o usuário não adotou nome de usuário, ou pelas condições de retrospectiva de 30 dias / lista de contatos; é omitido apenas quando o usuário adota username e o número não pode ser incluído. Seu sistema está preparado para receber mensagens com BSUID, com ou sem número.

O que é BSUID?

BSUID (Business-Scoped User ID) é um identificador único gerado pela Meta para cada par usuário-negócio. Formato: CC. + até 128 alfanuméricos (ex: US.13491208655302741918); o BSUID principal usa o prefixo CC.ENT. (ex: US.ENT.11815799212886844830). O username do WhatsApp (ex: joao.silva) é exibido no app, mas o identificador usado na API é o BSUID.

Estrutura do Webhook com BSUID

Exemplo — Mensagem com BSUID (Julho 2026+)
{
  "event": "message",
  "direction": "inbound",
  "contact": {
    "user_id": "BR.13491208655302741918",
    "username": "joao.silva",
    "name": "João Silva"
  },
  "message": {
    "id": "wamid...",
    "type": "text",
    "text": "Oi, tudo bem?"
  }
}

Transição (Junho 2026) — Dual Mode

Durante a transição, webhooks incluem ambos número e BSUID. Seu sistema escolhe qual usar:

Exemplo — Mensagem com Número + BSUID
{
  "event": "message",
  "direction": "inbound",
  "contact": {
    "wa_id": "5547999999999",      ← número (omitido se username adotado e nº indisponível)
    "user_id": "US.13491208655302741918", ← BSUID (identificador estável)
    "username": "joao.silva",       ← username opcional
    "parent_user_id": "US.ENT.118...",    ← Parent BSUID (só multi-portfólio)
    "name": "João Silva"
  },
  "message": { ... }
}

Recomendações de Implementação

  • Use o BSUID como identificador estável: Se user_id estiver presente, use-o como chave. Mantenha o wa_id (número) quando vier — ele continua sendo enviado na maioria dos casos.
  • Armazene username: Novos contatos vêm com username — capture e exiba quando disponível.
  • Não remova o suporte a números: O número não é descontinuado; apenas pode faltar para usuários que adotaram nome de usuário.
  • Solicite o número quando precisar: Use o botão REQUEST_CONTACT_INFO (veja abaixo) para pedir o telefone de um usuário que o ocultou.

Campos aditivos por tipo de webhook

A partir de julho/2026 os webhooks podem trazer campos extras. Todos são aditivos — só aparecem quando a Meta os envia, sem quebrar o formato existente.

CampoOndeDescrição
contact.user_id / message.from_user_idmessage / echoBSUID do remetente.
contact.parent_user_idmessage / echoParent BSUID (só se multi-portfólio habilitado).
contact.usernamemessage / echoNome de usuário, quando o usuário adotou um.
status.recipient_user_idstatusBSUID do destinatário.
status.recipient_parent_user_idstatusParent BSUID do destinatário (multi-portfólio).
status.usernamestatus (delivered/read)Username do destinatário, quando disponível.

Solicitar contato — REQUEST_CONTACT_INFO (Julho 2026)

Quando o usuário toca no botão REQUEST_CONTACT_INFO (ou compartilha um contato), chega um webhook de mensagem com type: "contacts" contendo o número.

Payload — Webhook de contacts (compartilhamento)
{
  "event": "message",
  "contact": { "user_id": "US.13491208655302741918" },
  "message": {
    "type": "contacts",
    "from_user_id": "US.13491208655302741918",
    "contacts": [
      {
        "origin": "contact_request",   // "other" = compartilhou um contato direto
        "vcard": "...",                // presente só quando origin = "other"
        "phones": [
          { "phone": "+5547999999999", "wa_id": "5547999999999", "type": "..." }
        ]
      }
    ]
  }
}

Novos eventos de webhook

Três novos eventos passam a ser encaminhados ao seu endpoint. Inscreva os campos correspondentes no Painel de Apps da Meta para recebê-los.

user_id_update — BSUID do usuário mudou

Disparado quando o BSUID de um usuário muda (ex.: troca de número). Atualize o mapeamento no seu CRM usando previouscurrent.

Payload — Evento user_id_update
{
  "event": "user_id_update",
  "from_number_id": "<phone_number_id>",
  "contacts": [ { "wa_id": "5547999999999" } ],
  "user_id_update": [
    {
      "wa_id": "5547999999999",
      "user_id": { "previous": "US.OLD...", "current": "US.NEW..." },
      "parent_user_id": { "previous": "US.ENT.OLD...", "current": "US.ENT.NEW..." },
      "timestamp": "1750030073"
    }
  ]
}

business_username_updates — status do nome de usuário comercial

Disparado quando o status do seu nome de usuário comercial muda (reserved, approved ou deleted).

Payload — Evento business_username_updates
{
  "event": "business_username_updates",
  "from_number_id": "<phone_number_id>",
  "from_number": "5547999999999",
  "username": "minhaempresa",
  "status": "approved"   // approved | reserved | deleted
}

user_preferences — preferências de marketing (opt-in/out)

Encaminha as preferências do usuário, agora com BSUID/username quando presentes.

Payload — Evento user_preferences
{
  "event": "user_preferences",
  "from_number_id": "<phone_number_id>",
  "contacts": [ { "wa_id": "5547999999999", "user_id": "US.134..." } ],
  "user_preferences": [
    { "category": "marketing_messages", "value": "stop", "timestamp": 1750030073 }
  ]
}

Calling API — Webhooks de Chamadas (v25.0+)

Quando usuários ligam para seu número WhatsApp, você recebe webhooks detalhados sobre o evento de chamada com informações completas do chamador, incluindo BSUID e username.

Tipos de Eventos de Chamada

StatusDescrição
incomingChamada entrante iniciada pelo usuário
missedChamada não atendida
rejectedVocê rejeitou a chamada
acceptedVocê aceitou a chamada
connectedConexão estabelecida (início da conversa)
endedChamada finalizada

Exemplo de Webhook — Chamada Recebida

Payload — Nova Chamada Entrante
{
  "event": "call",
  "timestamp": "2026-06-01T10:30:00-03:00",
  "from_number_id": "950147584848138",
  "from_number": "+55 47 9999-9999",
  "call": {
    "id": "call_HBgM...",
    "status": "incoming",
    "duration_seconds": 0,
    "from": "5547999999999",
    "from_user_id": "BR.13491208655302741918",
    "from_username": "joao.silva",
    "from_name": "João Silva",
    "to_user_id": "seu_bsuid_aqui",
    "contacts": [{
      "user_id": "BR.13491208655302741918",
      "username": "joao.silva",
      "profile": {
        "name": "João Silva"
      }
    }]
  }
}

Exemplo — Chamada Finalizada (com duração)

Payload — Chamada Encerrada
{
  "event": "call",
  "timestamp": "2026-06-01T10:35:45-03:00",
  "from_number": "+55 47 9999-9999",
  "call": {
    "id": "call_HBgM...",
    "status": "ended",
    "duration_seconds": 312,     ← 5 minutos 12 segundos
    "from_user_id": "BR.xxxxx",
    "from_username": "joao.silva",
    "from_name": "João Silva"
  }
}

Casos de Uso

  • Rastreamento de chamadas: Armazene call_id, from_user_id, duration_seconds para análise.
  • CRM integration: Sincronize automaticamente chamadas entrantes com seu CRM usando BSUID.
  • Alertas: Notifique agentes sobre chamadas perdidas ou rejeitadas.
  • Relatórios: Gere dashboards de volume de chamadas e duração média por período.

Rastreamento de Origem — Facebook Ads

Quando usuários iniciam conversa através de anúncios do Facebook, você recebe dados detalhados sobre a campanha que gerou o contato. Ideal para análise de ROI e origem de leads.

Campo referral no Webhook

Presente apenas quando o usuário chegou via Facebook Ads ou outro canal de origem Meta:

Exemplo — Mensagem com Referral (Facebook Ads)
{
  "event": "message",
  "direction": "inbound",
  "contact": {
    "wa_id": "5547999999999",
    "user_id": "BR.xxxxx",
    "name": "João Silva"
  },
  "message": {
    "id": "wamid...",
    "type": "text",
    "text": "Poderia me passar mais informações?",
    "referral": {
      "source_type": "ad",
      "source_id": "120249258250040188",
      "source_url": "https://fb.me/6Y5vfFmfx",
      "headline": "Converse conosco",
      "ctwa_clid": "AfgryxuX3no-rLzpWHK9py-sp-..."
    }
  }
}

Campos de Referral

CampoTipoDescrição
source_typestringTipo de origem: "ad", "conversation_referral", etc.
source_idstringID único da campanha/anúncio na Meta.
source_urlstringURL que o usuário clicou para iniciar a conversa.
headlinestringTexto do botão ou copy do anúncio.
ctwa_clidstringID de clique do WhatsApp para rastreamento granular.

Integração com Analytics

  • ROI por Campanha: Agrupe source_id e calcule custo/conversão.
  • Performance por Headline: Compare qual copy do anúncio gera mais engajamento.
  • Atribuição Multi-canal: Cruzar dados com Google Analytics usando ctwa_clid.
Nota: O campo referral só aparece quando o usuário inicia conversa através de um anúncio. Não está presente em mensagens iniciadas manualmente.

Webhook de Alertas

Configure um endpoint separado para receber alertas operacionais como quedas de qualidade ou suspensões.

Mensagens

POST Enviar Mensagem de Texto

Envia uma mensagem de texto simples. Use to para enviar ao número de telefone ou recipient para enviar ao BSUID do usuário (veja IDs de Usuário).

Múltiplos Números Conectados:

Se você possui mais de um número ativo, adicione o parâmetro "from": "ID_DO_SEU_NUMERO" no JSON para especificar por qual canal enviar a mensagem. Se não for informado e houver mais de um número ativo, a API retorna erro MULTIPLE_NUMBERS (com a lista de números disponíveis) em vez de escolher um automaticamente — importante para contas SaaS com Link Direto compartilhado entre vários clientes (veja Contas SaaS). Com um único número ativo, o comportamento continua automático, sem precisar informar from.

Números Internacionais:

Por padrão o campo to é tratado como número brasileiro: se vier sem DDI (10 ou 11 dígitos), o sistema adiciona 55 automaticamente. Para enviar a um número de outro país, informe o número completo com + na frente (ex: "to": "+12025550123") — assim o DDI informado é respeitado e o 55 não é adicionado.

POST /api/v1/messages/send (Por Número)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "text",
  "text": {
    "body": "Olá! Como posso ajudar?"
  }
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "text",
  "text": {
    "body": "Olá! Como posso ajudar?"
  }
}

Resposta

JSON — 200 OK
{
  "success": true,
  "message": "Mensagem enviada com sucesso",
  "message_id": "wamid.HBgLNTU0Nzk5...",
  "from": "1234567890",
  "to": "5547999999999",
  "user_id": "BR.13491208655302741918"
}

POST Enviar Imagem

POST /api/v1/messages/send (Via Link)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "image",
  "image": {
    "link": "https://exemplo.com/imagem.jpg",
    "caption": "Legenda da imagem"
  }
}
POST /api/v1/messages/send (Via Link - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "image",
  "image": {
    "link": "https://exemplo.com/imagem.jpg",
    "caption": "Legenda da imagem"
  }
}
POST /api/v1/messages/send (Via Media ID)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "image",
  "image": {
    "id": "8451871321564",
    "caption": "Legenda opcional"
  }
}
POST /api/v1/messages/send (Via Media ID - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "image",
  "image": {
    "id": "8451871321564",
    "caption": "Legenda opcional"
  }
}

POST Enviar Vídeo

POST /api/v1/messages/send (Via Link)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "video",
  "video": {
    "link": "https://exemplo.com/video.mp4",
    "caption": "Legenda opcional"
  }
}
POST /api/v1/messages/send (Via Link - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "video",
  "video": {
    "link": "https://exemplo.com/video.mp4",
    "caption": "Legenda opcional"
  }
}
POST /api/v1/messages/send (Via Media ID)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "video",
  "video": {
    "id": "8451871321564"
  }
}
POST /api/v1/messages/send (Via Media ID - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "video",
  "video": {
    "id": "8451871321564"
  }
}

POST Enviar Áudio

Para enviar um arquivo de áudio simples. Se quiser que o áudio apareça como uma mensagem de voz (voice note gravado na hora), o arquivo deve estar no formato .ogg (codec opus) e você deve incluir o parâmetro "voice": true.

POST /api/v1/messages/send (Via Link)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "audio",
  "audio": {
    "link": "https://exemplo.com/audio.mp3"
  }
}
POST /api/v1/messages/send (Via Link - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "audio",
  "audio": {
    "link": "https://exemplo.com/audio.mp3"
  }
}
POST /api/v1/messages/send (Voice Note via Media ID)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "audio",
  "audio": {
    "id": "8451871321564",
    "voice": true
  }
}
POST /api/v1/messages/send (Voice Note via Media ID - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "audio",
  "audio": {
    "id": "8451871321564",
    "voice": true
  }
}

POST Enviar Documento

POST /api/v1/messages/send (Via Link)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "document",
  "document": {
    "link": "https://exemplo.com/arquivo.pdf",
    "filename": "documento.pdf"
  }
}
POST /api/v1/messages/send (Via Link - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "document",
  "document": {
    "link": "https://exemplo.com/arquivo.pdf",
    "filename": "documento.pdf"
  }
}
POST /api/v1/messages/send (Via Media ID)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "document",
  "document": {
    "id": "8451871321564",
    "filename": "boleto.pdf"
  }
}
POST /api/v1/messages/send (Via Media ID - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "document",
  "document": {
    "id": "8451871321564",
    "filename": "boleto.pdf"
  }
}

POST Enviar Sticker (Figurinha)

POST /api/v1/messages/send (Via Link)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "sticker",
  "sticker": {
    "link": "https://exemplo.com/figurinha.webp"
  }
}
POST /api/v1/messages/send (Via Link - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "sticker",
  "sticker": {
    "link": "https://exemplo.com/figurinha.webp"
  }
}
POST /api/v1/messages/send (Via Media ID)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "sticker",
  "sticker": {
    "id": "8451871321564"
  }
}
POST /api/v1/messages/send (Via Media ID - Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "sticker",
  "sticker": {
    "id": "8451871321564"
  }
}

POST Enviar Localização

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "location",
  "location": {
    "latitude": -26.9195,
    "longitude": -49.0661,
    "name": "Covercut",
    "address": "Rua Exemplo, 123"
  }
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "location",
  "location": {
    "latitude": -26.9195,
    "longitude": -49.0661,
    "name": "Covercut",
    "address": "Rua Exemplo, 123"
  }
}

POST Enviar Contato

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "contacts",
  "contacts": [
    {
      "name": { "formatted_name": "João Silva", "first_name": "João" },
      "phones": [{ "phone": "5547999999999", "type": "CELL" }]
    }
  ]
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "contacts",
  "contacts": [
    {
      "name": { "formatted_name": "João Silva", "first_name": "João" },
      "phones": [{ "phone": "5547999999999", "type": "CELL" }]
    }
  ]
}

POST Enviar Reação

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "reaction",
  "reaction": {
    "message_id": "wamid.xxx",
    "emoji": "👍"
  }
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "reaction",
  "reaction": {
    "message_id": "wamid.xxx",
    "emoji": "👍"
  }
}

POST Responder / Citar Mensagem

Para responder uma mensagem específica (fazendo com que ela apareça como citação no balão), inclua o campo context com o message_id (wamid) da mensagem original. Funciona com qualquer tipo de mensagem: texto, imagem, vídeo, áudio, documento, interativo, etc.

O message_id da mensagem recebida chega pelo webhook no campo message.id.

POST /api/v1/messages/send — Responder com texto
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "text",
  "text": {
    "body": "Claro! Vou verificar isso agora mesmo."
  },
  "context": {
    "message_id": "wamid.HBgLNTU0Nzk5OTk5OTk5..."
  }
}
POST /api/v1/messages/send — Responder com texto (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "text",
  "text": {
    "body": "Claro! Vou verificar isso agora mesmo."
  },
  "context": {
    "message_id": "wamid.HBgLNTU0Nzk5OTk5OTk5..."
  }
}
POST /api/v1/messages/send — Responder com imagem
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "image",
  "image": {
    "link": "https://exemplo.com/imagem.jpg",
    "caption": "Aqui está o comprovante!"
  },
  "context": {
    "message_id": "wamid.HBgLNTU0Nzk5OTk5OTk5..."
  }
}
POST /api/v1/messages/send — Responder com imagem (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "image",
  "image": {
    "link": "https://exemplo.com/imagem.jpg",
    "caption": "Aqui está o comprovante!"
  },
  "context": {
    "message_id": "wamid.HBgLNTU0Nzk5OTk5OTk5..."
  }
}
Como obter o message_id para responder:

Ao receber uma mensagem pelo webhook, o campo message.id contém o wamid. Armazene-o e passe-o no campo context.message_id ao responder. Veja a seção Webhooks para o formato do payload.

POST Enviar Mensagem Interativa

Crie uma experiência dinâmica com menus de lista, botões de resposta rápida ou botões de link externo (CTA URL).

POST Enviar Botões de Resposta Rápida

Envia uma mensagem com até 3 botões de resposta rápida. Ideal para confirmações simples.

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "header": { "type": "text", "text": "Confirmação" },
    "body": { "text": "Você gostaria de agendar agora?" },
    "footer": { "text": "Selecione uma opção" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "btn_yes", "title": "Sim" } },
        { "type": "reply", "reply": { "id": "btn_no", "title": "Não" } }
      ]
    }
  }
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "header": { "type": "text", "text": "Confirmação" },
    "body": { "text": "Você gostaria de agendar agora?" },
    "footer": { "text": "Selecione uma opção" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "btn_yes", "title": "Sim" } },
        { "type": "reply", "reply": { "id": "btn_no", "title": "Não" } }
      ]
    }
  }
}

1. Menu de Lista (List Message)

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "interactive",
  "interactive": {
    "type": "list",
    "header": { "type": "text", "text": "Título do Menu" },
    "body": { "text": "Escolha uma opção abaixo:" },
    "footer": { "text": "Rodapé opcional" },
    "action": {
      "button": "Ver Opções",
      "sections": [
        {
          "title": "Serviços",
          "rows": [
            { "id": "id1", "title": "Corte de Cabelo", "description": "30 minutos" },
            { "id": "id2", "title": "Barba Completa", "description": "20 minutos" }
          ]
        }
      ]
    }
  }
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "interactive",
  "interactive": {
    "type": "list",
    "header": { "type": "text", "text": "Título do Menu" },
    "body": { "text": "Escolha uma opção abaixo:" },
    "footer": { "text": "Rodapé opcional" },
    "action": {
      "button": "Ver Opções",
      "sections": [
        {
          "title": "Serviços",
          "rows": [
            { "id": "id1", "title": "Corte de Cabelo", "description": "30 minutos" },
            { "id": "id2", "title": "Barba Completa", "description": "20 minutos" }
          ]
        }
      ]
    }
  }
}

2. Botões de Resposta Rápida (Reply Buttons)

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "body": { "text": "Você gostaria de agendar agora?" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "btn_yes", "title": "Sim" } },
        { "type": "reply", "reply": { "id": "btn_no", "title": "Não" } }
      ]
    }
  }
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "body": { "text": "Você gostaria de agendar agora?" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "btn_yes", "title": "Sim" } },
        { "type": "reply", "reply": { "id": "btn_no", "title": "Não" } }
      ]
    }
  }
}

3. Botão de URL (CTA URL)

Envia uma mensagem interativa com um botão que abre um link externo. Este é o novo recurso oficial da Meta que substitui a necessidade de templates para links simples.

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "interactive",
  "interactive": {
    "type": "cta_url",
    "header": { "type": "text", "text": "Confira nosso site" },
    "body": { "text": "Clique no botão abaixo para ver as ofertas exclusivas que preparamos para você hoje." },
    "footer": { "text": "Ofertas válidas por tempo limitado" },
    "action": {
      "name": "cta_url",
      "parameters": {
        "display_text": "Ver Ofertas",
        "url": "https://sua-loja.com.br/ofertas"
      }
    }
  }
}
POST /api/v1/messages/send (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "interactive",
  "interactive": {
    "type": "cta_url",
    "header": { "type": "text", "text": "Confira nosso site" },
    "body": { "text": "Clique no botão abaixo para ver as ofertas exclusivas que preparamos para você hoje." },
    "footer": { "text": "Ofertas válidas por tempo limitado" },
    "action": {
      "name": "cta_url",
      "parameters": {
        "display_text": "Ver Ofertas",
        "url": "https://sua-loja.com.br/ofertas"
      }
    }
  }
}
Notas Importantes:

• O display_text do botão aceita no máximo 20 caracteres.

• O cabeçalho (header) é opcional e aceita text, image, video ou document.

• Este tipo de mensagem não requer aprovação prévia da Meta (diferente de templates).

4. WhatsApp Flow (Formulário Nativo)

Envia um formulário interativo do WhatsApp Flows (cadastro, pesquisa, agendamento etc.), aberto dentro do próprio app, sem redirecionar para uma página web. O Flow precisa existir e estar publicado na conta do WhatsApp Manager do número (crie e publique pelo Flow Builder da Meta para obter o flow_id).

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "interactive",
  "interactive": {
    "type": "flow",
    "header": { "type": "text", "text": "Cadastro" },
    "body": { "text": "Preencha o formulário abaixo para concluir seu cadastro." },
    "footer": { "text": "Leva menos de 1 minuto" },
    "action": {
      "name": "flow",
      "parameters": {
        "flow_id": "1234567890123456",
        "flow_cta": "Preencher agora",
        "flow_action_payload": {
          "screen": "TELA_INICIAL"
        }
      }
    }
  }
}
Notas Importantes:

flow_id é o ID do Flow publicado no WhatsApp Manager (ou use flow_name no lugar de flow_id).

flow_token é opcional — se você não enviar, geramos um automaticamente e devolvemos no campo flow_token da resposta, para você correlacionar com a resposta que chegar depois pelo webhook.

flow_cta (texto do botão) aceita no máximo 30 caracteres.

flow_action_payload.screen é obrigatório e deve ser o ID da primeira tela do Flow.

• Este endpoint cobre Flows estáticos (telas fixas definidas no próprio Flow JSON). Para telas com dados em tempo real, veja Flow Dinâmico abaixo.

5. Recebendo a resposta do Flow (Webhook)

Quando o usuário conclui o formulário, a resposta chega no seu webhook como uma mensagem interactive do tipo nfm_reply. O campo response_json vem como texto (conforme a Meta envia); nós também decodificamos automaticamente em response, já como objeto, para facilitar o consumo.

POST (seu webhook)
{
  "event": "message",
  "direction": "inbound",
  "contact": { "wa_id": "5547999999999", "name": "Maria" },
  "message": {
    "id": "wamid.XXXX",
    "type": "interactive",
    "interactive": {
      "type": "nfm_reply",
      "nfm_reply": {
        "name": "flow",
        "body": "Sent",
        "response_json": "{\"nome\":\"Maria\",\"email\":\"maria@exemplo.com\"}",
        "response": { "nome": "Maria", "email": "maria@exemplo.com" }
      }
    }
  }
}
Dica de correlação:

O nfm_reply não traz de volta o flow_token automaticamente. Para correlacionar a resposta com o envio original, inclua um campo (ex.: id_pedido) na última tela do seu Flow (ação "Complete") e leia-o dentro de response.

6. Exemplo completo: formulário de 2 telas (Geração de leads)

Um Flow estático pode ter várias telas sem precisar de endpoint próprio — a navegação entre telas acontece dentro do próprio Flow JSON (ação navigate), só a última tela usa complete para encerrar e devolver os dados coletados. Este exemplo replica o padrão do template pronto da Meta Coletar interesse de compra: uma tela de formulário (nome, e-mail e aceite de termos) seguida de uma tela de confirmação.

Monte este JSON no Flow Builder do WhatsApp Manager (categoria "Geração de leads" ou "Compras", conforme o seu caso) e publique para obter o flow_id:

Flow JSON
{
  "version": "7.3",
  "screens": [
    {
      "id": "FORM",
      "title": "Fale conosco",
      "data": {},
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "Form",
            "name": "form",
            "children": [
              { "type": "TextHeading", "text": "Garanta acesso antecipado" },
              { "type": "TextBody", "text": "Preencha seus dados para receber nossas ofertas exclusivas." },
              { "type": "TextInput", "name": "nome", "label": "Nome", "input-type": "text", "required": true },
              { "type": "TextInput", "name": "email", "label": "E-mail", "input-type": "email", "required": true },
              {
                "type": "CheckboxGroup",
                "name": "aceite_termos",
                "label": "Termos",
                "required": true,
                "data-source": [
                  { "id": "aceito", "title": "Concordo com os termos de uso" }
                ]
              },
              {
                "type": "Footer",
                "label": "Continuar",
                "on-click-action": {
                  "name": "navigate",
                  "next": { "type": "screen", "name": "SUCCESS" },
                  "payload": {
                    "nome": "${form.nome}",
                    "email": "${form.email}"
                  }
                }
              }
            ]
          }
        ]
      }
    },
    {
      "id": "SUCCESS",
      "title": "Confirmação",
      "terminal": true,
      "success": true,
      "data": {
        "nome": { "type": "string", "__example__": "Maria" },
        "email": { "type": "string", "__example__": "maria@exemplo.com" }
      },
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          { "type": "TextHeading", "text": "Cadastro concluído!" },
          { "type": "TextBody", "text": "`'Obrigado, ' ${data.nome} '. Enviamos a confirmação para ' ${data.email} '.'`" },
          {
            "type": "Footer",
            "label": "Concluir",
            "on-click-action": {
              "name": "complete",
              "payload": {
                "nome": "${data.nome}",
                "email": "${data.email}"
              }
            }
          }
        ]
      }
    }
  ]
}

Envio via nossa API (repare que flow_action_payload.screen aponta para a primeira tela, FORM):

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "interactive",
  "interactive": {
    "type": "flow",
    "body": { "text": "Preencha o formulário para garantir seu acesso antecipado." },
    "action": {
      "name": "flow",
      "parameters": {
        "flow_id": "SEU_FLOW_ID",
        "flow_cta": "Começar",
        "flow_action_payload": { "screen": "FORM" }
      }
    }
  }
}
Atenção — duas pegadinhas do Flow JSON:

• Todo campo declarado em data de uma tela (como nome e email na tela SUCCESS acima) precisa de um "__example__" junto do "type" — o Flow Builder usa esse valor pra renderizar a prévia da tela isoladamente. Sem ele, a publicação falha com "Required property '__example__' is missing".

${data.x} e ${form.x} precisam ser o valor inteiro da propriedade — não dá pra misturar texto fixo com a variável direto na mesma string ("Obrigado, ${data.nome}" é inválido). Isso não dá erro na publicação, só falha silenciosamente e mostra o texto cru pro usuário. Para concatenar, use a sintaxe com crase (Flow JSON v6.0+): `'Obrigado, ' ${data.nome}` — como no exemplo acima.

Como o webhook chega:

Ao concluir, o nfm_reply.response traz exatamente os campos que a tela SUCCESS devolveu no complete: {"nome": "Maria", "email": "maria@exemplo.com"} — o mesmo formato do exemplo da seção anterior.

7. Flow Dinâmico (telas com dados em tempo real)

Para Flows cujas telas dependem de lógica de negócio em tempo real — mostrar horários disponíveis, validar um CPF, listar produtos em estoque — a Meta chama de volta um endpoint HTTPS a cada interação do usuário, com o conteúdo criptografado (RSA-OAEP-SHA256 + AES-128-GCM). Nós cuidamos de toda essa criptografia e do roteamento multi-tenant; você só implementa a lógica de negócio no seu próprio servidor — exatamente como já funciona com o Webhook de mensagens.

Como ativar
  1. No painel, acesse Flow Dinâmico → selecione o número → clique em Gerar par de chaves e depois Registrar no WhatsApp Manager.
  2. Copie a URL de endpoint gerada (é única por número) e cole no Flow Builder da Meta, na opção "Com ponto de extremidade", ao criar/editar o Flow.
  3. No painel, configure a URL do seu servidor — é para lá que repassamos (já decifrado) tudo que a Meta manda.
O que o seu servidor recebe

Uma requisição POST em texto plano (JSON), assinada do mesmo jeito que o Webhook normal (header X-BSP-Signature, HMAC-SHA256 do corpo com o secret mostrado no painel):

POST (o seu servidor recebe)
{
  "version": "3.0",
  "action": "data_exchange",
  "screen": "AGENDAMENTO",
  "data": { "servico": "corte" },
  "flow_token": "a1b2c3..."
}

E deve responder com a próxima tela (ou a conclusão do Flow), no mesmo formato que a Meta espera — nós cifrarmos essa resposta de volta antes de repassar para a Meta:

Resposta esperada do seu servidor
{
  "screen": "CONFIRMACAO",
  "data": { "horario_disponivel": "14:00" }
}
Notas importantes:

• Responda rápido — a Meta está com a conversa do usuário esperando essa resposta (poucos segundos de timeout).

action pode ser INIT (abertura do Flow), data_exchange (avançou de tela) ou BACK (voltou). Health checks (ping) e notificações de erro da Meta são respondidos por nós diretamente — não chegam no seu servidor.

• A Meta não devolve o flow_token original automaticamente em nenhum outro lugar além deste próprio payload — se precisar correlacionar com o envio original, guarde o flow_token que você mesmo gerou/recebeu ao enviar o Flow.

Exemplo completo: agendamento com endpoint dinâmico

Um Flow de agendamento com 4 telas (APPOINTMENTDETAILSSUMMARYTERMS), onde os campos Local, Data e Horário só ficam disponíveis depois que o usuário escolhe o campo anterior — cada seleção dispara um on-select-action do tipo data_exchange, que chama o seu endpoint para buscar as próximas opções (é assim que se faz um dropdown em cascata, algo que não dá pra fazer num Flow estático). Repare em routing_model, que declara pra Meta o caminho possível entre as telas.

Flow JSON — Agendamento
{
  "version": "7.3",
  "data_api_version": "3.0",
  "routing_model": {
    "APPOINTMENT": ["DETAILS"],
    "DETAILS": ["SUMMARY"],
    "SUMMARY": ["TERMS"],
    "TERMS": []
  },
  "screens": [
    {
      "id": "APPOINTMENT",
      "title": "Appointment",
      "data": {
        "department": {
          "type": "array",
          "items": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" } } },
          "__example__": [
            { "id": "shopping", "title": "Shopping & Groceries" },
            { "id": "beauty", "title": "Beauty & Personal Care" }
          ]
        },
        "location": {
          "type": "array",
          "items": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" } } },
          "__example__": [
            { "id": "1", "title": "King's Cross, London" }
          ]
        },
        "is_location_enabled": { "type": "boolean", "__example__": true },
        "date": {
          "type": "array",
          "items": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" } } },
          "__example__": [
            { "id": "2024-01-01", "title": "Mon Jan 01 2024" }
          ]
        },
        "is_date_enabled": { "type": "boolean", "__example__": true },
        "time": {
          "type": "array",
          "items": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" } } },
          "__example__": [
            { "id": "10:30", "title": "10:30" },
            { "id": "11:00", "title": "11:00", "enabled": false }
          ]
        },
        "is_time_enabled": { "type": "boolean", "__example__": true }
      },
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "Form",
            "name": "appointment_form",
            "children": [
              {
                "type": "Dropdown",
                "label": "Department",
                "name": "department",
                "data-source": "${data.department}",
                "required": true,
                "on-select-action": {
                  "name": "data_exchange",
                  "payload": { "trigger": "department_selected", "department": "${form.department}" }
                }
              },
              {
                "type": "Dropdown",
                "label": "Location",
                "name": "location",
                "data-source": "${data.location}",
                "required": "${data.is_location_enabled}",
                "enabled": "${data.is_location_enabled}",
                "on-select-action": {
                  "name": "data_exchange",
                  "payload": { "trigger": "location_selected", "department": "${form.department}", "location": "${form.location}" }
                }
              },
              {
                "type": "Dropdown",
                "label": "Date",
                "name": "date",
                "data-source": "${data.date}",
                "required": "${data.is_date_enabled}",
                "enabled": "${data.is_date_enabled}",
                "on-select-action": {
                  "name": "data_exchange",
                  "payload": { "trigger": "date_selected", "department": "${form.department}", "location": "${form.location}", "date": "${form.date}" }
                }
              },
              {
                "type": "Dropdown",
                "label": "Time",
                "name": "time",
                "data-source": "${data.time}",
                "required": "${data.is_time_enabled}",
                "enabled": "${data.is_time_enabled}"
              },
              {
                "type": "Footer",
                "label": "Continue",
                "on-click-action": {
                  "name": "navigate",
                  "next": { "type": "screen", "name": "DETAILS" },
                  "payload": { "department": "${form.department}", "location": "${form.location}", "date": "${form.date}", "time": "${form.time}" }
                }
              }
            ]
          }
        ]
      }
    },
    {
      "id": "DETAILS",
      "title": "Details",
      "data": {
        "department": { "type": "string", "__example__": "beauty" },
        "location": { "type": "string", "__example__": "1" },
        "date": { "type": "string", "__example__": "2024-01-01" },
        "time": { "type": "string", "__example__": "11:30" }
      },
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "Form",
            "name": "details_form",
            "children": [
              { "type": "TextInput", "label": "Name", "name": "name", "required": true },
              { "type": "TextInput", "label": "Email", "name": "email", "input-type": "email", "required": true },
              { "type": "TextInput", "label": "Phone", "name": "phone", "input-type": "phone", "required": true },
              { "type": "TextArea", "label": "Further details", "name": "more_details", "required": false },
              {
                "type": "Footer",
                "label": "Continue",
                "on-click-action": {
                  "name": "data_exchange",
                  "payload": {
                    "department": "${data.department}", "location": "${data.location}", "date": "${data.date}", "time": "${data.time}",
                    "name": "${form.name}", "email": "${form.email}", "phone": "${form.phone}", "more_details": "${form.more_details}"
                  }
                }
              }
            ]
          }
        ]
      }
    },
    {
      "id": "SUMMARY",
      "title": "Summary",
      "terminal": true,
      "data": {
        "appointment": { "type": "string", "__example__": "Beauty & Personal Care Department at Kings Cross, London\nMon Jan 01 2024 at 11:30." },
        "details": { "type": "string", "__example__": "Name: John Doe\nEmail: john@example.com\nPhone: 123456789" },
        "department": { "type": "string", "__example__": "beauty" },
        "location": { "type": "string", "__example__": "1" },
        "date": { "type": "string", "__example__": "2024-01-01" },
        "time": { "type": "string", "__example__": "11:30" },
        "name": { "type": "string", "__example__": "John Doe" },
        "email": { "type": "string", "__example__": "john@example.com" },
        "phone": { "type": "string", "__example__": "123456789" },
        "more_details": { "type": "string", "__example__": "A free skin care consultation, please" }
      },
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          {
            "type": "Form",
            "name": "confirmation_form",
            "children": [
              { "type": "TextHeading", "text": "Appointment" },
              { "type": "TextBody", "text": "${data.appointment}" },
              { "type": "TextHeading", "text": "Details" },
              { "type": "TextBody", "text": "${data.details}" },
              {
                "type": "OptIn",
                "name": "terms",
                "label": "I agree to the terms",
                "required": true,
                "on-click-action": {
                  "name": "navigate",
                  "next": { "type": "screen", "name": "TERMS" },
                  "payload": {}
                }
              },
              {
                "type": "Footer",
                "label": "Confirm Appointment",
                "on-click-action": {
                  "name": "data_exchange",
                  "payload": {
                    "department": "${data.department}", "location": "${data.location}", "date": "${data.date}", "time": "${data.time}",
                    "name": "${data.name}", "email": "${data.email}", "phone": "${data.phone}", "more_details": "${data.more_details}"
                  }
                }
              }
            ]
          }
        ]
      }
    },
    {
      "id": "TERMS",
      "title": "Terms and Conditions",
      "layout": {
        "type": "SingleColumnLayout",
        "children": [
          { "type": "TextHeading", "text": "Our Terms" },
          { "type": "TextBody", "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit." }
        ]
      }
    }
  ]
}

Quando o usuário escolhe um "Department" na tela APPOINTMENT, o seu endpoint recebe (já decifrado):

POST (o seu servidor recebe)
{
  "version": "3.0",
  "action": "data_exchange",
  "screen": "APPOINTMENT",
  "data": { "trigger": "department_selected", "department": "beauty" },
  "flow_token": "a1b2c3..."
}

E deve responder com todos os campos declarados em data daquela mesma tela (mesmo os que não mudaram) — aqui, já com as opções de "Location" filtradas pelo departamento escolhido:

Resposta esperada do seu servidor
{
  "screen": "APPOINTMENT",
  "data": {
    "department": [{ "id": "beauty", "title": "Beauty & Personal Care" }],
    "location": [{ "id": "1", "title": "King's Cross, London" }],
    "is_location_enabled": true,
    "date": [],
    "is_date_enabled": false,
    "time": [],
    "is_time_enabled": false
  }
}

Pra abrir esse Flow pela nossa API, aponte flow_action_payload.screen pra primeira tela (APPOINTMENT):

POST /api/v1/messages/send
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "interactive",
  "interactive": {
    "type": "flow",
    "body": { "text": "Vamos agendar seu horário?" },
    "action": {
      "name": "flow",
      "parameters": {
        "flow_id": "SEU_FLOW_ID",
        "flow_cta": "Agendar",
        "flow_action_payload": { "screen": "APPOINTMENT" }
      }
    }
  }
}

POST Indicador de Digitação + Marcar como Lida

Em uma única chamada, este endpoint faz duas coisas simultaneamente:

  • Marca a mensagem como lida — o remetente vê os dois tiques azuis (✓✓)
  • Exibe o "digitando..." — o indicador de digitação aparece para o contato

Use o message_id (wamid) recebido no evento de webhook da mensagem inbound.

POST /api/v1/messages/typing
{
  "message_id": "wamid.xxx"
}

Opcional — se tiver múltiplos números, especifique qual usar com o campo from:

Com múltiplos números
{
  "message_id": "wamid.xxx",
  "from": "phone_number_id"
}

POST Carrossel de Mídia Interativo

Envie uma mensagem interativa com 2 a 10 cartões em formato de carrossel horizontal. Cada cartão pode ter uma imagem ou vídeo no cabeçalho, texto no corpo e um botão de URL ou de resposta rápida.

Regras da Meta para Carrosséis:

• A mensagem deve ter entre 2 e 10 cartões.

• O campo body.text principal (acima dos cartões) é obrigatório.

• Cada cartão deve ter cabeçalho do tipo image ou video.

• O tipo e a quantidade de botões deve ser igual em todos os cartões.

Endpoint dedicado

POST /api/v1/messages/carousel
POST https://api.covercut.com.br/api/v1/messages/carousel

Parâmetros

Campo Tipo Obrigatório Descrição
from string Condicional Phone Number ID do remetente. Obrigatório se há múltiplos números conectados.
to string Sim* Telefone do destinatário. Use to ou recipient.
recipient string Sim* BSUID do destinatário. Use to ou recipient.
body.text string Sim Texto principal exibido acima dos cartões.
cards array Sim Array de 2 a 10 cartões do carrossel.
cards[].header.type string Sim image ou video.
cards[].header.image.link string Condicional URL pública da imagem. Use link ou id.
cards[].header.image.id string Condicional Media ID da imagem (upload prévio). Use link ou id.
cards[].body.text string Não Texto descritivo exibido no corpo do cartão.
cards[].action object Sim Botão do cartão — cta_url ou quick_reply (veja exemplos abaixo).

Exemplo 1 — Carrossel com Botão de URL (CTA URL)

POST /api/v1/messages/carousel
{
  "from": "123456789012345",
  "to": "5547999999999",
  "body": {
    "text": "Confira nossas promoções da semana! 🔥"
  },
  "cards": [
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/promo1.jpg"
        }
      },
      "body": {
        "text": "Produto A — De R$99 por R$59,90"
      },
      "action": {
        "name": "cta_url",
        "parameters": {
          "display_text": "Ver Produto",
          "url": "https://sua-loja.com.br/produto-a"
        }
      }
    },
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/promo2.jpg"
        }
      },
      "body": {
        "text": "Produto B — Frete Grátis acima de R$100"
      },
      "action": {
        "name": "cta_url",
        "parameters": {
          "display_text": "Ver Produto",
          "url": "https://sua-loja.com.br/produto-b"
        }
      }
    }
  ]
}
POST /api/v1/messages/carousel (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "body": {
    "text": "Confira nossas promoções da semana! 🔥"
  },
  "cards": [
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/promo1.jpg"
        }
      },
      "body": {
        "text": "Produto A — De R$99 por R$59,90"
      },
      "action": {
        "name": "cta_url",
        "parameters": {
          "display_text": "Ver Produto",
          "url": "https://sua-loja.com.br/produto-a"
        }
      }
    },
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/promo2.jpg"
        }
      },
      "body": {
        "text": "Produto B — Frete Grátis acima de R$100"
      },
      "action": {
        "name": "cta_url",
        "parameters": {
          "display_text": "Ver Produto",
          "url": "https://sua-loja.com.br/produto-b"
        }
      }
    }
  ]
}

Exemplo 2 — Carrossel com Botões de Resposta Rápida

POST /api/v1/messages/carousel
{
  "from": "123456789012345",
  "to": "5547999999999",
  "body": {
    "text": "Escolha o serviço que deseja agendar 📅"
  },
  "cards": [
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/servico-corte.jpg"
        }
      },
      "body": {
        "text": "Corte de Cabelo — R$45,00 (30 min)"
      },
      "action": {
        "buttons": [
          {
            "type": "quick_reply",
            "quick_reply": {
              "id": "agendar_corte",
              "title": "Agendar"
            }
          }
        ]
      }
    },
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/servico-barba.jpg"
        }
      },
      "body": {
        "text": "Barba Completa — R$35,00 (20 min)"
      },
      "action": {
        "buttons": [
          {
            "type": "quick_reply",
            "quick_reply": {
              "id": "agendar_barba",
              "title": "Agendar"
            }
          }
        ]
      }
    },
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/servico-combo.jpg"
        }
      },
      "body": {
        "text": "Combo Completo — R$70,00 (50 min)"
      },
      "action": {
        "buttons": [
          {
            "type": "quick_reply",
            "quick_reply": {
              "id": "agendar_combo",
              "title": "Agendar"
            }
          }
        ]
      }
    }
  ]
}
POST /api/v1/messages/carousel (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "body": {
    "text": "Escolha o serviço que deseja agendar 📅"
  },
  "cards": [
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/servico-corte.jpg"
        }
      },
      "body": {
        "text": "Corte de Cabelo — R$45,00 (30 min)"
      },
      "action": {
        "buttons": [
          {
            "type": "quick_reply",
            "quick_reply": {
              "id": "agendar_corte",
              "title": "Agendar"
            }
          }
        ]
      }
    },
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/servico-barba.jpg"
        }
      },
      "body": {
        "text": "Barba Completa — R$35,00 (20 min)"
      },
      "action": {
        "buttons": [
          {
            "type": "quick_reply",
            "quick_reply": {
              "id": "agendar_barba",
              "title": "Agendar"
            }
          }
        ]
      }
    },
    {
      "header": {
        "type": "image",
        "image": {
          "link": "https://exemplo.com/servico-combo.jpg"
        }
      },
      "body": {
        "text": "Combo Completo — R$70,00 (50 min)"
      },
      "action": {
        "buttons": [
          {
            "type": "quick_reply",
            "quick_reply": {
              "id": "agendar_combo",
              "title": "Agendar"
            }
          }
        ]
      }
    }
  ]
}

Resposta

JSON — 200 OK
{
  "success": true,
  "message": "Carrossel enviado com sucesso",
  "message_id": "wamid.HBgLNTU0Nzk5...",
  "from": "123456789012345",
  "to": "5547999999999",
  "user_id": "BR.13491208655302741918"
}
Atenção — Consistência de Botões:

Todos os cartões do carrossel devem usar o mesmo tipo e a mesma quantidade de botões. Por exemplo, se o primeiro cartão tem 1 botão quick_reply, todos os demais cartões também devem ter exatamente 1 botão quick_reply. Misturar cta_url com quick_reply resultará em erro.

Mensagens de Catálogo e Produto

Envie produtos do seu catálogo do WhatsApp diretamente na conversa — sem precisar de template aprovado. Use o endpoint dedicado:

Endpoint
POST https://api.covercut.com.br/api/v1/messages/catalog
Pré-requisitos e Limitações:
  • Catálogo vinculado: Crie o catálogo no Commerce Manager e vincule ao WABA do número antes de usar.
  • catalog_id e SKUs: O catalog_id e os product_retailer_id (SKUs) são obtidos no Commerce Manager da Meta.
  • Janela de 24h: Esses tipos de mensagem só podem ser enviados dentro da janela de atendimento ativa (o cliente precisa ter iniciado uma conversa nas últimas 24h). Fora da janela, use um Template de Catálogo aprovado.

POST Produto Único (Single Product)

Exibe um card interativo com um único produto do catálogo. O cliente pode ver detalhes, adicionar ao carrinho e iniciar o checkout.

POST /api/v1/messages/catalog
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "product",
  "body": {
    "text": "Confira este produto em promoção!"
  },
  "footer": {
    "text": "Estoque limitado"
  },
  "action": {
    "catalog_id": "SEU_CATALOG_ID",
    "product_retailer_id": "SKU_DO_PRODUTO"
  }
}
POST /api/v1/messages/catalog (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "product",
  "body": {
    "text": "Confira este produto em promoção!"
  },
  "footer": {
    "text": "Estoque limitado"
  },
  "action": {
    "catalog_id": "SEU_CATALOG_ID",
    "product_retailer_id": "SKU_DO_PRODUTO"
  }
}
Campos opcionais:

body, footer e header são opcionais no produto único. O card do produto (imagem, título e preço) é exibido automaticamente a partir do catálogo.

POST Multi-Produtos (Product List)

Exibe uma lista com até 30 produtos organizados em até 10 seções. Ideal para vitrines e recomendações personalizadas.

POST /api/v1/messages/catalog
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "product_list",
  "header": {
    "type": "text",
    "text": "Produtos em Destaque"
  },
  "body": {
    "text": "Selecionamos estes itens especialmente para você 🛍️"
  },
  "footer": {
    "text": "Frete grátis acima de R$200"
  },
  "action": {
    "catalog_id": "SEU_CATALOG_ID",
    "sections": [
      {
        "title": "Mais Vendidos",
        "product_items": [
          { "product_retailer_id": "SKU_1" },
          { "product_retailer_id": "SKU_2" },
          { "product_retailer_id": "SKU_3" }
        ]
      },
      {
        "title": "Novidades",
        "product_items": [
          { "product_retailer_id": "SKU_4" },
          { "product_retailer_id": "SKU_5" }
        ]
      }
    ]
  }
}
POST /api/v1/messages/catalog (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "product_list",
  "header": {
    "type": "text",
    "text": "Produtos em Destaque"
  },
  "body": {
    "text": "Selecionamos estes itens especialmente para você 🛍️"
  },
  "footer": {
    "text": "Frete grátis acima de R$200"
  },
  "action": {
    "catalog_id": "SEU_CATALOG_ID",
    "sections": [
      {
        "title": "Mais Vendidos",
        "product_items": [
          { "product_retailer_id": "SKU_1" },
          { "product_retailer_id": "SKU_2" },
          { "product_retailer_id": "SKU_3" }
        ]
      },
      {
        "title": "Novidades",
        "product_items": [
          { "product_retailer_id": "SKU_4" },
          { "product_retailer_id": "SKU_5" }
        ]
      }
    ]
  }
}

POST Catálogo Completo (Catalog Message)

Envia um botão que abre o catálogo completo do negócio no WhatsApp. O parâmetro thumbnail_product_retailer_id é opcional e define qual produto aparece como capa.

POST /api/v1/messages/catalog
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "catalog_message",
  "body": {
    "text": "Confira todo o nosso catálogo com preços e disponibilidade em tempo real! 🛒"
  },
  "footer": {
    "text": "Entrega para todo o Brasil"
  },
  "action": {
    "name": "catalog_message",
    "parameters": {
      "thumbnail_product_retailer_id": "SKU_CAPA"
    }
  }
}
POST /api/v1/messages/catalog (Por BSUID)
{
  "from": "123456789012345",
  "recipient": "BR.13491208655302741918",
  "type": "catalog_message",
  "body": {
    "text": "Confira todo o nosso catálogo com preços e disponibilidade em tempo real! 🛒"
  },
  "footer": {
    "text": "Entrega para todo o Brasil"
  },
  "action": {
    "name": "catalog_message",
    "parameters": {
      "thumbnail_product_retailer_id": "SKU_CAPA"
    }
  }
}

Resposta (todos os tipos)

JSON — 200 OK
{
  "success": true,
  "message": "Mensagem de catálogo enviada com sucesso",
  "message_id": "wamid.HBgLNTU0Nzk5...",
  "from": "123456789012345",
  "to": "5547999999999"
}

GET Consultar Status da Mensagem

Consulte o status de entrega das mensagens enviadas pelo seu número. Use este endpoint para sincronizar os status sent, delivered, read e failed com o seu banco de dados.

GET /api/v1/messages/status
GET https://api.covercut.com.br/api/v1/messages/status

Parâmetros de Query

ParâmetroTipoDescrição
wamidstringID único da mensagem (WAID).
statusstringFiltra por: sent, delivered, read ou failed.
fromstringFiltra pelo número de origem (aceita o telefone ou o phone_number_id).
recipientstringFiltra pelo telefone de destino.
sincestringData/hora início. ISO 8601 ou YYYY-MM-DD.
untilstringData/hora fim. ISO 8601 ou YYYY-MM-DD.
limitintegerQuantidade por página (Padrão 50, Máx 200).
offsetintegerPaginação (Padrão 0).
Exemplo de Resposta — 200 OK
{
  "success": true,
  "messages": [
    {
      "wamid": "wamid.HBgLNTUxMTk5OTk5OTk5...",
      "status": "read",
      "recipient": "5511999990000",
      "created_at": "2025-01-10 14:32:00"
    }
  ]
}

GET Exportar Logs (CSV)

Gere e baixe um arquivo .csv completo contendo o histórico de mensagens, facilitando a importação em ferramentas de BI, ERPs ou CRMs para retenção própria.

GET /api/v1/messages/export
GET https://api.covercut.com.br/api/v1/messages/export

Parâmetros de Query Opcionais

ParâmetroTipoDescrição
statusstringFiltra por: sent, delivered, read ou failed.
fromstringFiltra pelo número de origem que enviou a mensagem.
recipientstringFiltra pelo telefone de destino.
sincestringData de início. Formato YYYY-MM-DD (Ex: 2026-05-01).
untilstringData final. Formato YYYY-MM-DD (Ex: 2026-05-31).
Exemplo de Requisição (cURL)
curl -X GET "https://api.covercut.com.br/api/v1/messages/export?since=2026-05-01&until=2026-05-31" \
     -H "X-API-Key: sua-chave-api" \
     -H "X-API-Secret: seu-secret-api" \
     --output logs_maio.csv

Gestão de Templates

Crie, gerencie e envie templates de mensagem aprovados pela Meta.

Múltiplos Números Conectados

Todo endpoint desta página (enviar, listar, criar, editar, excluir template) opera sobre um único número (uma WABA) por chamada, via o campo from (phone_number_id). Se sua conta tem só um número ativo, from é opcional — é usado automaticamente. Se você tem mais de um número ativo (conta com vários números, ou revendedor com contas SaaS), omitir from retorna erro MULTIPLE_NUMBERS (com a lista de números disponíveis) em vez de escolher um sozinho. Veja o detalhe completo do erro na seção Listar Templates.

Headers Requeridos (todos os endpoints desta página)
X-API-Key: sua_chave_de_api
X-API-Secret: seu_segredo_de_api
Content-Type: application/json

Para não repetir os headers em cada exemplo desta página, os blocos de código abaixo mostram só a URL e o body — mas X-API-Key e X-API-Secret são obrigatórios em todas as chamadas. Já from vai no body (POST) ou na query string (GET), nunca em um header.

Enviar Template

Inicie conversas com mensagens pré-aprovadas. Use to (telefone) ou recipient (BSUID) como destino.

Quando usar o campo components?

O campo components serve para preencher variáveis dinâmicas do seu template (exemplo: {{1}}, {{2}}) ou para enviar arquivos de mídia no cabeçalho.

  • Se o seu template for apenas um texto fixo (sem variáveis e sem arquivos), você não precisa enviar o components no JSON. Basta enviar apenas o nome e o idioma.
  • Se o seu template tiver variáveis no texto, você deve enviar o type: body preenchendo cada variável.
  • Se o seu template não tiver variáveis no texto, mas exigir um arquivo de imagem/vídeo/documento, você deve enviar apenas o type: header com o link/id do arquivo.

Template sem variáveis (texto fixo)

Quando o template não possui nenhuma variável ({{1}}, {{2}}…) nem cabeçalho de mídia, basta enviar o name e o language. O campo components é completamente opcional nesse caso.

POST /api/v1/messages/template (sem variáveis)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "boas_vindas",
    "language": { "code": "pt_BR" }
  }
}

Template com variáveis no texto

Quando o template contém variáveis ({{1}}, {{2}}…), envie o componente body com os valores que substituirão cada variável, na mesma ordem.

POST /api/v1/messages/template (com variáveis)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "confirmacao_pedido",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "João Silva" },
          { "type": "text", "text": "#4521" }
        ]
      }
    ]
  }
}

Templates com Mídia (Imagem/Vídeo/Documento)

Para templates que possuem cabeçalho de mídia, use o componente header. Se ele não tiver variáveis de texto, remova o bloco body do exemplo abaixo:

Exemplo: Template com Vídeo
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "video_promo",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "header",
        "parameters": [
          {
            "type": "video",
            "video": { "link": "https://suisite.com/video.mp4" }
          }
        ]
      },
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "Sandro" }
        ]
      }
    ]
  }
}

Templates com Botões Dinâmicos

Templates podem ter botões de Call to Action (Link/Telefone), Quick Reply ou Copy Code (copiar código).

Exemplo: Botão URL Dinâmico
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "confirmacao_agendamento",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "body",
        "parameters": [{ "type": "text", "text": "15/05 às 14h" }]
      },
      {
        "type": "button",
        "sub_type": "url",
        "index": 0,
        "parameters": [
          { "type": "text", "text": "agendar/id_123" }
        ]
      }
    ]
  }
}

O valor em text é apenas o sufixo que substitui o {{1}} da URL do template — a Meta concatena ao prefixo fixo. Se o template é https://seusite.com.br/{{1}} e você envia agendar/id_123, o cliente abre https://seusite.com.br/agendar/id_123. Não envie a URL completa. Para criar um template com esse botão, veja Criar Template → Botão de URL Dinâmica.

Combinando Cabeçalho + Corpo + Botões (payload completo)

O caso mais comum de template promocional junta os três blocos no mesmo envio. Envie os componentes nesta ordem: headerbodybutton.

Regra de ouro: só inclua o componente que carrega dado dinâmico
  • Header de mídia: sempre entra no payload (o arquivo — imagem/vídeo — é passado no momento do envio via link ou id).
  • Body: só entra se o texto tiver variáveis ({{1}}, {{2}}…). Se o texto for estático, não envie o bloco body.
  • Botões: só entram os botões dinâmicos (quick_reply com payload, URL com sufixo variável, copy_code, payment_request). Botão totalmente estático (URL fixa, texto fixo) não precisa de componente — ele aparece sozinho.
  • O index do botão é base 0 e conta a posição do botão entre todos os botões do template (0, 1, 2…).
Texto estático + imagem + botões

O corpo é fixo, então não há bloco body. Entram o header (imagem) e apenas os botões dinâmicos — aqui, dois botões de resposta rápida (quick_reply). O payload é o texto que volta no webhook quando o cliente toca no botão.

Texto estático + imagem + botões (quick reply)
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "promo_com_imagem",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "header",
        "parameters": [
          { "type": "image", "image": { "link": "https://seusite.com/banner.jpg" } }
        ]
      },
      {
        "type": "button",
        "sub_type": "quick_reply",
        "index": 0,
        "parameters": [
          { "type": "payload", "payload": "QUERO_SABER_MAIS" }
        ]
      },
      {
        "type": "button",
        "sub_type": "quick_reply",
        "index": 1,
        "parameters": [
          { "type": "payload", "payload": "FALAR_COM_VENDEDOR" }
        ]
      }
    ]
  }
}

Se os botões forem URL/telefone fixos (sem variável), o payload fica com apenas o bloco header — nenhum bloco button é necessário.

Texto variável + imagem ou vídeo + botões

Aqui entram os três blocos: header (mídia), body (preenchendo as variáveis) e button (URL com sufixo dinâmico). Para vídeo, troque o parâmetro do header de image para video (mostrado logo abaixo).

Texto variável + imagem + botão URL dinâmico
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "promo_personalizada",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "header",
        "parameters": [
          { "type": "image", "image": { "link": "https://seusite.com/banner.jpg" } }
        ]
      },
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "João" },
          { "type": "text", "text": "20%" }
        ]
      },
      {
        "type": "button",
        "sub_type": "url",
        "index": 0,
        "parameters": [
          { "type": "text", "text": "produto/id_123" }
        ]
      }
    ]
  }
}

Mesmo payload com vídeo no cabeçalho — apenas o parâmetro do header muda:

Variação: cabeçalho de vídeo
{
  "type": "header",
  "parameters": [
    { "type": "video", "video": { "link": "https://seusite.com/promo.mp4" } }
  ]
}

Em vez de link, você pode usar { "id": "MEDIA_ID" } com o id de um arquivo já enviado via upload de mídia — evita a Meta baixar a URL a cada disparo.

Botão Copiar Código (Copy Code)

Permite que o cliente toque num botão e o código seja copiado automaticamente para a área de transferência — ideal para chaves PIX, cupons ou códigos de acesso.

Restrições confirmadas pela Meta: exclusivo para categoria MARKETING. O texto do botão é fixo ("Copiar código da oferta") e não pode ser customizado. A interface do Meta Business Manager não exibe essa opção — o template deve ser criado via nossa plataforma ou pela API. Não funciona no WhatsApp Web.
1. Criar o template (via nossa plataforma)

No dashboard, crie um template de categoria Marketing e adicione um botão "Copiar Código (Cupom)". O payload enviado à Meta será:

Criar template com botão COPY_CODE
{
  "from": "123456789012345",
  "name": "cupom_desconto",
  "category": "MARKETING",
  "language": "pt_BR",
  "components": [
    {
      "type": "BODY",
      "text": "Olá, {{1}}! Use o código abaixo para obter {{2}} de desconto.",
      "example": {
        "body_text": [["João", "20%"]]
      }
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "COPY_CODE",
          "example": ["DESCONTO20"]
        }
      ]
    }
  ]
}
2. Enviar a mensagem

Após aprovação, passe o código real no parâmetro coupon_code. O parâmetro usa "type": "coupon_code" (não "text").

Enviar mensagem com botão Copiar Código
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "cupom_desconto",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "João" },
          { "type": "text", "text": "20%" }
        ]
      },
      {
        "type": "button",
        "sub_type": "copy_code",
        "index": 0,
        "parameters": [
          { "type": "coupon_code", "coupon_code": "DESCONTO20" }
        ]
      }
    ]
  }
}
Limitações: código limitado a 15 caracteres. Apenas 1 botão COPY_CODE por template. Não funciona no WhatsApp Web. A Meta pode rejeitar templates cujo conteúdo seja transacional (ex: cobrança PIX) — o botão é destinado a cupons e ofertas promocionais.

Templates de Solicitação de Pagamento (Brasil)

Permite que comerciantes solicitem pagamentos via PIX dinâmico, Boleto ou Link de pagamento diretamente na conversa do WhatsApp, sem necessidade da integração de Detalhes do Pedido. Disponível para categorias Marketing e Utilidade.

Vantagens: até 3 botões de pagamento por template (um de cada tipo); integração simplificada — o código/link real é enviado dinamicamente ao disparar a mensagem; combina com outros tipos de botões e com modelos de oferta por tempo limitado.

1. Criar o template

No dashboard selecione o botão "Solicitação de Pagamento (BR)" e escolha o tipo. O texto do botão é fixado pela Meta conforme o tipo escolhido.

TipoTexto fixo do botãoCampo obrigatório no envio
pix_dynamic_codeCopy Pix codepix_dynamic_code.code
boletoCopy Boleto codeboleto.digitable_line
payment_linkOpen payment linkpayment_link.uri
Criar template com 3 botões de pagamento
{
  "from": "123456789012345",
  "name": "cobranca_pagamento",
  "category": "UTILITY",
  "language": "pt_BR",
  "components": [
    {
      "type": "BODY",
      "text": "Olá, {{1}}! Sua fatura de R$ {{2}} está disponível. Escolha a forma de pagamento:"
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "PAYMENT_REQUEST",
          "text": "Copy Pix code",
          "payment_setting": {
            "type": "pix_dynamic_code",
            "pix_dynamic_code": { "code": "00020101021226700014br.gov.bcb.pix..." }
          }
        },
        {
          "type": "PAYMENT_REQUEST",
          "text": "Copy Boleto code",
          "payment_setting": {
            "type": "boleto",
            "boleto": { "digitable_line": "03399026944140000002628346101018898510000008848" }
          }
        },
        {
          "type": "PAYMENT_REQUEST",
          "text": "Open payment link",
          "payment_setting": {
            "type": "payment_link",
            "payment_link": { "uri": "https://exemplo.com/pagar/123" }
          }
        }
      ]
    }
  ]
}

2. Enviar a mensagem

O código/link real é passado nos parâmetros do botão com sub_type: "payment_request". O index corresponde à posição do botão (0, 1, 2).

Enviar mensagem com botões de pagamento
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "cobranca_pagamento",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "João" },
          { "type": "text", "text": "150,00" }
        ]
      },
      {
        "type": "button",
        "sub_type": "payment_request",
        "index": "0",
        "parameters": [{
          "type": "action",
          "action": {
            "payment_request": {
              "payment_setting": {
                "type": "pix_dynamic_code",
                "pix_dynamic_code": { "code": "00020101021226700014br.gov.bcb.pix..." }
              }
            }
          }
        }]
      },
      {
        "type": "button",
        "sub_type": "payment_request",
        "index": "1",
        "parameters": [{
          "type": "action",
          "action": {
            "payment_request": {
              "payment_setting": {
                "type": "boleto",
                "boleto": { "digitable_line": "03399026944140000002628346101018898510000008848" }
              }
            }
          }
        }]
      },
      {
        "type": "button",
        "sub_type": "payment_request",
        "index": "2",
        "parameters": [{
          "type": "action",
          "action": {
            "payment_request": {
              "payment_setting": {
                "type": "payment_link",
                "payment_link": { "uri": "https://exemplo.com/pagar/123" }
              }
            }
          }
        }]
      }
    ]
  }
}

Template Detalhes do Pedido (Brasil)

Template com formato especial ORDER_DETAILS que exibe itens do pedido, subtotal, impostos e opções de pagamento (PIX, Boleto, Link). Ideal para faturas e cobranças detalhadas. Disponível para categorias Marketing e Utilidade.

1. Criar o template

No dashboard selecione Formato: Detalhes do Pedido. O botão ORDER_DETAILS é adicionado automaticamente. Se o cabeçalho for Imagem ou Documento, envie um arquivo de exemplo — o dashboard faz o upload e preenche example.header_handle (cabeçalho de mídia não usa text).

Criar template Order Details (cabeçalho com Documento)
{
  "from": "123456789012345",
  "name": "fatura_detalhada",
  "category": "UTILITY",
  "language": "pt_BR",
  "display_format": "ORDER_DETAILS",
  "components": [
    {
      "type": "HEADER",
      "format": "DOCUMENT",
      "example": { "header_handle": ["4::YXBwbGljYXRpb24vcGRm..."] }
    },
    {
      "type": "BODY",
      "text": "Olá, {{1}}! Segue sua fatura. Escolha a forma de pagamento."
    },
    {
      "type": "BUTTONS",
      "buttons": [
        { "type": "ORDER_DETAILS", "text": "Ver detalhes do pedido" }
      ]
    }
  ]
}

2. Enviar a mensagem

O payload de envio inclui os itens do pedido, valores e o código de pagamento no componente do botão com sub_type: "order_details". Os valores usam offset: 100 (centavos). Se o template tem cabeçalho de Imagem ou Documento (PDF), é obrigatório incluir também um componente header com a mídia a cada envio — a Meta não reaproveita o arquivo de exemplo cadastrado na criação, ele precisa ser enviado (por link público ou id de mídia já enviada à Meta) em toda mensagem.

Enviar mensagem Order Details com PIX dinâmico + PDF no cabeçalho
{
  "from": "123456789012345",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "fatura_detalhada",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "header",
        "parameters": [{
          "type": "document",
          "document": {
            "link": "https://exemplo.com/faturas/pedido-001.pdf",
            "filename": "fatura-pedido-001.pdf"
          }
        }]
      },
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "João" }
        ]
      },
      {
        "type": "button",
        "sub_type": "order_details",
        "index": 0,
        "parameters": [{
          "type": "action",
          "action": {
            "order_details": {
              "reference_id": "PEDIDO-001-2026",
              "type": "digital-goods",
              "payment_type": "br",
              "payment_settings": [{
                "type": "pix_dynamic_code",
                "pix_dynamic_code": {
                  "code": "00020101021226700014br.gov.bcb.pix...",
                  "merchant_name": "Empresa LTDA",
                  "key": "39580525000189",
                  "key_type": "CNPJ"
                }
              }],
              "currency": "BRL",
              "total_amount": { "value": 15000, "offset": 100 },
              "order": {
                "status": "pending",
                "items": [{
                  "retailer_id": "PROD-001",
                  "name": "Plano Mensal",
                  "amount": { "value": 15000, "offset": 100 },
                  "quantity": 1
                }],
                "subtotal": { "value": 15000, "offset": 100 },
                "tax": { "value": 0, "offset": 100, "description": "Sem impostos" }
              }
            }
          }
        }]
      }
    ]
  }
}
Offset: todos os valores monetários usam offset: 100 — ou seja, "value": 15000 = R$ 150,00. O campo reference_id deve ser único por pedido para rastreamento. Tipos de chave PIX aceitos: CNPJ, CPF, EMAIL, PHONE, RANDOM.
Cabeçalho de mídia: troque "type": "document" por "type": "image" (e o objeto image com link) se o template usar cabeçalho de Imagem. O componente header segue a mesma regra de qualquer template com mídia — veja Enviar Template de Marketing — e deve ser o primeiro item do array components.

Enviar Template de Marketing

Endpoint dedicado (alias) para disparo de Custom Marketing Templates, com suporte a parâmetros nomeados.

POST /api/v1/messages/marketing
{
  "from": "123456789012345",
  "to": "5547999999999",
  "template": {
    "name": "oferta_especial_maio",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "parameter_name": "discount_code", "text": "BEMVINDO20" },
          { "type": "text", "parameter_name": "discount_amount", "text": "20%" }
        ]
      }
    ]
  }
}

Listar Templates da Meta

Consulte todos os templates de mensagem cadastrados na sua conta WABA diretamente da Meta. Este endpoint retorna o status de aprovação, categoria e componentes de cada template.

GET Listar Templates

Requer Autenticação

Mesmo sendo uma requisição GET, este endpoint exige o envio dos headers X-API-Key e X-API-Secret.

GET /api/v1/templates/list
curl "https://api.covercut.com.br/api/v1/templates/list?from=123456789012345" \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api"

O ?from=123456789012345 na URL acima é o phone_number_id do número que você quer consultar — é assim que ele é enviado neste endpoint GET (na query string, não no body e não em um header). Se sua conta só tem um número, pode remover o ?from=... inteiro da URL.

Query Params (opcionais)

ParâmetroDescrição
fromphone_number_id do número a consultar. Veja o aviso abaixo — pode ser obrigatório dependendo da sua conta.
statusFiltra por status: APPROVED, PENDING, REJECTED ou PAUSED.
categoryFiltra por categoria: MARKETING, UTILITY ou AUTHENTICATION.
nameFiltra por nome (busca parcial, sem diferenciar maiúsculas/minúsculas).
limitMáximo de templates retornados. Padrão 200, máximo 500.
from é obrigatório se você tem mais de um número ativo

Este endpoint sempre consulta os templates de um único número (uma WABA) por chamada. Se a sua conta tiver só um número ativo, from é opcional — ele é usado automaticamente. Se você tiver mais de um número ativo (conta com vários números, ou revendedor com contas SaaS), omitir from retorna erro em vez de escolher um número sozinho:

{
  "success": false,
  "error": "Você tem mais de um número ativo — informe ?from={phone_number_id} pra escolher de qual número listar os templates.",
  "code": "MULTIPLE_NUMBERS",
  "numbers": [
    { "phone_number_id": "111...", "display_phone_number": "+55 47 99999-0001", "client_ref": null },
    { "phone_number_id": "222...", "display_phone_number": "+55 47 99999-0002", "client_ref": "32" }
  ]
}

Use os valores em numbers[].phone_number_id pra tentar de novo já com from definido. Se você usa contas SaaS com Link Direto compartilhado entre vários clientes, client_ref ajuda a identificar de qual cliente é cada número (veja Contas SaaS).

Exemplo de Resposta — 200 OK

{
  "success": true,
  "message": "Templates listados com sucesso",
  "waba_id": "123456789012345",
  "phone_number_id": "111222333444555",
  "total": 2,
  "templates": [
    {
      "id": "987654321098765",
      "name": "boas_vindas",
      "status": "APPROVED",
      "category": "MARKETING",
      "language": "pt_BR",
      "components": [ { "type": "BODY", "text": "Olá {{1}}, bem-vindo!" } ],
      "quality_score": { "score": "GREEN", "date": 1755648000 }
    }
  ]
}

O campo quality_score vem junto de cada template — veja o que ele significa e como é exibido no painel na seção Qualidade e Pausa Automática de Templates logo abaixo.

Qualidade e Pausa Automática de Templates

A Meta atribui uma nota de qualidade a cada template aprovado, calculada a partir do feedback dos destinatários (bloqueios, denúncias, taxa de leitura). Templates com qualidade ruim são pausados automaticamente — e, em casos repetidos, desabilitados. Já exibimos essa nota no painel de Templates, ao lado de cada modelo.

Níveis de Qualidade

O campo quality_score.score (retornado por Listar Templates) assume um destes quatro valores:

NotaSignificado
GREENQualidade alta — pouco ou nenhum feedback negativo, envia normalmente.
YELLOWQualidade média — feedback negativo de vários usuários ou baixa taxa de leitura. Entrega pode começar a ser afetada.
REDQualidade baixa — feedback negativo significativo. Risco alto de pausa automática.
UNKNOWNAinda sem dado suficiente — comum em templates recém-aprovados, até acumular volume de envio.

Pausa Automática

Quando um template atinge a pior faixa de qualidade, a Meta pausa o envio automaticamente — de forma progressiva:

OcorrênciaConsequência
1ª vezPausado por 3 horas
2ª vezPausado por 6 horas
3ª vezDesabilitado permanentemente (precisa criar um template novo)
Envio durante a pausa: a API rejeita a chamada (o template não é entregue), mas você não é cobrado pela tentativa. Se você dispara campanhas automatizadas, verifique o status do template (via Listar Templates) antes de reenviar em massa — um template pausado no meio de uma campanha grande derruba silenciosamente todos os envios seguintes até o fim da pausa.

Como se recuperar

  • Esperar passivamente — o template despausa sozinho ao fim das 3h/6h.
  • Editar e reenviar pra análise — mudar o conteúdo do template (ver Editar Template) reabre uma nova análise da Meta, o que também reresta o histórico de pausas.
Monitoramento em tempo real (Meta, não implementado por nós ainda): a Meta também dispara um webhook próprio, message_template_status_update, toda vez que a qualidade ou o status de um template muda — incluindo pausas. Hoje o painel mostra a nota de qualidade e o status puxando ao vivo da Meta sempre que você abre a tela de Templates (não há um histórico salvo aqui). Se no futuro for necessário reagir a pausas em tempo real (ex.: pausar uma campanha automaticamente), esse webhook é o caminho.

Upload de Exemplo (Mídia)

A Meta exige que templates com cabeçalho de mídia (imagem, vídeo ou documento) possuam um arquivo de exemplo. Use este endpoint para obter o header_handle necessário para a criação do template.

POST Obter Handle

POST /api/v1/media/upload_handle
POST https://api.covercut.com.br/api/v1/media/upload_handle

Request Body (Multipart)

CampoTipoDescrição
filefileO arquivo de imagem/vídeo (Máx 5MB).
fromstringO phone_number_id que será dono do template.
Exemplo de Resposta — 200 OK
{
  "success": true,
  "header_handle": "h/4_C_A0B1C2D3E4F5G6H7I8J9K0L1M2N3O4P5Q6"
}

Criar Template

Envia um novo template de mensagem para análise da Meta. Você deve especificar a categoria (MARKETING ou UTILITY) e os componentes (HEADER, BODY, FOOTER, BUTTONS).

POST Criar Template

O endpoint de criação aceita variáveis tanto Posicionais (ex: {{1}}, {{2}}) quanto Nomeadas (ex: {{nome}}, {{pedido_id}}).

Limites e Regras da Meta
  • Tamanhos máximos: Cabeçalho (60 caracteres), Corpo (1024 caracteres), Rodapé (60 caracteres).
  • Formato de Variáveis Nomeadas: Apenas letras minúsculas, números e sublinhados (ex: {{customer_name}}). Letras maiúsculas ou espaços resultarão em erro.
  • Exemplos: Todo componente com variável precisa do campo example. O formato do exemplo muda caso use variáveis nomeadas.
POST /api/v1/templates/create
curl -X POST https://api.covercut.com.br/api/v1/templates/create \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "123456789012345",
    "name": "boas_vindas",
    "category": "MARKETING",
    "language": "pt_BR",
    "components": [ ... ]
  }'
from é obrigatório se você tem mais de um número ativo

Assim como em Listar Templates, o from (phone_number_id) vai no body desta chamada, não como header nem query string. Se sua conta tem só um número, pode omitir. Se tem mais de um, omitir retorna:

{
  "success": false,
  "error": "Você tem mais de um número ativo — informe \"from\": \"{phone_number_id}\" pra escolher em qual número criar o template.",
  "code": "MULTIPLE_NUMBERS",
  "numbers": [
    { "phone_number_id": "111...", "display_phone_number": "+55 47 99999-0001", "client_ref": null },
    { "phone_number_id": "222...", "display_phone_number": "+55 47 99999-0002", "client_ref": "32" }
  ]
}

Exemplo Payload (Variáveis Posicionais - Padrão)

{
  "from": "123456789012345",
  "name": "boas_vindas",
  "category": "MARKETING",
  "language": "pt_BR",
  "components": [
    {
      "type": "BODY",
      "text": "Olá {{1}}, bem-vindo!",
      "example": {
        "body_text": [ ["João"] ]
      }
    }
  ]
}

Exemplo Payload (Variáveis Nomeadas)

Quando utilizar nomes nas variáveis, é obrigatório enviar "parameter_format": "NAMED" e o bloco example muda para um array de objetos.

{
  "from": "123456789012345",
  "name": "boas_vindas_nomeado",
  "category": "MARKETING",
  "language": "pt_BR",
  "parameter_format": "NAMED",
  "components": [
    {
      "type": "BODY",
      "text": "Olá {{nome_cliente}}, bem-vindo!",
      "example": {
        "body_text_named_params": [
          { "param_name": "nome_cliente", "example": "João" }
        ]
      }
    }
  ]
}

Botão de URL Dinâmica

Botões de link podem ter uma URL dinâmica: a URL do template termina com a variável {{1}} e o valor real é informado a cada disparo (útil para links de pedido, rastreio, fatura etc.).

Regras da Meta para URL dinâmica
  • 1 variável por botão, sempre no final da URL. Não é possível colocar a variável no meio (ex: site.com/{{1}}/x).
  • Numeração independente: o botão usa sempre {{1}}, mesmo que o corpo já use {{1}}, {{2}} etc. Cada componente conta as próprias variáveis. Isso vale inclusive para templates com variáveis nomeadas no corpo — o botão continua posicional ({{1}}).
  • Campo example: array com apenas o valor que substitui {{1}} (ex: ["pedido/123"]) — não a URL completa. É obrigatório para a análise da Meta.
  • São permitidos até 2 botões de URL por template.
Criar template com botão de URL dinâmica
{
  "from": "123456789012345",
  "name": "acompanhar_pedido",
  "category": "UTILITY",
  "language": "pt_BR",
  "components": [
    {
      "type": "BODY",
      "text": "Olá {{1}}, seu pedido saiu para entrega!",
      "example": {
        "body_text": [ ["João"] ]
      }
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "URL",
          "text": "Acompanhar pedido",
          "url": "https://seusite.com.br/{{1}}",
          "example": [ "pedido/123" ]
        }
      ]
    }
  ]
}

No disparo, envie o valor no componente button com sub_type: "url" — veja o exemplo Botão URL Dinâmico na seção Enviar Template. O valor enviado é apenas o sufixo (a Meta concatena ao prefixo fixo do template). Também é possível criar esse tipo de botão pela tela Templates do painel, escolhendo URL Dinâmica no tipo de link.

Cabeçalho com Imagem, Vídeo ou Documento (PDF)

Templates com cabeçalho de mídia usam "type": "HEADER", o campo format (IMAGE, VIDEO ou DOCUMENT) e um arquivo de exemplo obrigatório — a Meta não aprova o template sem ele. O arquivo de exemplo não é enviado direto no JSON: primeiro faça upload em Upload de Exemplo (Mídia) pra obter um header_handle, depois referencie-o em example.header_handle.

Criar template com cabeçalho de Imagem
{
  "from": "123456789012345",
  "name": "promo_com_imagem",
  "category": "MARKETING",
  "language": "pt_BR",
  "components": [
    {
      "type": "HEADER",
      "format": "IMAGE",
      "example": { "header_handle": ["4::YXBwbGljYXRpb24vcGRm..."] }
    },
    {
      "type": "BODY",
      "text": "Olá {{1}}, aproveite {{2}} de desconto essa semana!",
      "example": {
        "body_text": [ ["João", "20%"] ]
      }
    },
    {
      "type": "FOOTER",
      "text": "Responda SAIR para não receber mais promoções"
    }
  ]
}

Para Vídeo ou Documento (PDF), troque apenas o format do HEADER — o header_handle continua vindo do upload de exemplo:

Variações: cabeçalho de Vídeo e de Documento
// Vídeo
{ "type": "HEADER", "format": "VIDEO", "example": { "header_handle": ["4::YXBwbGljYXRpb24v..."] } }

// Documento (PDF)
{ "type": "HEADER", "format": "DOCUMENT", "example": { "header_handle": ["4::YXBwbGljYXRpb24vcGRm..."] } }

Cabeçalho de texto (sem mídia) usa "format": "TEXT" e um campo text no lugar de example.header_handle — pode ter no máximo 1 variável, ex: { "type": "HEADER", "format": "TEXT", "text": "Pedido {{1}}", "example": { "header_text": ["#4521"] } }.

O header_handle é só o exemplo de análise. Depois de aprovado, todo envio desse template precisa mandar a mídia real de novo no componente header (por link ou id) — veja Enviar Template → Templates com Mídia. A Meta não reaproveita o arquivo de exemplo cadastrado na criação.

Payload Completo: Cabeçalho + Corpo + Rodapé + Botões

Exemplo combinando tudo: cabeçalho de imagem, corpo com variáveis, rodapé fixo, um botão de Quick Reply e um botão de URL dinâmica. A ordem dos componentes não importa para a Meta, mas mantenha HEADER → BODY → FOOTER → BUTTONS por legibilidade.

Criar template com imagem + botões (quick reply + URL dinâmica)
{
  "from": "123456789012345",
  "name": "pedido_a_caminho",
  "category": "UTILITY",
  "language": "pt_BR",
  "components": [
    {
      "type": "HEADER",
      "format": "IMAGE",
      "example": { "header_handle": ["4::YXBwbGljYXRpb24vcGRm..."] }
    },
    {
      "type": "BODY",
      "text": "Olá {{1}}, seu pedido {{2}} saiu para entrega!",
      "example": {
        "body_text": [ ["João", "#4521"] ]
      }
    },
    {
      "type": "FOOTER",
      "text": "CoverCut - sua loja online"
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "QUICK_REPLY",
          "text": "Já recebi"
        },
        {
          "type": "URL",
          "text": "Acompanhar pedido",
          "url": "https://seusite.com.br/rastreio/{{1}}",
          "example": [ "4521" ]
        }
      ]
    }
  ]
}

Botão de telefone segue o mesmo padrão do URL, mas com "type": "PHONE_NUMBER" e o campo phone_number (formato E.164, ex: "+5547999999999") no lugar de url — é sempre fixo, não aceita variável.

Criar Template de Marketing (Simplificado)

Versão simplificada para criação rápida de templates promocionais com suporte nativo a botões de CTA.

POST Criar Marketing

POST /api/v1/templates/marketing/create
{
  "name": "oferta_especial_maio",
  "language": "pt_BR",
  "components": [
    {
      "type": "BODY",
      "text": "Temos uma oferta especial para você!"
    }
  ]
}

Editar Template

Atualiza o conteúdo de um template existente. Note que templates em uso podem exigir nova análise da Meta após a edição.

POST Editar Template

POST /api/v1/templates/update
curl -X POST https://api.covercut.com.br/api/v1/templates/update \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "123456789012345",
    "template_id": "111222333444555",
    "components": [
      {
        "type": "BODY",
        "text": "Novo texto do corpo do template"
      }
    ]
  }'
from é obrigatório se você tem mais de um número ativo

Vai no body, junto de template_id. Se sua conta tem só um número, pode omitir. Se tem mais de um, omitir retorna:

{
  "success": false,
  "error": "Você tem mais de um número ativo — informe \"from\": \"{phone_number_id}\" pra escolher em qual número editar o template.",
  "code": "MULTIPLE_NUMBERS",
  "numbers": [
    { "phone_number_id": "111...", "display_phone_number": "+55 47 99999-0001", "client_ref": null },
    { "phone_number_id": "222...", "display_phone_number": "+55 47 99999-0002", "client_ref": "32" }
  ]
}

Excluir Template

Remove permanentemente um template da sua conta WABA.

DELETE Excluir

DELETE /api/v1/templates/delete
curl -X DELETE https://api.covercut.com.br/api/v1/templates/delete \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "123456789012345",
    "name": "nome_do_template_a_excluir"
  }'
from é obrigatório se você tem mais de um número ativo

Vai no body, junto de name. Se sua conta tem só um número, pode omitir. Se tem mais de um, omitir retorna:

{
  "success": false,
  "error": "Você tem mais de um número ativo — informe \"from\": \"{phone_number_id}\" pra escolher de qual número excluir o template.",
  "code": "MULTIPLE_NUMBERS",
  "numbers": [
    { "phone_number_id": "111...", "display_phone_number": "+55 47 99999-0001", "client_ref": null },
    { "phone_number_id": "222...", "display_phone_number": "+55 47 99999-0002", "client_ref": "32" }
  ]
}

Baixar Mídia

Use este endpoint para baixar arquivos recebidos via Webhook. A API resolve a autenticação com a Meta automaticamente.

GET Obter Mídia

Endpoint
GET /api/v1/media/get?id={media_id}&from={phone_number_id}
Autenticação Obrigatória: Mesmo sendo uma requisição GET, você deve enviar os headers X-API-Key e X-API-Secret.

Parâmetro from (Recomendado): Identificador do número que recebeu a mídia. Obrigatório se você gerencia múltiplos números para garantir o uso do token correto. Pode ser o phone_number_id ou o número completo.

Adicione &mode=stream para receber o arquivo binário direto (download).

POST Fazer Upload de Mídia

Use este endpoint para enviar um arquivo para os servidores da Meta. O upload permite que você use o media_id para enviar mensagens, o que é mais rápido e confiável que o uso de links externos.

Opção 1: Formulário (multipart/form-data)

POST /api/v1/media/upload
Content-Type: multipart/form-data

file: [arquivo físico]
from: [phone_number_id - obrigatório se tiver mais de um número]

Opção 2: JSON com Base64 (Ideal para IA)

Se você possui o conteúdo do arquivo em Base64 (comum em IAs de áudio), pode enviar diretamente via JSON:

POST /api/v1/media/upload
Content-Type: application/json

{
  "file_base64": "T2dnUwACAAAAAAAA...",
  "filename": "audio.ogg",
  "filetype": "audio/ogg",
  "from": "phone_number_id" // Obrigatório se tiver mais de um número
}
Resposta
{
  "success": true,
  "message": "Upload concluído com sucesso",
  "media_id": "8451871321564"
}

Integração com n8n

Como Fazer Upload de Arquivos (POST)

Se você utiliza o n8n e precisa fazer o upload de um arquivo gerado em nós anteriores (como áudios do ElevenLabs), configure o seu nó HTTP Request da seguinte maneira:

  • Method: POST
  • Send Body: Habilitado
  • Body Content Type: Form-Data (muito importante!)
  • Specify Body: Using Fields Below
Atenção para Múltiplos Números:

Se você tiver mais de um número conectado e estiver fazendo o envio de arquivos (imagem, áudio, vídeo ou PDF) pelo n8n, é obrigatório incluir o campo from no body. Isso é necessário para que o sistema saiba de qual número está sendo feito o upload para a Meta.

Em Body Parameters, adicione os seguintes campos:

Nome: file
Tipo (Type): n8n Binary Data / Binary File
Input Data Field Name: data (ou o nome da propriedade onde está o arquivo)

Nome: from (Obrigatório se tiver mais de um número)
Tipo (Type): Text
Value: Seu ID do Número

Como Baixar Arquivos (GET)

Configure o nó HTTP Request com o Method GET, inclua seus Headers de autenticação (X-API-Key) e marque a opção Response Format como File para o n8n extrair a mídia perfeitamente.

Gestão de Grupos

Gerencie grupos onde o seu número de negócio é administrador ou membro. Você pode listar grupos, criar novos e gerenciar links de convite.

Múltiplos Números Conectados:

Todo endpoint desta página opera sobre um único número (uma WABA) por chamada, via o campo from (phone_number_id). Se sua conta tem só um número ativo, from é opcional. Se você tem mais de um número ativo (conta com vários números, ou revendedor com contas SaaS), omitir from retorna erro MULTIPLE_NUMBERS (com a lista de números disponíveis) em vez de escolher um sozinho.

Elegibilidade e Requisitos de Grupos:

A API de Grupos da Meta está disponível exclusivamente para contas comerciais oficiais (Official Business Account - OBA) na Cloud API tradicional. Ela NÃO está disponível para números que utilizam o aplicativo móvel WhatsApp Business ou que estão no modelo de Coexistência (Multi-solution Conversations).

Limites e Restrições Oficiais (Meta):
  • Capacidade: Máximo de 8 participantes por grupo.
  • Quantidade: Máximo de 10.000 grupos por número comercial.
  • Negócios: Apenas 1 número comercial Cloud API (WABA) por grupo.
  • Mensagens Não Suportadas: Chamadas (Calling), mensagens temporárias, visualização única (view-once), mensagens de autenticação, mensagens de comércio e mensagens interativas.
  • Ações Não Suportadas: Esconder lista de participantes por administradores, editar mensagens e deletar mensagens.
Fluxo de Convite (Invite-only):

Por políticas de privacidade da Meta, sistemas não podem adicionar usuários diretamente em grupos. Você deve seguir este fluxo:

  1. Crie o grupo usando o endpoint /api/v1/groups/create. A resposta do webhook conterá o evento group_lifecycle_update com o link de convite (invite_link).
  2. Crie um modelo de mensagem (Template) do tipo especial "Group invite upon request" no Gerenciador de Negócios e aguarde a aprovação da Meta.
  3. Envie esse template de convite para o usuário final via API informando os parâmetros do grupo (JID, nome e ícone).
  4. Quando o usuário aceitar o convite e entrar no grupo, o seu sistema receberá a confirmação através do webhook group_participants_update.

POST Criar Grupo

Cria um novo grupo no WhatsApp e define as configurações iniciais.

POST /api/v1/groups/create
{
  "from": "PHONE_NUMBER_ID", // Opcional
  "subject": "Nome do Grupo",
  "description": "Descrição do grupo (opcional)",
  "join_approval_mode": "auto_approve" // "auto_approve" ou "approval_required"
}

GET / POST Link de Convite

Obtenha ou resete o link de convite do grupo.

Obter Link — GET

GET /api/v1/groups/invite_link
GET /api/v1/groups/invite_link?group_id=GROUP_ID&from=PHONE_NUMBER_ID

Resetar Link — POST

POST /api/v1/groups/invite_link
{
  "from": "PHONE_NUMBER_ID", // Opcional
  "group_id": "ID_DO_GRUPO"
}

POST Enviar para Grupo

Funcionalidade oficial da Meta para comunicação em grupos. Envie mensagens de texto livre, arquivos ou templates aprovados. Lembre-se de definir recipient_type: group.

POST /api/v1/messages/send — Grupo (Texto)
{
  "from": "123456789012345",
  "recipient_type": "group",
  "to": "120363214587@g.us",
  "type": "text",
  "text": { "body": "Olá grupo!" }
}
POST /api/v1/messages/send — Grupo (Template de Convite)
{
  "from": "123456789012345",
  "recipient_type": "individual",
  "to": "5547999999999",
  "type": "template",
  "template": {
    "name": "seu_template_convite",
    "language": { "code": "pt_BR" },
    "components": [
      {
        "type": "body",
        "parameters": [
          {
            "type": "group_invite_link",
            "group_invite_link": {
              "group_jid": "120363214587@g.us",
              "group_name": "Grupo Oficial Covercut",
              "group_icon": "8451871321564"
            }
          }
        ]
      }
    ]
  }
}
Modelo de Faturamento de Grupos (PMP):

O envio de mensagens para grupos segue a precificação baseada em entrega individual (Per-Message Pricing):

  • Você é cobrado por cada mensagem que for efetivamente entregue a um participante do grupo (e não por um custo fixo único).
  • O valor unitário por participante segue a tabela correspondente do país e operadora do participante (código DDI).
  • Sem descontos de volume: Mensagens de utilidade para grupos (Group utility messages) não qualificam para faixas de desconto por volume comercial na Meta.
Janelas de Serviço em Grupos:

Diferente de conversas 1:1 onde cada usuário tem sua própria janela de 24h:

  • Se qualquer participante enviar uma mensagem no grupo, uma janela de serviço de 24h é aberta para todo o grupo.
  • Dentro dessa janela de 24h, você pode enviar mensagens de texto livre (freeform) e templates de utilidade para o grupo totalmente grátis.
  • Mensagens de Marketing e Autenticação são sempre pagas, mesmo que enviadas dentro da janela ativa.
Métricas de Performance de Templates:

A Meta não disponibiliza métricas de performance (como taxa de leitura ou entrega) para modelos de mensagens (Templates) que forem disparados em grupos. Se você precisar rastrear esses dados, crie templates separados e exclusivos para envios de grupos.

GET Listar Grupos

Retorna uma lista de todos os grupos em que o número de telefone do BSP é membro.

GET /api/v1/groups/list
GET /api/v1/groups/list

GET Informações do Grupo

Retorna os detalhes de um grupo (assunto, descrição, participantes, modo de aprovação, etc).

GET /api/v1/groups/info
GET /api/v1/groups/info?group_id=GROUP_ID&from=PHONE_NUMBER_ID

DELETE Excluir Grupo

Exclui o grupo permanentemente da sua conta do WhatsApp Business.

DELETE /api/v1/groups/delete
{
  "from": "PHONE_NUMBER_ID", // Opcional
  "group_id": "ID_DO_GRUPO"
}

Códigos de Erro de Grupos

Abaixo estão listados os códigos de erro específicos retornados pela Meta em operações da Groups API. Utilize esta referência para tratamento de exceções no seu sistema.

Código Erro / Status HTTP Descrição / Solução
131020 Bad Group (400) Não é possível enviar mensagens para grupos que possuem apenas 1 participante (ex: apenas o seu próprio número de negócio). Adicione mais membros antes de enviar mensagens.
131041 Group Unknown (400) O grupo especificado não foi encontrado. Certifique-se de que o group_id (JID) está correto ou se o seu número comercial ainda é membro desse grupo.
131059 Invalid Cursor (400) O cursor de paginação fornecido expirou ou é inválido. Reinicie a paginação da lista de grupos desde o início.
131201 Partial Success (206) A requisição de participantes obteve sucesso parcial. Verifique a lista detalhada de resposta para identificar quais operações ou números de telefone falharam.
131202 Duplicate Participant (400) Foram informados participantes duplicados no array de entrada. Remova as duplicatas da sua requisição.
131204 Participant Overlimit (400) O número de participantes excede o limite oficial permitido pela Meta (máximo de 8 participantes por grupo).
131207 Group Suspended (403) O grupo foi suspenso automaticamente pela Meta por violação das políticas de uso comercial do WhatsApp.
131208 Rate Limit Hit (429) O limite de operações de grupo por minuto do seu número foi atingido. Implemente um fluxo de backoff e reduza a frequência das requisições.
131209 Invalid Aspect Ratio (400) A proporção da imagem do ícone do grupo é inválida. A imagem deve ser estritamente quadrada (proporção 1:1).
131210 Image Too Small (400) A imagem enviada para o ícone do grupo é muito pequena. A largura e altura mínimas devem ser de 192px.
131211 Limit Reached (400) O limite de criação de grupos do seu número de telefone foi atingido (máximo de 10.000 grupos ativos).
131212 Not a Participant (400) O participante especificado não faz parte do grupo informado.
131213 Invalid Request (400) A solicitação de participação (join request) informada não existe ou já foi processada.
131214 Creation Disabled (400) A criação de grupos está temporariamente desativada para este número devido a alto volume de mensagens de marketing enviadas na janela de suporte nos últimos 7 dias.
131215 Not Eligible (400) O número de telefone não é elegível para utilizar a API de Grupos (ex: número em modo sandbox/teste, WhatsApp Business App móvel ou onboarded em modelo de coexistência). O número precisa ser uma Official Business Account (OBA).

Integração ChatWoot (Auto-Config)

Nossa integração oficial permite conectar seu número ao Chatwoot em segundos, sem a necessidade de ferramentas externas como n8n.

Estilo Evolution API: O sistema detecta suas configurações e cria automaticamente a Caixa de Entrada (Inbox) e os Webhooks necessários dentro do seu Chatwoot.

1. O que você precisa:

  • URL da Instância: Ex: https://chat.suaempresa.com
  • ID da Conta: O número que aparece após /accounts/ na URL do Chatwoot.
  • Token de Acesso: Encontrado em Configurações de Perfil > Access Token no seu Chatwoot.

2. Ativando a Integração:

  1. Acesse seu Dashboard no BSP → Meus Números.
  2. Clique em Configurar ChatWoot no número desejado.
  3. Preencha os dados e clique em [ Conectar e Configurar ].
URL que o sistema usará para o Webhook do Chatwoot
https://api.covercut.com.br/api/adapters/chatwoot.php

O BSP configurará essa URL automaticamente no seu Chatwoot. Sempre que um atendente responder por lá, nós receberemos a notificação e enviaremos para o WhatsApp do cliente.

Como Funciona o Fluxo

Sentido Ação do Sistema
WhatsApp → Chatwoot Quando o cliente manda um "Oi", o BSP cria o contato e a conversa no Chatwoot e empurra a mensagem via API.
Chatwoot → WhatsApp Quando o atendente responde no Chatwoot, ele dispara o Webhook para nosso adapter, que envia para a Meta.

Sincronizar Envios via API

Por padrão, mensagens enviadas via API (como notificações automáticas ou bots externos) não aparecem no Chatwoot, pois o Chatwoot só recebe eventos de mensagens que "passam" por ele ou chegam pelo Webhook.

Endpoint Sincronizado: Use este endpoint especial para garantir que mensagens enviadas pelo seu sistema também apareçam no histórico do Chatwoot.
POST /api/v1/messages/send_sync

Este endpoint aceita os mesmos parâmetros do /messages/send, mas realiza uma ação adicional:

  • Envia a mensagem ao cliente via WhatsApp.
  • Cria/Localiza a conversa no Chatwoot.
  • Insere a mensagem como "Saída" (Agent Message) no painel do Chatwoot.
Exemplo de Requisição Sincronizada
{
  "to": "5547999999999",
  "type": "text",
  "text": {
    "body": "Seu pedido #123 foi enviado! 🚚"
  }
}

Com isso, seus atendentes terão o histórico completo da conversa, incluindo as automações disparadas pelo seu sistema.

Contas SaaS (White-label)

Visão Geral

Estes endpoints permitem que você, como revendedor, crie e gerencie Contas SaaS (sub-contas) de forma automatizada — exatamente o que a tela Contas SaaS do seu dashboard faz, porém via API. É ideal para embutir a conexão de WhatsApp diretamente dentro do seu próprio produto.

Dois jeitos de organizar suas Contas SaaS — escolha um (ou misture, se fizer sentido para o seu produto):
  • Link Direto compartilhado (o mais usado): você cria UMA conta SaaS com um limite alto (ex.: 100 conexões), pega o direct_link/integration_snippet uma única vez e embute no seu produto. Todos os seus clientes clicam no MESMO botão. Identificação de quem é quem: client_ref (veja abaixo).
  • Uma sub-conta por cliente: você chama POST /api/v1/saas/create dinamicamente para cada cliente seu, com um external_id próprio. Cada um tem seu próprio link/limite/status, isolados. Mais isolamento, mais chamadas de API.

Link Direto compartilhado + client_ref (padrão mais usado)

Se o botão "Conectar WhatsApp" do seu produto é o mesmo link/script para todos os seus clientes, o external_id da conta não ajuda a distinguir quem clicou — ele identifica a CONTA (que é uma só, compartilhada), não a pessoa. Para isso existe o client_ref: um parâmetro que o seu próprio JavaScript acrescenta na URL, na hora do clique, usando o ID que você já tem do usuário logado no seu produto. Não precisa chamar nenhuma API nossa nesse momento — é só concatenar a URL.

Fluxo — do clique do seu cliente até você identificar quem conectou
1. Você cria a conta SaaS UMA VEZ (painel ou API), define o limite (ex.: 100),
   copia o script pronto (integration_snippet) e coloca no seu produto.
   Isso não muda mais — é feito uma vez só.

2. Cliente seu #32 (já logado no SEU produto) clica em "Conectar WhatsApp"

3. O PRÓPRIO script (que você colou) chama:
     connectWhatsApp(usuarioAtual.id)     // ex.: connectWhatsApp(32)

   Isso acrescenta &client_ref=32 na URL antes de abrir a janela —
   tudo no seu frontend, sem chamar nenhuma API nossa aqui.

4. Cliente #32 conecta o WhatsApp dele normalmente na nossa janela.

5. Você descobre que foi o cliente #32, de duas formas (pode usar as duas):

   a) Na hora, no navegador: o evento WA_CONNECT_SUCCESS volta com
      event.data.client_ref === "32"

   b) No seu backend, a qualquer momento: webhook (evento em tempo real)
      ou GET /api/v1/saas/list?client_ref=32 (consulta pontual) —
      ambos dizem exatamente qual número o cliente 32 conectou.

O client_ref é gravado no NÚMERO conectado (não na conta) — porque números diferentes da mesma conta compartilhada podem pertencer a clientes seus diferentes. É por isso que GET /api/v1/saas/list devolve client_ref dentro de cada item de numbers, e não no nível da conta.

client_ref não é uma trava de segurança:

Qualquer um que tenha o link pode, em teoria, editar a URL manualmente e informar outro valor de client_ref. Isso não vaza dados entre REVENDEDORES diferentes (cada conta SaaS já é isolada por você), mas se for importante garantir que o client_ref realmente corresponde a quem está logado, valide isso no SEU backend (ex.: assine o valor, ou confirme via sessão) antes de confiar cegamente nele para decisões sensíveis.

Uma sub-conta por cliente + external_id (alternativa)

Se você prefere isolar cada cliente numa conta própria (limite, status e link individuais), chame POST /api/v1/saas/create dinamicamente, no momento em que aquele cliente específico (que seu backend já sabe quem é) inicia a conexão — passando o nome dele e o external_id. A chamada é idempotente por external_id: repetir para o mesmo cliente não cria duplicata, só reemite um link novo (veja reused_existing na resposta).

Sem código agora?

Para poucos clientes, dá pra fazer 100% manual pelo painel: crie a conta em Dashboard → Contas SaaS → Nova Conta SaaS e envie o link gerado individualmente (e-mail, WhatsApp etc.). Funciona, só não escala sozinho para muitos clientes.

Autenticação e Escopo

Todos os endpoints de SaaS usam as suas credenciais de API (as mesmas dos endpoints de mensagens). A sub-conta criada fica automaticamente vinculada à sua conta, e você só consegue listar, alterar ou excluir sub-contas que pertençam a você.

Headers Requeridos
X-API-Key: sua_chave_de_api
X-API-Secret: seu_segredo_de_api
Content-Type: application/json
Segurança:

As credenciais são obrigatórias no cabeçalho HTTP (não é aceita sessão de navegador). Também se aplicam a whitelist de IP e o rate limit do seu plano, iguais aos demais endpoints da API.

POST Criar Conta SaaS

Cria uma nova sub-conta (cliente SaaS) e retorna os links de conexão prontos para uso. O único campo obrigatório é o company_name. Se você usa o modelo de Link Direto compartilhado, chama este endpoint só UMA VEZ (não por cliente) — a identificação de cada cliente que clicar depois é feita pelo client_ref, um parâmetro de URL usado na hora da conexão, não um campo deste body. Veja a seção acima.

Marca Padrão: os campos de personalização (logo, cores, textos, layout) abaixo são todos opcionais porque seguem uma cascata: valor enviado nesta chamada → Marca Padrão salva em Dashboard → Contas SaaS → "Marca Padrão" → default padrão da página de conexão. Configure sua marca uma vez no painel e não precisa repetir logo/cores/textos em toda chamada da API.
Parâmetros (JSON Body)
ParâmetroObrigatórioDescrição
company_nameSimNome do cliente/empresa SaaS. Máximo de 100 caracteres.
number_limitNãoMáximo de números que essa conta poderá conectar. Padrão 1. Não pode ultrapassar o limite disponível no seu plano.
external_idRecomendadoReferência do SEU sistema para esta sub-conta (ex.: o ID do cliente no seu próprio banco). Devolvida em todas as respostas e no webhook de conexão — é como você correlaciona o nosso sub_customer_id com o seu cliente. Idempotente: se já existe uma sub-conta sua com este external_id, a chamada não cria duplicata — reaproveita a existente e só emite um link novo. Isso torna seguro chamar este endpoint toda vez que o mesmo cliente clicar em "Conectar", sem se preocupar em checar antes se ele já tem conta. Máximo de 191 caracteres.
logoNãoURL http(s) do logo exibido na página de conexão (white-label).
primaryNãoCor do botão/ícone em hexadecimal (ex.: #25D366).
bgNãoCor de fundo (cartão e página) em hexadecimal (ex.: #ffffff). Padrão é branco.
text_colorNãoCor do texto em hexadecimal (ex.: #111b21).
titleNãoTítulo customizado da página. Padrão "Conectar WhatsApp". Máximo de 100 caracteres.
textNãoTexto de instrução customizado, substitui o texto padrão (incluindo a saudação). Máximo de 500 caracteres.
hide_greetingNãotrue remove a saudação "Olá, [nome do cliente]!" do texto padrão. Sem efeito se text for enviado.
btn_textNãoTexto do botão de conexão. Padrão "Conectar Agora". Máximo de 50 caracteres.
redirectNãoURL http(s) para onde o cliente é enviado após conectar com sucesso. Só se aplica quando layout é full.
autostartNãotrue (padrão) abre o pop-up da Meta automaticamente ao carregar o link.
layoutNãopopup (padrão) ou full. Use popup ao embutir como botão; use full para redirecionar a página inteira e depois voltar via redirect.
Exemplo — cURL
curl -X POST https://api.covercut.com.br/api/v1/saas/create \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api" \
  -H "Content-Type: application/json" \
  -d '{
    "company_name": "Cliente Final do SaaS",
    "number_limit": 1,
    "external_id": "seu-id-interno-123",
    "logo": "https://seu-saas.com/logo.png",
    "primary": "#0A66C2",
    "bg": "#f5f7fa",
    "title": "Conectar WhatsApp",
    "hide_greeting": true,
    "redirect": "https://seu-saas.com/painel",
    "autostart": true,
    "layout": "popup"
  }'

Resposta

JSON — 200 OK
{
  "success": true,
  "message": "Cliente SaaS criado com sucesso!",
  "sub_customer_id": 118,
  "external_id": "seu-id-interno-123",
  "reused_existing": false,
  "company_name": "Cliente Final do SaaS",
  "number_limit": 1,
  "token": "7a9af1b339abcf6395a28b92ef4fc25f...",
  "magic_link": "https://api.covercut.com.br/conectar.php?token=7a9af1b3...",
  "direct_link": "https://api.covercut.com.br/conectar.php?token=7a9af1b3...&autostart=1&logo=...&primary=%230A66C2&redirect=...&layout=popup",
  "integration_snippet": "function connectWhatsApp() { ... }"
}
Campos da Resposta
CampoTipoDescrição
sub_customer_idintID da sub-conta. Guarde-o: é usado para listar, ativar/inativar e excluir.
external_idstring|nullEco do external_id que você enviou (ou null se não enviou nenhum).
reused_existingbooltrue quando já existia uma sub-conta com este external_id e ela foi reaproveitada (só um token novo foi gerado). false quando uma sub-conta nova foi criada.
tokenstringToken de onboarding único do link de conexão.
magic_linkstringLink de conexão simples (sem personalização).
direct_linkstringLink de conexão white-label (com logo, cores, textos, redirect e layout). É este que vira o botão no seu SaaS.
integration_snippetstringFunção JavaScript pronta que abre a conexão (pop-up ou página inteira, conforme layout) e escuta o retorno.

Embutindo o botão no seu SaaS

Cole a função recebida em integration_snippet na sua página e chame-a no clique de um botão. No modo popup, ela abre o direct_link em uma janela e escuta três eventos via postMessage:

EventoQuando ocorre
WA_CONNECT_SUCCESSO cliente concluiu a conexão. Vem com customer_id (nosso ID da conta), external_id (se a conta tiver um) e client_ref (o valor que você passou ao chamar connectWhatsApp(clientRef) — é ele que diz qual dos SEUS clientes é este, no cenário de link compartilhado).
WA_CONNECT_CLOSEDO cliente fechou a janela ou cancelou o diálogo da Meta sem concluir. Vem com reason (window_closed, login_cancelled ou embedded_signup_incomplete — autorizou o OAuth mas cancelou antes de escolher a WABA/número) e client_ref.
WA_CONNECT_ERRORFalha ao finalizar a conexão (erro de rede ou da Meta). Vem com message e client_ref.
Trate o fechamento sem concluir:

Antes, se o cliente fechasse a janela sem terminar, seu sistema não recebia nenhum aviso. Agora escute também WA_CONNECT_CLOSED e WA_CONNECT_ERROR para mostrar uma mensagem elegante em vez de deixar o usuário sem retorno.

HTML + JS — Botão de conexão
<button onclick="connectWhatsApp(usuarioAtual.id)">Conectar WhatsApp</button>

<script>
function connectWhatsApp(clientRef) {
  // clientRef = o ID do SEU cliente que está clicando AGORA. Se este link é
  // compartilhado entre vários dos seus clientes, é isso que diz depois
  // qual deles conectou qual número.
  let url = "COLE_AQUI_O_direct_link";
  if (clientRef) url += "&client_ref=" + encodeURIComponent(clientRef);

  const w = 550, h = 700;
  const left = (screen.width / 2) - (w / 2), top = (screen.height / 2) - (h / 2);
  const win = window.open(url, "ConnectWhatsApp", "width=" + w + ",height=" + h + ",top=" + top + ",left=" + left);

  if (!win || win.closed || typeof win.closed === "undefined") {
    alert("Não foi possível abrir a janela de conexão. Desative o bloqueador de pop-up.");
    return;
  }

  window.addEventListener("message", (event) => {
    if (event.data.type === "WA_CONNECT_SUCCESS") {
      console.log("Conectado!", event.data.client_ref);
      location.reload();
    } else if (event.data.type === "WA_CONNECT_CLOSED") {
      alert("A janela foi fechada antes de concluir a conexão. Tente novamente.");
    } else if (event.data.type === "WA_CONNECT_ERROR") {
      alert("Não foi possível concluir a conexão: " + event.data.message);
    }
  }, false);
}
</script>
Limite do plano:

Se a soma dos limites já alocados às suas contas SaaS ultrapassar o limite do seu plano, a criação retorna erro. Planos com excedente habilitado (overage) permitem ultrapassar, e o excedente é cobrado no fechamento do ciclo.

Webhook de servidor (recomendado)

O postMessage do exemplo acima só funciona se o navegador do cliente final ainda estiver com a janela aberta. Se ele fechar antes de o JS rodar, ou se o seu botão não estiver embutido num navegador (ex.: fluxo full com redirecionamento), você não recebe o evento. Para não depender disso, configure um webhook de servidor — nosso backend avisa o seu diretamente, sem depender do navegador do cliente.

Configure em Dashboard → Contas SaaS → "Webhook de Notificação" (botão no topo da página). Entrega assíncrona com retry automático (mesma fila dos webhooks de mensagem), então mesmo que sua URL fique fora do ar por um tempo, o evento não se perde.

Quando qualquer uma das suas contas SaaS conecta um número com sucesso, o payload enviado é:

JSON — POST no seu webhook
{
  "event": "saas_customer_connected",
  "timestamp": "2026-08-14T10:30:00-03:00",
  "customer_id": 42,
  "title": "Novo cliente conectou via SaaS!",
  "message": "\"Cliente Final do SaaS\" conectou 1 número(s): +55 47 99999-0001.",
  "data": {
    "sub_customer_id": 118,
    "external_id": "seu-id-interno-123",
    "company_name": "Cliente Final do SaaS",
    "waba_id": "123456789012345",
    "numbers": [
      {
        "phone_number_id": "123456789012345",
        "display_phone_number": "+55 47 99999-0001",
        "client_ref": "32"
      }
    ]
  }
}

No cenário de Link Direto compartilhado (o mais comum), é o client_ref dentro de cada item de numbers que diz qual dos seus clientes conectou aquele número específico — vem do ?client_ref= que o seu próprio JS passou na URL. Já external_id identifica a CONTA (só faz sentido quando você usa uma sub-conta por cliente). Os dois podem vir null se você não os usa.

Assinatura:

A requisição vem com os headers X-BSP-Signature (HMAC-SHA256 do corpo com o secret configurado), X-BSP-Timestamp e X-BSP-Event. Valide a assinatura antes de confiar no payload, do mesmo jeito que faz para os webhooks de mensagens.

GET Listar Contas SaaS

Retorna todas as suas sub-contas, com o limite, o status, o external_id e os números conectados de cada uma (cada número já vem com o client_ref de quem o conectou). Aceita GET ou POST. É a forma mais simples de checar, a qualquer momento, qual dos SEUS clientes já conectou qual número — sem depender de webhook nem de guardar nada extra do seu lado.

Dois filtros opcionais, para consulta pontual (query string ou body no POST):

FiltroUso
external_idRetorna só a conta com esse ID — cenário de "uma sub-conta por cliente".
client_refRetorna só os números conectados com esse valor — cenário de "Link Direto compartilhado". Mais usado.
Exemplo — "o cliente 32 já conectou algum número?"
curl "https://api.covercut.com.br/api/v1/saas/list?client_ref=32" \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api"
Exemplo — listar tudo
curl https://api.covercut.com.br/api/v1/saas/list \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api"

Resposta

JSON — 200 OK
{
  "success": true,
  "clients": [
    {
      "id": 118,
      "external_id": null,
      "name": "Minha Conta SaaS",
      "number_limit": 100,
      "status": "active",
      "created_at": "2026-07-07 12:00:00",
      "pending_token": "7a9af1b3...",
      "numbers": [
        {
          "display_phone_number": "+55 47 99999-0001",
          "phone_number_id": "123456789012345",
          "status": "active",
          "client_ref": "32"
        }
      ]
    }
  ]
}
Dica:

O campo id de cada item é o sub_customer_id usado nos endpoints de ativar/inativar e excluir. Já o phone_number_id dentro de cada item de numbers[] é o que você usa em Ativar / Suspender / Desconectar Número pra agir num número específico dessa sub-conta. No cenário de Link Direto compartilhado (o mais comum), você vai filtrar por client_ref, não por external_id — é normal external_id vir null aqui, já que a conta é uma só, compartilhada. O pending_token aparece quando existe um link de conexão ainda não utilizado.

POST Ativar / Inativar Conta SaaS

Altera o status de uma sub-conta. Envie status explicitamente para definir o valor, ou omita-o para alternar entre ativo e inativo.

Parâmetros (JSON Body)
ParâmetroObrigatórioDescrição
sub_customer_idSimID da sub-conta (retornado em create / list).
statusNãoactive ou suspended. Se omitido, o status atual é invertido.
Exemplo — Inativar
curl -X POST https://api.covercut.com.br/api/v1/saas/status \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api" \
  -H "Content-Type: application/json" \
  -d '{ "sub_customer_id": 118, "status": "suspended" }'

Resposta

JSON — 200 OK
{
  "success": true,
  "message": "Cliente SaaS inativado com sucesso!",
  "sub_customer_id": 118,
  "status": "suspended"
}
Efeito:

Uma conta inativa (suspended) fica bloqueada, mas continua na sua lista e mantém o limite alocado. Para reativar, envie "status": "active".

POST Ativar / Suspender / Desconectar Número

O endpoint acima age na sub-conta inteira. Este age em um único número de uma sub-conta — útil quando o cliente final tem mais de um número conectado (number_limit > 1) e você quer bloquear só um deles, sem afetar os outros nem a conta como um todo.

Três ações, dois níveis de reversibilidade:
  • suspend → bloqueia o envio imediatamente, mas mantém o token e a conexão com a Meta intactos. Reversível a qualquer momento com activate.
  • activate → reverte um suspend, volta a permitir envio.
  • disconnectdestrutivo. Remove a inscrição do número na Meta e apaga o token local. Para voltar a funcionar, o cliente final precisa reconectar o WhatsApp do zero (gere um novo link em Criar Conta SaaS ou reenvie o direct_link existente).
Isolamento entre revendedores:

Este endpoint só enxerga números de sub-contas suas (parent_id igual à sua conta) — nunca os seus próprios números, nem os de outro revendedor. Identificar um número que não é seu (ou que é seu, e não de um cliente) retorna 404 como se não existisse, sem detalhar o motivo.

Parâmetros (JSON Body)
ParâmetroObrigatórioDescrição
phone_number_idSim (ou number_id)O phone_number_id do número, retornado em numbers[] na resposta de Listar Contas SaaS.
number_idSim (ou phone_number_id)Alternativa: o id interno do número, caso você já o guarde do seu lado.
actionSimsuspend, activate ou disconnect.
reasonNãoSó usado em suspend: motivo exibido no painel do cliente. Padrão: "Suspenso via API pelo revendedor".
Exemplo — Suspender um número
curl -X POST https://api.covercut.com.br/api/v1/saas/number_status \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number_id": "123456789012345",
    "action": "suspend",
    "reason": "Fatura em atraso"
  }'

Resposta

JSON — 200 OK
{
  "success": true,
  "message": "Número suspenso com sucesso",
  "number_id": 42,
  "phone_number_id": "123456789012345",
  "sub_customer_id": 118,
  "status": "suspended"
}

Para reverter, chame de novo com "action": "activate". Para desconectar de vez (destrutivo), use "action": "disconnect" — a resposta é igual, só com "status": "disconnected" (e um array warnings se a Meta recusar a desinscrição, o que não impede a desconexão local).

Regras entre ações:
  • Não é possível suspend um número já disconnected — a única saída dali é reconectar.
  • activate só funciona sobre um número suspended. Sobre um número disconnected retorna erro RECONNECT_REQUIRED — reativar sem passar pela Meta deixaria o token inválido e o envio falharia silenciosamente.
  • disconnect é idempotente: chamar de novo num número já desconectado retorna sucesso sem repetir a chamada à Meta.

POST Excluir Conta SaaS

Exclui uma sub-conta. É uma exclusão segura (soft-delete): os números vinculados são desconectados, o limite é liberado de volta ao seu plano e a conta some da sua lista.

Parâmetros (JSON Body)
ParâmetroObrigatórioDescrição
sub_customer_idSimID da sub-conta a ser excluída.
Exemplo — cURL
curl -X POST https://api.covercut.com.br/api/v1/saas/delete \
  -H "X-API-Key: sua_chave_de_api" \
  -H "X-API-Secret: seu_segredo_de_api" \
  -H "Content-Type: application/json" \
  -d '{ "sub_customer_id": 118 }'

Resposta

JSON — 200 OK
{
  "success": true,
  "message": "Cliente SaaS excluído com sucesso!",
  "sub_customer_id": 118
}
Atenção:

A exclusão desconecta imediatamente todos os números da sub-conta. A ação não pode ser desfeita — para conectar novamente, crie uma nova conta SaaS.

Códigos de Erro

HTTPSituação
400Parâmetro obrigatório ausente ou inválido (ex.: company_name vazio, URL de logo inválida, limite do plano excedido).
401Headers X-API-Key / X-API-Secret ausentes ou credenciais inválidas.
404Sub-conta não encontrada ou não pertence a você.
405Método HTTP não permitido para o endpoint.
429Limite de requisições do plano excedido. Aguarde 1 minuto.

Business-Scoped User IDs (BSUID) — Junho 2026

O WhatsApp introduz BSUID (Business-Scoped User ID): um identificador único por usuário-negócio que permite suporte a usernames (nomes de usuário), similar ao Instagram. O BSUID é adicionado aos webhooks — a Meta NÃO descontinua o número de telefone. O número continua presente quando o usuário não adotou um nome de usuário, ou quando se aplicam as condições de retrospectiva de 30 dias / lista de contatos. Ele é omitido apenas quando o usuário adota um nome de usuário e o número não pode ser incluído por essas condições.

Timeline

DataEventoImpacto
Abril 2026BSUIDs começam a aparecerWebhooks incluem from_user_id
29 Junho 2026Nome de usuário comercialEmpresas podem reservar/adotar username
Julho 2026API aceita BSUID em envio + botão REQUEST_CONTACT_INFOPode enviar para BSUID e/ou número
Número de telefone continua disponível. Ao enviar, você pode usar to (número) e/ou recipient (BSUID); se ambos forem enviados, o número tem precedência.

O que é um BSUID?

Exemplos de BSUID
US.13491208655302741918      ← Formato: CC. + até 128 alfanuméricos
US.ENT.11815799212886844830  ← BSUID principal (parent), prefixo CC.ENT.
joao.silva                   ← Username (exibido no app, após adoção)

Propriedades do BSUID

  • Único por negócio: Cada contato tem um BSUID diferente por empresa.
  • Persistente: Não muda mesmo se o usuário mudar de número ou desabilitar username.
  • Prefixado: Começa com código do país (ex: BR, US, ES).
  • Privado: A Meta garante privacidade — números não são expostos em BSUIDs.

Campos Novos nos Webhooks (Maio 2026+)

CampoLocalizaçãoTipoDescrição
from_user_idmessagesstringBSUID do remetente em mensagens inbound
recipient_user_idstatusesstringBSUID do destinatário em webhooks de status
to_user_idcallsstringBSUID do destinatário em eventos de chamada
to_parent_user_idcallsstringParent BSUID (para contexto de relacionamento)
user_idcontactsstringBSUID do contato
usernamecontactsstringNome de usuário WhatsApp do contato

Exemplo — Transição Junho 2026

Durante a transição, webhooks incluem ambos número e BSUID:

Webhook de Mensagem com Número + BSUID
{
  "from_number": "5547999999999",        ← número (omitido se username adotado e nº indisponível)
  "contact": {
    "wa_id": "5547999999999",            ← número (omitido se username adotado e nº indisponível)
    "user_id": "US.13491208655302741918", ← BSUID (novo!)
    "username": "joao.silva",            ← username (novo!)
    "name": "João Silva"
  }
}

Implementação Recomendada

Aceite BSUID como identificador, mantendo o número quando presente. O número não é descontinuado — apenas pode faltar para usuários com nome de usuário.
  1. Capture BSUID em webhooks:
    • Use user_id / from_user_id como identificador estável do contato
    • Mantenha o wa_id (número) quando vier — ele continua sendo enviado na maioria dos casos
  2. Armazene username: Novos contatos incluem username — capture para exibição amigável
  3. Envio de mensagens: Use to (número) e/ou recipient (BSUID). Com ambos, o número tem precedência.
  4. Solicite o número quando precisar: Se o usuário ocultou o número, peça-o com o botão REQUEST_CONTACT_INFO (abaixo).

Solicitar o número do usuário — REQUEST_CONTACT_INFO (Julho 2026)

Quando um usuário adota nome de usuário, o número pode não vir nos webhooks. Para pedi-lo, use o botão REQUEST_CONTACT_INFO. Ao tocar, o usuário compartilha o número e dispara um webhook de contacts com o telefone (e o vcard, se ele compartilhar um contato diretamente).

O texto do botão é opcional, o rótulo não é personalizável e o botão não leva parâmetros ao disparar o template.

Como mensagem interativa

POST /api/v1/messages/send
{
  "to": "5547999999999",            // ou "recipient": "<BSUID>"
  "type": "interactive",
  "interactive": {
    "type": "request_contact_info",
    "body": { "text": "Para concluir, compartilhe seu número:" },
    "action": { "name": "request_contact_info" }
  }
}

Como botão de template (utility/marketing)

Inclua o botão na criação do template (POST /api/v1/templates). No envio do template, não passe parâmetros para esse botão.

Componente de botão no template
{
  "type": "buttons",
  "buttons": [
    { "type": "REQUEST_CONTACT_INFO", "text": "Compartilhar contato" }
  ]
}

Webhook recebido ao compartilhar o contato

Evento de contacts
{
  "event": "message",
  "message": {
    "type": "contacts",
    "from_user_id": "US.13491208655302741918",
    "contacts": [
      {
        "origin": "contact_request",   // ou "other" (compartilhou um contato direto)
        "vcard": "...",                // presente só quando origin = "other"
        "phones": [
          { "phone": "+5547999999999", "wa_id": "5547999999999", "type": "..." }
        ]
      }
    ]
  }
}

Nome de usuário comercial — API (Julho 2026)

Reserve, consulte e remova o nome de usuário comercial (@) de um número. O nome de usuário não oculta o número da empresa no app. Endpoints autenticados com X-API-Key / X-API-Secret; parâmetro from = phone_number_id (opcional só se você tiver um único número ativo — com mais de um, é obrigatório, senão a API retorna erro MULTIPLE_NUMBERS).

Método / EndpointDescrição
GET /api/v1/usernameRetorna { username, status } (status: approved ou reserved).
POST /api/v1/usernameAdota/altera. Corpo: { "username": "...", "transfer_action": "none|force_transfer" }. Use force_transfer para mover um nome já usado em outro número do seu portfólio (erro 147005 sem ele).
DELETE /api/v1/usernameRemove o nome de usuário do número.
GET /api/v1/username_suggestionsLista os nomes reservados para você (maior chance de aprovação).
POST /api/v1/username
{
  "from": "<phone_number_id>",     // opcional
  "username": "minhaempresa",
  "transfer_action": "none"           // ou "force_transfer"
}

// Resposta: { "success": true, "username": "minhaempresa", "status": "reserved" }

Regras do nome: 3–35 caracteres, apenas letras (a-z), dígitos, ponto e sublinhado; pelo menos uma letra; sem acentos; não começa/termina com ponto nem tem dois pontos seguidos; não começa com www. O status muda de reserved para approved quando o recurso é liberado (acompanhe pelo webhook business_username_updates).

Listar Números Conectados

Retorna todos os números WhatsApp conectados à sua conta. Use este endpoint para descobrir o phone_number_id de cada número — esse é o valor que você passa no campo from ao enviar mensagens.

GET /api/v1/numbers/list
Quando usar?

Ideal para automatizações que precisam descobrir o phone_number_id correto sem acessar o painel. Basta fazer essa chamada uma vez e armazenar os IDs na sua aplicação.

Exemplo de Uso (cURL)
curl --request GET \
  --url 'https://api.covercut.com.br/api/v1/numbers/list' \
  --header 'X-API-Key: sua_api_key' \
  --header 'X-API-Secret: seu_api_secret'
Exemplo de Resposta
{
  "success": true,
  "data": {
    "count": 2,
    "numbers": [
      {
        "phone_number_id": "123456789012345",
        "display_phone_number": "+55 47 99999-0001",
        "status": "active",
        "quality_rating": "GREEN",
        "phone_tier": "TIER_1K",
        "connected_at": "2026-01-10 09:00:00"
      },
      {
        "phone_number_id": "987654321098765",
        "display_phone_number": "+55 81 91234-5678",
        "status": "active",
        "quality_rating": "YELLOW",
        "phone_tier": "TIER_250",
        "connected_at": "2026-03-22 14:30:00"
      }
    ]
  },
  "message": "Números listados com sucesso."
}
Campos da Resposta
CampoTipoDescrição
phone_number_idstringID da Meta. Use como valor de from nos endpoints de envio.
display_phone_numberstringNúmero formatado para exibição (ex: +55 47 99999-0001).
statusstringactive, suspended ou disconnected.
quality_ratingstringAvaliação de qualidade: GREEN, YELLOW ou RED.
phone_tierstringLimite de templates diários: TIER_50, TIER_250, TIER_1K, TIER_10K, TIER_100K.
connected_atstringData e hora em que o número foi conectado.

Consultar Status do Número

Utilize este endpoint para consultar, em tempo real, a avaliação de qualidade de saúde do seu número e o status da conexão diretamente nos servidores da Meta.

GET /api/v1/numbers/status
Parâmetros de Consulta (Query String / JSON Body)
Parâmetro Obrigatório Descrição
from Sim O ID do número de telefone (Phone Number ID) que deseja consultar. Pode ser passado via query string (GET) ou no corpo JSON (POST).
Exemplo de Uso (cURL)
curl --request GET \
  --url 'https://api.covercut.com.br/api/v1/numbers/status?from=SEU_PHONE_NUMBER_ID' \
  --header 'X-API-Key: sua_api_key' \
  --header 'X-API-Secret: seu_api_secret'
Exemplo de Uso (POST com JSON)
curl --request POST \
  --url 'https://api.covercut.com.br/api/v1/numbers/status' \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sua_api_key' \
  --header 'X-API-Secret: seu_api_secret' \
  --data '{
    "from": "SEU_PHONE_NUMBER_ID"
}'
Exemplo de Resposta
{
  "success": true,
  "data": {
    "phone_number_id": "123456789012345",
    "display_phone_number": "5547999999999",
    "status": "CONNECTED",
    "quality_rating": "GREEN",
    "name_status": "APPROVED",
    "connected_at": "2026-05-15 10:00:00"
  },
  "message": "Status recuperado com sucesso."
}

Nome de Usuário Comercial (API)

Gerencie o nome de usuário (@) comercial dos seus números diretamente pela API — ideal para quem usa sistema próprio. Os mesmos recursos estão na tela Nome de Usuário do painel. Disponível a partir de julho/2026 (reserva/adoção desde 29/jun/2026).

Importante

O nome de usuário comercial não oculta o número de telefone da empresa no app. O parâmetro from (phone_number_id) é opcional em todos os endpoints somente quando você tem um único número ativo — nesse caso é usado automaticamente. Se houver mais de um número ativo e from não for informado, a API retorna erro MULTIPLE_NUMBERS em vez de escolher um sozinho.

Reservar / Adotar

POST /api/v1/username
Parâmetros (corpo JSON)
ParâmetroObrigatórioDescrição
usernameSim3–35 caracteres; apenas letras (a-z), dígitos, ponto e sublinhado; ao menos uma letra; sem acentos; não começa/termina com ponto; não começa com www.
transfer_actionNãonone (padrão) ou force_transfer para transferir um nome já usado em outro número do seu portfólio (sem isso, retorna erro 147005).
fromNão*phone_number_id do número alvo. *Obrigatório se você tiver mais de um número ativo.
Exemplo de Uso (cURL)
curl --request POST \
  --url 'https://api.covercut.com.br/api/v1/username' \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sua_api_key' \
  --header 'X-API-Secret: seu_api_secret' \
  --data '{
    "username": "minhaempresa",
    "transfer_action": "none"
}'
Exemplo de Resposta
{
  "success": true,
  "message": "Nome de usuário definido",
  "from": "123456789012345",
  "username": "minhaempresa",
  "status": "reserved"
}

status: reserved (reservado e aprovado, ainda não visível) ou approved (visível aos usuários do WhatsApp). Acompanhe a mudança pelo webhook business_username_updates.

Consultar nome atual

GET /api/v1/username
curl --request GET \
  --url 'https://api.covercut.com.br/api/v1/username?from=SEU_PHONE_NUMBER_ID' \
  --header 'X-API-Key: sua_api_key' \
  --header 'X-API-Secret: seu_api_secret'
{ "success": true, "from": "123456789012345", "username": "minhaempresa", "status": "approved" }

Sugestões reservadas

Lista os nomes que o WhatsApp reservou para o seu portfólio — têm maior chance de aprovação.

GET /api/v1/username_suggestions
{ "success": true, "from": "123456789012345", "suggestions": ["minhaempresa", "minhaempresa.oficial"] }

Remover

DELETE /api/v1/username
curl --request DELETE \
  --url 'https://api.covercut.com.br/api/v1/username?from=SEU_PHONE_NUMBER_ID' \
  --header 'X-API-Key: sua_api_key' \
  --header 'X-API-Secret: seu_api_secret'
{ "success": true, "from": "123456789012345", "deleted": true }
Erros específicos
Código MetaSignificado
147001Nome indisponível (já reivindicado ou reprovado). Tente outro.
147002Conta não qualificada — precisa de um limite de mensagens maior.
147003 / 147004É preciso vincular o número à Página do Facebook / conta do Instagram que já usa o nome.
147005Nome em uso em outro número do portfólio — reenvie com transfer_action: "force_transfer".
133010Número ainda não registrado para uso com a API.

Códigos de Erro

CódigoDescrição
400Requisição inválida ou parâmetros faltando.
401Credenciais inválidas ou expiradas.
429Rate limit excedido.
500Erro interno no servidor.

Rate Limiting

  • Plano Básico: 60 requisições/min.
  • Plano Pro: 180 requisições/min.

Suporte

Email: suporte@covercut.com.br
WhatsApp: (47) 99618-1954

Changelog

Histórico completo de atualizações, melhorias e correções da API.

Novo v1.12.0 29 Ago 2026
  • Novo
    Flow Dinâmico — Suporte a WhatsApp Flows com telas dependentes de dados em tempo real: geramos e registramos a chave de criptografia (RSA-OAEP-SHA256 + AES-128-GCM) na Meta e repassamos as requisições decifradas para um endpoint do seu próprio servidor, assinado (X-BSP-Signature) igual ao Webhook de mensagens. Configuração self-service na tela Flow Dinâmico do painel. Veja Flow Dinâmico.
Novo v1.11.0 21 Ago 2026
  • Novo
    Qualidade de template exibida no painel — Cada template agora mostra a nota de qualidade da Meta (GREEN/YELLOW/RED/UNKNOWN) direto no card, em Templates. O campo já era retornado por Listar Templates (quality_score); agora é exibido visualmente. Veja Qualidade e Pausa Automática de Templates para entender os níveis e como funciona a pausa progressiva (3h → 6h → desabilitado) que a Meta aplica a templates de baixa qualidade.
Novo v1.10.0 17 Ago 2026
  • Novo
    Personalização completa do Link Direto (Conectar) — Novos parâmetros de URL: bg (cor de fundo do cartão e da página), text_color (cor do texto), title (título), text (texto de instrução), hide_greeting (remove a saudação "Olá, [nome]!") e btn_text (texto do botão). O layout popup agora detecta bloqueio de pop-up e escuta três eventos via postMessage: WA_CONNECT_SUCCESS, WA_CONNECT_CLOSED (janela fechada ou Embedded Signup cancelado no meio) e WA_CONNECT_ERROR. Veja Contas SaaS.
  • Novo
    client_ref — identificação de cliente final em conta SaaS compartilhada — Quando o mesmo Link Direto é usado por vários clientes finais de um revendedor (uma conta SaaS única com number_limit alto), o parâmetro ?client_ref= na URL identifica qual cliente conectou qual número. Gravado por número, devolvido no postMessage, no webhook de conexão e em GET /api/v1/saas/list (que agora aceita filtro ?client_ref=).
  • Novo
    external_id com idempotência em POST /api/v1/saas/create — Informe external_id (a referência do cliente no seu próprio sistema) na criação da conta SaaS. Chamadas repetidas com o mesmo external_id não criam duplicata: reaproveitam a sub-conta existente e só emitem um link novo (campo reused_existing na resposta).
  • Novo
    Webhook de Notificação SaaS — Evento saas_customer_connected, disparado (com retry automático) sempre que uma conta SaaS conecta um número. Configurável em Dashboard → Contas SaaS → "Webhook de Notificação", com secret HMAC próprio e botão de teste.
  • Novo
    Marca Padrão para Contas SaaS — Logo, cores e textos configurados uma única vez em Dashboard → Contas SaaS → "Marca Padrão" são aplicados automaticamente em todo direct_link gerado via POST /api/v1/saas/create, sem precisar repetir os parâmetros de white-label em cada chamada (um campo enviado explicitamente na chamada continua podendo sobrescrever a marca padrão).
  • Correção
    Desambiguação obrigatória de número (MULTIPLE_NUMBERS) — Em 22 endpoints da API (messages/send, messages/template, messages/marketing, messages/carousel, messages/typing, messages/send_sync, messages/catalog, media/upload, media/get, username, username_suggestions, templates/*, groups/* e o adaptador CRM), quando o parâmetro from é omitido e há mais de um número ativo na conta, a API agora retorna erro MULTIPLE_NUMBERS (com a lista de números disponíveis) em vez de escolher silenciosamente o número mais antigo conectado. Isso evita enviar mensagens ou gerenciar templates/grupos pelo número (e cliente) errado em contas SaaS com Link Direto compartilhado entre vários clientes. Contas com um único número ativo continuam funcionando exatamente igual, sem precisar informar from.
  • Correção
    Mensagens de erro do fluxo de conexão — Cancelamentos e fluxos incompletos do Embedded Signup (fechar a janela, cancelar antes de escolher a WABA/número) agora mostram "tente novamente" em vez de uma mensagem de erro de conta. Erros internos genuínos (limite de plano, configuração de servidor) nunca são exibidos crus ao cliente final — o motivo detalhado vai só para o console do navegador e o postMessage do revendedor, para diagnóstico.
  • Segurança
    Proteção contra SSRF no Webhook de Notificação SaaS — URLs de webhook que resolvem para endereços privados/internos (loopback, redes RFC1918, link-local/metadata de nuvem) são bloqueadas, tanto ao salvar a URL quanto no botão de teste.
  • Segurança
    Validação de esquema em redirect/logo do Link Direto — A página de conexão agora só aceita URLs http(s) nesses parâmetros, bloqueando esquemas como javascript: que poderiam executar código no navegador do cliente final.
Novo v1.9.0 13 Ago 2026
  • Novo
    WhatsApp Flows (formulário nativo) — Envio de Flows estáticos via POST /api/v1/messages/send com interactive.type: "flow". A resposta do formulário chega pelo webhook como interactive.nfm_reply, já com o campo response decodificado. Veja Enviar Interativo → WhatsApp Flow. Cobre apenas Flows com telas fixas (sem endpoint próprio de dados dinâmicos).
Novo v1.8.0 29 Jul 2026
  • Novo
    Botão de URL Dinâmica em templates — Agora é possível criar templates com botão de link cuja URL termina em {{1}}, preenchida a cada disparo (links de pedido, rastreio, fatura etc.). Disponível na tela Templates do painel (tipo de link URL Dinâmica) e via POST /api/v1/templates/create. Regras: 1 variável por botão, sempre no final da URL; o example leva apenas o valor que substitui {{1}}; no disparo, envie só o sufixo no componente button (sub_type: "url"). Veja Criar Template → Botão de URL Dinâmica.
  • Melhoria
    Envio de template pelo painel com URL dinâmica — A tela Enviar Template (e o link de template do Chatwoot) agora detecta botões de URL dinâmica e solicita o valor da variável, exibindo o prefixo fixo do template. Se a URL completa for colada, o prefixo é removido automaticamente.
Novo v1.7.0 07 Jul 2026
  • Novo
    API de Contas SaaS (White-label) — Novos endpoints para revendedores automatizarem sub-contas: POST /api/v1/saas/create (retorna direct_link white-label + snippet de botão), GET /api/v1/saas/list, POST /api/v1/saas/status (ativar/inativar) e POST /api/v1/saas/delete. Veja a seção Contas SaaS (White-label).
Novo v1.6.0 30 Jun 2026
  • Correção
    Esclarecimento: o número de telefone NÃO é descontinuado — A documentação anterior dizia que a partir de 01/07/2026 a Meta enviaria "apenas BSUID". Isso está incorreto: o BSUID é adicionado aos webhooks e o número continua presente quando o usuário não adotou nome de usuário (ou pelas condições de 30 dias / lista de contatos). Ao enviar, você pode usar to (número) e/ou recipient (BSUID) — o número tem precedência.
  • Novo
    Botão REQUEST_CONTACT_INFO — Novo tipo interativo request_contact_info (e botão de mesmo nome em templates) para solicitar o número de telefone de um usuário que adotou nome de usuário. Ao compartilhar, chega um webhook type: "contacts" com o telefone (e vcard quando o usuário compartilha um contato direto). O texto é opcional, o rótulo não é personalizável e o botão não leva parâmetros no envio.
  • Novo
    Nome de usuário comercial (Business Username) — Novos endpoints GET/POST/DELETE /api/v1/username (com transfer_action) e GET /api/v1/username_suggestions para reservar, consultar e remover o @ do seu número. Também disponível na tela Nome de Usuário do painel.
  • Novo
    Novos eventos de webhookuser_id_update (BSUID do usuário mudou — atualize seu CRM via previouscurrent), business_username_updates (status do seu nome de usuário: reserved/approved/deleted) e user_preferences (opt-in/out de marketing). Inscreva os campos no Painel de Apps da Meta para recebê-los.
  • Melhoria
    Campos aditivos nos webhookscontact.parent_user_id / status.recipient_parent_user_id (Parent BSUID, multi-portfólio) e status.username em entregue/lido. Todos aditivos — só aparecem quando presentes, sem alterar o formato existente.
  • Melhoria
    Integração Chatwoot compatível com usuários só-username — O envio pelo Chatwoot agora reconhece destinatários BSUID (sem número) e usa o campo correto na API da Meta. O comportamento para números de telefone permanece idêntico.
  • Melhoria
    Novos códigos de erro traduzidos131062 (BSUID não suportado em templates de autenticação) e 147001–147005/133010 (nome de usuário comercial).
Novo v1.5.1 19 Jun 2026
  • Novo
    Identificação da origem das respostas (echo) — Mensagens de saída agora trazem echo_source (api = Cloud API/Chatwoot/integração; phone = celular em coexistência) e message.agent_name. Respostas de atendentes pelo Chatwoot passam a incluir o nome do atendente em agent_name, permitindo distinguir resposta humana de resposta da IA/integração.
  • Melhoria
    Mensagens não suportadas (type: "unsupported") — O webhook agora repassa o bloco unsupported junto com o array message.errors (ex.: código 131051) quando a Meta o envia, deixando claro o motivo de a mensagem não ter sido entregue. Na integração com Chatwoot, esses casos (e também location, contacts, interactive e button) passaram a exibir rótulos legíveis em português em vez do tipo cru em inglês.
Novo v1.5.0 28 Mai 2026 — CRÍTICO
  • Novo
    Business-Scoped User IDs (BSUID) — Suporte Completo — A Meta passou a adicionar from_user_id e to_user_id (BSUID — identificadores únicos por usuário-negócio) aos webhooks; o número de telefone não é descontinuado e continua presente na maioria dos casos. Seu sistema funciona com ou sem número: quando não há from, usa-se o BSUID como identificador. Usernames WhatsApp (como Instagram) também são suportados. Envio direcionado a BSUID e botão REQUEST_CONTACT_INFO disponíveis a partir de julho/2026.
  • Novo
    WhatsApp Calling API — Webhooks de Chamadas de Voz — Implementado suporte nativo aos eventos de chamadas (voice calls). Quando usuários ligam para seu número, você recebe webhook com event: "call" contendo call_id, status (incoming, connected, ended, missed, rejected), duration_seconds, além de BSUID e username do chamador. Webhook inclui estrutura completa com nome, username e dados de contato.
  • Novo
    Rastreamento de Origem — Facebook Ads & Referral — Adicionados 5 novos campos para capturar origem de mensagens via Facebook Ads: referral_source_type (ex: "ad"), referral_source_id (ID da campanha), referral_source_url, referral_headline (copy do anúncio) e referral_ctwa_clid. Permite rastreamento de ROI por campanha. Webhook do cliente inclui campo referral completo quando aplicável.
  • Novo
    Documentação Atualizada — Junho 2026 — Adicionada seção completa sobre BSUID e transição para identificação por username. Documentação de webhooks expandida com exemplos de Calling API e referral. Nova página de status com changelog detalhado de implementação e checklist de testes.
  • Melhoria
    Echo Detection Aprimorado — Corrigida lógica de detecção de echo (mensagens de saída recebidas como webhook) para funcionar corretamente quando há apenas BSUID sem número de telefone. Sistema agora suporta transição dual (número + BSUID simultâneos) até julho de 2026.
Novo v1.4.8 21 Mai 2026
  • Novo
    Exportação Programática de Logs CSV via API — Disponibilizado novo endpoint nativo GET /api/v1/messages/export que permite aos clientes e contas SaaS exportarem relatórios completos de histórico bruto em arquivo CSV. Este recurso permite filtrar via query params e facilita a automação de backup (via webhook/cron, n8n ou Make) transferindo o controle do armazenamento a longo prazo diretamente para o cliente.
Novo v1.4.7 20 Mai 2026
  • Novo
    Sistema Multi-Usuário (Equipes) com Controle de Acesso (RBAC) — Implementada aba "Gerenciar Equipe" permitindo adicionar e gerenciar colaboradores com perfis dedicados: admin (acesso total), developer (chaves de API, webhooks e Chatwoot, ocultando áreas financeiras/segurança) e viewer (perfil de apenas leitura com bloqueio de requisições POST).
  • Novo
    Fila e Logs de Entrega de Webhooks — Criada fila de reprocessamento e auditoria persistente de webhooks com rotina de limpeza automatizada (24h de retenção para envios normais e 48h para falhas de rede) e controle de reenvio síncrono.
  • Novo
    Rate Limit Centralizado — Implementado motor global de controle de limites de requisições por rota baseada na tabela bsp_rate_limits, garantindo estabilidade e impedindo abusos nas APIs de mensageria.
  • Melhoria
    Otimizações de Concorrência e Rate Limit na Coexistência — Resolução do erro (#4) Application request limit reached da Meta nos endpoints de sincronização de coexistência. O sistema agora executa um atraso seguro entre as chamadas e gerencia localmente uma trava de 10 minutos (HTTP 429), evitando desperdício de cota da API da Meta.
  • Correção
    Autenticação Híbrida e Resiliência de Migração — Resolvido erro de banco de dados causado por chaves de API migradas em tabelas herdadas (antigas). Chamadas feitas pelo próprio painel (como carregamento de templates e mídia) agora se autenticam de forma transparente e segura usando a sessão do usuário ativo, corrigindo a "Falha de comunicação".
Novo v1.4.6 20 Mai 2026
  • Novo
    Reconexão de Coexistência (account_update) — Suporte nativo aos novos eventos de reconexão e offboarding (ACCOUNT_OFFBOARDED e ACCOUNT_RECONNECTED) da Meta para clientes em modo Coexistence. Isso permite que softwares parceiros pausem disparos e previnam falhas durante trocas de dispositivo.
Novo v1.4.5 19 Mai 2026
  • Novo
    Suporte à API de Grupos da Meta (Maio 2026) — Habilitados novos eventos de webhooks para controle de grupos (group_lifecycle_update, group_participants_update, etc.). Adicionado o repasse de metadados de precificação PMP (Per-Message Pricing) nos webhooks de status. Documentação de grupos atualizada com as novas diretrizes da Meta.
Novo v1.4.4 19 Mai 2026
  • Novo
    Endpoint de Monitoramento de Número — Adicionado o endpoint GET /api/v1/numbers/status que permite aos clientes SaaS consultarem em tempo real a avaliação de qualidade (quality rating) e o status de conexão dos seus números direto da Graph API da Meta, com sincronização automática do banco de dados local.
Novo v1.4.3 18 Mai 2026
  • Novo
    Filtros Avançados de Busca para Administração — Adicionado um novo sistema unificado de pesquisa rápida nas telas administrativas de Clientes (Index), Números Conectados e Financeiro. Permite localizar clientes de forma instantânea por Nome, E-mail ou Nome da Empresa (e também por IDs de WABA/Telefone ou Número de Telefone na aba de Números), mantendo o estado da busca e do filtro de status persistentes durante a paginação de registros.
  • Novo
    Relatórios e Exportação Premium de Logs — Novo painel de relatório interativo no dashboard de logs, permitindo visualizar volumetria, taxas de conversão (entregues/lidos) e distribuição de erros consolidados. Inclusa exportação em planilha CSV (com BOM UTF-8) e relatórios executivos em formato PDF otimizados para impressão vetorial nativa com suporte a logomarca da empresa do cliente.
  • Novo
    Personalização de Perfil e Logotipo — Nova aba Perfil & Segurança adicionada ao menu lateral, permitindo que cada cliente configure o nome da empresa e insira a URL da logomarca oficial da sua marca, que é consumida e exibida de forma automática e personalizada (white-label) no cabeçalho de todos os relatórios PDF gerados.
  • Novo
    Página de Status do Sistema & Integração Meta — Nova página de status de alta performance alimentada de forma automática pela API do UptimeRobot com sistema inteligente de cache de 60s para evitar rate-limits. Conta com linha do tempo de 30 dias de disponibilidade recente por serviço, painel de onboarding interativo para desenvolvedores e integração nativa com a central de monitoramento da Meta (metastatus.com) para acompanhamento da WhatsApp Cloud API.
  • Fix
    Relatório de Logs e Métricas de Sucesso — Corrigida a lógica de cálculo da taxa de sucesso que antes ignorava mensagens em status sent (Enviado), gerando dados imprecisos. Adicionada a capacidade da modal de exportação herdar e processar dinamicamente todos os filtros de pesquisa ativos da tela (Status, Direção, Tipo e Telefone) nas exportações de PDF e planilhas CSV.
  • Fix
    Mapeamento Completo de Erros da API da Meta — Expandida e refinada a tradução de erros oficiais da Meta no ecossistema BSP. Além de corrigir a tradução do erro 131042 (Cobrança/Pagamento), o tradutor agora possui mapeamento humanizado em português para erros de contato bloqueado (130403), formato de mídia incompatível (131053), templates não encontrados/pausados/suspensos (132001, 132015, 132016), limite de volume por segundo da Cloud API (130429), número de remetente inativo (131009), PIN incorreto de 2 etapas (133005), além de experimentos da Meta (130472) e limites de envio.
  • Novo
    Documentação Completa do Carrossel de Mídia — Adicionados exemplos completos de JSON para o endpoint POST /api/v1/messages/carousel, cobrindo carrosseis com botão de URL (CTA URL) e botões de resposta rápida (quick_reply). Tabela de parâmetros expandida com todos os campos do cartão (header, body, action) e exemplo de resposta de sucesso.
Novo v1.4.2 16 Mai 2026
  • Novo
    Documentação de Prévia de Links — Adicionada seção detalhada sobre o funcionamento e requisitos técnicos (Open Graph) para a exibição de prévias de links em mensagens de texto.
Novo v1.4.1 13 Mai 2026
  • Novo
    Suporte Explícito a Múltiplos Números — Documentação atualizada para incluir o parâmetro from em todos os exemplos de envio da API. Isso permite que clientes com mais de um número conectado escolham o canal de saída da mensagem usando o ID do telefone desejado.
Novo v1.4.0 13 Mai 2026
  • Novo
    Envio de Stickers (Figurinhas) — Adicionado suporte ao tipo de mensagem sticker no endpoint POST /api/v1/messages/send, permitindo o envio de figurinhas dinâmicas por link ou ID.
Novo v1.3.0 12 Mai 2026
  • Novo
    Suporte a Respostas Citadas (Reply) via Chatwoot — Ao usar o botão Responder em uma conversa no Chatwoot, a mensagem agora chega no WhatsApp do cliente citando corretamente a mensagem original, igual ao app nativo do WhatsApp.
  • Novo
    Campo context no endpoint de envio — Os endpoints POST /api/v1/messages/send e /send_sync aceitam agora o campo opcional context.message_id para citar uma mensagem ao enviar via API.
    {
      "to": "5511999998888",
      "type": "text",
      "text": { "body": "Olá!" },
      "context": { "message_id": "wamid.HBgM..." }
    }
  • Fix
    Verificação de duplicidade no adapter Chatwoot — Corrigida a lógica que bloqueava indevidamente mensagens enviadas pelo botão Responder do Chatwoot, fazendo com que não chegassem ao WhatsApp.
Fix v1.2.1 11 Mai 2026
  • Fix
    Upload de mídia para Templates com cabeçalho de imagem — Corrigido o fluxo de duas etapas para upload de mídia na criação de templates, resolvendo o erro de "invalid media handle" ao usar token do sistema.
  • Fix
    Persistência de configurações Chatwoot — Resolvido problema onde as configurações da integração com Chatwoot eram perdidas após salvar.
Novo v1.2.0 08 Mai 2026
  • Novo
    Botão de URL (CTA URL) — Suporte ao tipo de mensagem interativa cta_url para enviar botões com link externo.
  • Novo
    Gestão de Templates via Dashboard — Interface completa para criar, editar, visualizar e excluir templates diretamente pelo painel, com suporte a mídia, botões e rodapé.
  • Novo
    Paginação no Painel Admin — Listas de clientes, números e financeiro passaram a usar paginação para melhorar a performance com grande volume de dados.
Novo v1.1.0 04 Mai 2026
  • Novo
    Integração nativa com Chatwoot — Sincronização bidirecional completa: mensagens recebidas do WhatsApp aparecem automaticamente no inbox do atendente, e mensagens enviadas pelo Chatwoot são entregues pelo WhatsApp sem configuração extra.
  • Novo
    Webhook de Alertas — Endpoint dedicado para receber notificações de eventos críticos (falha de entrega, número suspenso, etc.) separado do webhook principal de mensagens.
  • Fix
    Configuração de Webhook — Corrigido erro 500 ao salvar ou atualizar configurações de webhook no dashboard.
Launch v1.0.0 Abril 2026
  • Lançamento inicial da plataforma WhatsApp API Connect
  • Envio de texto, imagem, vídeo, áudio, documento, localização, contato, reação
  • Mensagens interativas (lista e botões) e templates aprovados pela Meta
  • Recebimento via webhook com rastreamento de status de entrega
  • Rate limiting, dashboard de gerenciamento e suporte a grupos (Oficial)
  • BSUID para identificação de usuários sem número de telefone