Ir para o conteúdo
OmniChannels.ai

Enviar mensagem de WhatsApp por API: cURL, Node, Python, PHP

Como enviar mensagem de WhatsApp por API: exemplos funcionais em cURL, Node.js, Python e PHP para templates e mensagens de sessão, além de rastreamento e erros.

Atualizado em 10 min de leitura

Por OmniChannels.ai Editorial Team · Revisado por BroadNet Messaging Solutions Team

Integração

Resposta curta

Com a API da OmniChannels.ai, você envia todas as mensagens de WhatsApp por um único endpoint, POST https://api.broadnet.me/whatsapp/message/sendbulk, como multipart form data com um cabeçalho Authorization: Bearer. O campo messageType seleciona o payload: TEMPLATE para templates aprovados, necessários para iniciar uma conversa ou escrever fora da janela de 24 horas, e TEXT, IMAGE ou INTERACTIVE para respostas dentro dela.

Nesta página
  1. Como envio uma mensagem de WhatsApp pela API?
  2. Template ou mensagem de sessão: qual enviar?
  3. Como envio um template aprovado?
  4. Quais campos da requisição mais importam?
  5. Como envio um template com botão ou um OTP?
  6. Como respondo dentro da janela de atendimento ao cliente?
  7. Como rastreio a entrega?
  8. Que erros devo tratar?
  9. Exemplo prático: um serviço de notificação de pedidos
  10. Quais são os erros comuns?
  11. Como a OmniChannels.ai ajuda

Pontos principais

  • Todas as mensagens de WhatsApp passam por POST /whatsapp/message/sendbulk; o messageType (TEMPLATE, TEXT, IMAGE, INTERACTIVE e outros) define quais campos são obrigatórios.
  • A requisição é multipart/form-data, não JSON, então o Python precisa de files= e o PHP precisa de um array no corpo para evitar a codificação em URL.
  • Fora da janela de atendimento ao cliente de 24 horas, só um template aprovado pode ser enviado; dentro dela, você pode enviar texto livre, mídia e mensagens interativas.
  • A chamada enfileira as mensagens e devolve um batchId imediatamente; os status por destinatário chegam ao seu webhook e pelo endpoint bulk-insights.
  • A API não tem chave de idempotência, então registre cada envio antes de fazê-lo e verifique o status após um timeout em vez de reenviar.

Este guia é para desenvolvedores que estão integrando o envio de WhatsApp a uma aplicação. Ele mostra requisições funcionais em cURL, Node.js, Python e PHP, explica a diferença entre mensagens de template e de sessão e cobre rastreamento e erros. Todo endpoint e campo vem da nossa referência da API de WhatsApp e da especificação OpenAPI.

Como envio uma mensagem de WhatsApp pela API?

Você faz um POST autenticado para https://api.broadnet.me/whatsapp/message/sendbulk com campos de formulário. O campo messageType escolhe o tipo de mensagem, e a resposta confirma que as mensagens foram enfileiradas.

Item Valor
URL base https://api.broadnet.me (sem prefixo /v1)
Endpoint POST /whatsapp/message/sendbulk
Autenticação Authorization: Bearer $BROADNET_TOKEN
Tipo de conteúdo multipart/form-data (definido automaticamente pelo seu cliente HTTP)
Resposta Resumo do lote em JSON com um batchId

As credenciais de teste são emitidas após uma breve solicitação de acesso, e os envios de teste vão para os seus próprios números verificados. Veja o quickstart e as páginas de autenticação. Mantenha o token apenas no seu servidor.

Template ou mensagem de sessão: qual enviar?

Envie um template para iniciar uma conversa ou escrever fora da janela, e uma mensagem de sessão quando o cliente escreveu para você nas últimas 24 horas. Essa regra é da Meta, não nossa.

Mensagem de template Mensagem de sessão
messageType TEMPLATE TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT, INTERACTIVE e outros
Quando é permitida A qualquer momento, depois que o template é aprovado Somente enquanto a janela de atendimento ao cliente está aberta
Exige aprovação Sim, pela Meta Não
Uso típico Atualizações de pedido, OTP, lembretes, campanhas Respostas de suporte, respostas a perguntas

