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.
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
- Como envio uma mensagem de WhatsApp pela API?
- Template ou mensagem de sessão: qual enviar?
- Como envio um template aprovado?
- Quais campos da requisição mais importam?
- Como envio um template com botão ou um OTP?
- Como respondo dentro da janela de atendimento ao cliente?
- Como rastreio a entrega?
- Que erros devo tratar?
- Exemplo prático: um serviço de notificação de pedidos
- Quais são os erros comuns?
- 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.
- O serviço de pedidos grava uma linha com a chave
order-8842-shippede statuspending. - Ele chama
sendbulkcom o template e o número do pedido comobodyTemplateVariables. - Em um
200, guarda obatchIde marca a linha comoaccepted. - O handler do webhook atualiza a linha para
DELIVEREDouREAD, ouFAILEDcom um motivo. - Se o cliente responder, a ferramenta de suporte usa uma mensagem de sessão
TEXTdentro da janela. - Um job noturno lê o
bulk-insightsdos 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/jsonnão funciona. data=no Python em vez defiles=, o que codifica o formulário em URL.- Texto livre fora da janela, que falha. Use um template.
- Código de idioma errado, como
enem vez doen_USdo template. - Tratar o
200como 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
Continue lendo
Mais guiasIntegração
Webhooks do WhatsApp: eventos, segurança e novas tentativas
Como funcionam os webhooks do WhatsApp: eventos de mensagem e status, handler confiável, assinatura da Meta, retries, idempotência e eventos perdidos.
10 min de leitura
Integração
Enviar SMS por API: exemplos em Python, Node.js, PHP e cURL
Envie SMS por API em Python, Node.js, PHP e cURL: envio único e em massa, Unicode e árabe, contagem de segmentos, webhooks de entrega e tratamento de erros.
10 min de leitura
WhatsApp
OTP por WhatsApp vs OTP por SMS: custo, entrega e fallback
OTP por WhatsApp vs OTP por SMS: modelo de custo, entrega, experiência do usuário, templates de autenticação, fallback para SMS e controles de fraude.
10 min de leitura