A Meta cobra por mensagem entregue, por categoria, e suas regras para mensagens de serviço e templates de utilidade mudaram em 1º de outubro de 2026. Nosso guia da janela cobre a mecânica, e nossa página de preços e a calculadora cobrem o custo.

Como envio um template aprovado?

Defina messageType=TEMPLATE, informe o nome do template e o código de idioma e passe os valores das variáveis. Use batchId=0 para iniciar um novo lote.

curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F campaignName=order-updates \
  -F destinationNo=971500000000 \
  -F messageType=TEMPLATE \
  -F templateName=order_update \
  -F templateLanguage=en_US \
  -F bodyTemplateVariables=INV-400
// Node.js 18+ (fetch e FormData nativos)
const form = new FormData();
form.append("batchId", "0");
form.append("campaignName", "order-updates");
form.append("destinationNo", "971500000000");
form.append("messageType", "TEMPLATE");
form.append("templateName", "order_update");
form.append("templateLanguage", "en_US");
form.append("bodyTemplateVariables", "INV-400");

const res = await fetch("https://api.broadnet.me/whatsapp/message/sendbulk", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.BROADNET_TOKEN}` }, // o fetch define o boundary do multipart
  body: form,
});
console.log(await res.json());
import os
import requests

fields = {
    "batchId": "0",
    "campaignName": "order-updates",
    "destinationNo": "971500000000",
    "messageType": "TEMPLATE",
    "templateName": "order_update",
    "templateLanguage": "en_US",
    "bodyTemplateVariables": "INV-400",
}
# Envie cada campo como uma parte multipart (files=) em vez de codificado em URL (data=).
r = requests.post(
    "https://api.broadnet.me/whatsapp/message/sendbulk",
    headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
    files={k: (None, v) for k, v in fields.items()},
    timeout=30,
)
print(r.json())
<?php
$ch = curl_init("https://api.broadnet.me/whatsapp/message/sendbulk");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BROADNET_TOKEN")],
    // Um array (não uma string) faz o cURL enviar multipart/form-data.
    CURLOPT_POSTFIELDS => [
        "batchId" => "0",
        "campaignName" => "order-updates",
        "destinationNo" => "971500000000",
        "messageType" => "TEMPLATE",
        "templateName" => "order_update",
        "templateLanguage" => "en_US",
        "bodyTemplateVariables" => "INV-400",
    ],
]);
echo curl_exec($ch);
curl_close($ch);

O destino é o código do país mais o número, apenas dígitos. A chamada retorna de imediato com um resumo do lote:

{
  "totalFiles": 1,
  "totalSuccess": 1,
  "totalFailed": 0,
  "totalDuplicates": 0,
  "batchId": "2609091405000000001",
  "fileResults": [
    {
      "fileName": "direct",
      "totalRecords": 1,
      "successCount": 1,
      "failedRecipients": [],
      "duplicateRecipients": []
    }
  ],
  "message": "Messages queued for delivery"
}

Guarde o batchId junto ao seu próprio registro. "Enfileirada" não é "entregue".

Quais campos da requisição mais importam?

Os campos obrigatórios dependem do messageType. Estes são os mais comuns.

Campo Obrigatório Descrição
messageType Sim TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT, STICKER, LOCATION, CONTACTS, TEMPLATE, INTERACTIVE, REACTION ou FLOW
destinationNo ou destinationFiles Um dos dois Números separados por vírgulas, ponto e vírgula ou quebras de linha; ou um arquivo TXT, CSV ou XLSX
batchId Não 0 inicia um novo lote
campaignName Não Seu rótulo para relatórios
allowDuplicate Não false remove destinatários duplicados
templateName, templateLanguage Para TEMPLATE Nome do template aprovado e código de idioma, como en_US ou ar
bodyTemplateVariables Conforme o template Valor da variável do corpo; parâmetros nomeados são passados como JSON
buttonsTemplateVariable Conforme o template Valor dinâmico do botão: sufixo de URL, código de cupom ou código OTP
content Para TEXT Corpo do texto, até 4.096 caracteres, ou legenda de mídia
mediaUrl Conforme o tipo URL HTTPS pública da mídia
interactivePayload Para INTERACTIVE O objeto interativo da Meta como string JSON

O agendamento usa scheduled, scheduleTime e gmt, e delay regula o ritmo da entrega. A lista completa está na referência da API de WhatsApp.

Como envio um template com botão ou um OTP?

Adicione o valor do botão em buttonsTemplateVariable. Para um template de autenticação com um botão de copiar código, passe o código tanto no corpo quanto nos campos do botão.

curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=TEMPLATE \
  -F templateName=login_code \
  -F templateLanguage=en_US \
  -F bodyTemplateVariables=482913 \
  -F buttonsTemplateVariable=482913

Aqui login_code é um espaço reservado para o seu próprio template de autenticação aprovado. Para vários botões, buttonsTemplateVariable recebe um array JSON. Gere o código no seu servidor, guarde-o com hash e uma expiração e nunca o registre em logs. Nosso guia de OTP cobre a entrega com um toque, sem toque e com copiar código.

Como respondo dentro da janela de atendimento ao cliente?

Use um tipo de mensagem de sessão. Uma resposta em texto simples precisa de messageType=TEXT e content.

curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=TEXT \
  -F campaignName=support \
  -F "content=Your order has shipped. Reply here if you need anything."

Para botões de resposta, envie messageType=INTERACTIVE e um interactivePayload que siga o formato de mensagem interativa da Meta.

curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -F batchId=0 \
  -F destinationNo=971500000000 \
  -F messageType=INTERACTIVE \
  -F campaignName=support \
  -F 'interactivePayload={"type":"button","body":{"text":"Confirm your delivery slot?"},"action":{"buttons":[{"type":"reply","reply":{"id":"slot_ok","title":"Confirm"}},{"type":"reply","reply":{"id":"slot_change","title":"Change"}}]}}'

Para marcar uma mensagem recebida como lida e, opcionalmente, mostrar um indicador de digitação, chame POST /whatsapp/message/read com JSON:

curl -X POST https://api.broadnet.me/whatsapp/message/read \
  -H "Authorization: Bearer $BROADNET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "messageId": "wamid.HBgMOTcxNTAwMDAwMDAwFQIAEhgg", "typing": true }'

Veja nosso guia de mensagens interativas para orientações de design.

Como rastreio a entrega?

Os status são enviados por push para o webhook configurado na sua conta e também podem ser lidos por lote. Cada destinatário passa por SUBMITTED, SENT, DELIVERED e READ ou termina como FAILED ou REJECTED com um motivo.

curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&status=FAILED&page=0&size=50" \
  -H "Authorization: Bearer $BROADNET_TOKEN"

batch_Id é obrigatório; status, destination, search, page e size são opcionais, e page começa em zero. Use consultas periódicas como reforço para eventos perdidos e monte o seu receptor como descrito no nosso guia de webhooks. Os lotes também podem ser pausados, retomados e abortados com os endpoints de lote da referência.

Que erros devo tratar?

Leia primeiro o status HTTP e depois o corpo do erro. A API gera erros em um corpo JSON de cinco campos, e falhas específicas do WhatsApp podem vir da Meta.

Status Significado Ação
400 Erro de validação, como um campo específico do tipo ausente ou destino inválido Corrija a requisição; não tente de novo sem alterá-la
401 / 403 Token ausente ou inválido, ou operação não permitida Corrija o token ou peça que habilitemos a operação
404 Template ou remetente não encontrado Confira o nome e o idioma do template
413 Arquivo enviado grande demais Reduza o arquivo
429 Limite de taxa Aguarde o Retry-After
502 A Meta rejeitou a chamada; a message traz o código e o texto de erro da Meta Leia-a; tente de novo só se for transitório
500, 503, 504 Problema no servidor ou a montante Tente de novo com backoff exponencial e jitter

Nosso guia de códigos de erro explica os códigos comuns da Meta. Como não há chave de idempotência, siga os passos de proteção contra duplicatas em Erros e limites: grave primeiro um registro pendente, guarde o batchId devolvido e, após um timeout, verifique o status antes de enviar de novo.

Exemplo prático: um serviço de notificação de pedidos

A "Souq Rafiq" (sintética), uma varejista dos Emirados Árabes Unidos, envia um template order_update quando um pedido é despachado.

  1. O serviço de pedidos grava uma linha com a chave order-8842-shipped e status pending.
  2. Ele chama sendbulk com o template e o número do pedido como bodyTemplateVariables.
  3. Em um 200, guarda o batchId e marca a linha como accepted.
  4. O handler do webhook atualiza a linha para DELIVERED ou READ, ou FAILED com um motivo.
  5. Se o cliente responder, a ferramenta de suporte usa uma mensagem de sessão TEXT dentro da janela.
  6. Um job noturno lê o bulk-insights dos lotes de ontem e concilia os status que faltarem.

Quais são os erros comuns?

  • Enviar JSON. O endpoint de envio do WhatsApp é multipart; Content-Type: application/json não funciona.
  • data= no Python em vez de files=, o que codifica o formulário em URL.
  • Texto livre fora da janela, que falha. Use um template.
  • Código de idioma errado, como en em vez do en_US do template.
  • Tratar o 200 como entregue. Ele significa enfileirada.
  • Novas tentativas às cegas após um timeout, que podem enviar a mensagem duas vezes ao cliente.
  • Token em código de cliente. Chame a API apenas a partir do seu backend.
  • Limites: os detalhes do payload variam por template. Confirme com a nossa equipe o layout das variáveis para a sua primeira campanha personalizada.

Como a OmniChannels.ai ajuda

A BroadNet é uma Meta Business Solution Provider oficial, e o onboarding da API do WhatsApp Business usa o Embedded Signup da Meta; nossa equipe prepara o registro com você, enquanto as aprovações continuam com a Meta. A API REST é documentada com uma especificação OpenAPI 3.1 e um SDK TypeScript, @broadnet/cpaas-sdk, e os status de entrega são normalizados entre os canais. Comece pela documentação da API de WhatsApp ou pelo quickstart.

Perguntas frequentes

Envio mensagens de WhatsApp como JSON ou como form data?

Form data. O endpoint de envio do WhatsApp, POST /whatsapp/message/sendbulk, recebe multipart/form-data, com cada campo como uma parte do formulário. Os envios únicos de SMS usam JSON, o que é uma fonte comum de confusão. No Python, envie os campos por files= em vez de data= e, no PHP, passe um array como corpo para que o cURL monte o multipart.

Posso enviar uma mensagem de WhatsApp sem template?

Somente dentro da janela de atendimento ao cliente. Quando um cliente envia uma mensagem, abre-se uma janela de 24 horas e você pode responder com texto, mídia ou mensagens interativas. Fora dela, ou para iniciar uma conversa, é preciso enviar um template aprovado. A API aplica as regras da Meta, e um envio livre para uma janela fechada vai falhar.

Como sei se a mensagem foi entregue?

A chamada de envio apenas confirma que as mensagens foram enfileiradas e devolve um batchId. Os status por destinatário passam por SUBMITTED, SENT, DELIVERED e READ ou terminam como FAILED ou REJECTED. Eles são enviados por push ao seu webhook, e você também pode lê-los por lote no endpoint bulk-insights.

Posso enviar para vários destinatários em uma única requisição?

Sim. destinationNo aceita vários números separados por vírgulas, ponto e vírgula ou quebras de linha, e destinationFiles aceita um arquivo TXT, CSV ou XLSX. Para um template com variáveis diferentes por destinatário, envie um arquivo personalizado com uma linha por destinatário. Defina allowDuplicate como false para remover números duplicados.

Existe uma chave de idempotência para evitar envios duplicados?

Não. A API não aceita chave de idempotência. Se uma requisição der timeout, você não consegue saber pelo cliente se ela foi aceita. Registre cada envio no seu próprio banco antes de chamar, guarde o batchId devolvido e verifique o status ou a lista de lotes antes de qualquer nova tentativa.

Preciso de opt-in antes de enviar?

Sim. A política da Meta exige que cada destinatário tenha feito opt-in para receber mensagens da sua empresa, e os templates precisam ser aprovados antes do uso. A API não verifica o consentimento por você. Colete o opt-in na origem, guarde a comprovação e suprima quem fizer opt-out.

Fontes

  1. WhatsApp Business Messaging Policy
  2. Pricing on the WhatsApp Business Platform — Meta for Developers
  3. RFC 7578: Returning Values from Forms: multipart/form-data