Atualizado em 25/10/2026
Para enviar uma mensagem pela API oficial do WhatsApp, faça um POST para https://graph.facebook.com/v26.0/<PHONE_NUMBER_ID>/messages com o token no cabeçalho Authorization e um JSON com messaging_product, to, type e o conteúdo. Texto livre só pode ser enviado dentro da janela de 24 horas aberta pelo cliente; fora dela, é preciso usar um template aprovado. A resposta 200 indica que a Meta aceitou o pedido, e a entrega é confirmada depois, por webhook.
Abaixo estão exemplos copiáveis de texto e template em cURL, Node.js e Python, a mesma chamada pela Vectalk e uma tabela com os erros que mais aparecem no primeiro envio. Se você ainda não tem acesso à API, veja o guia API Oficial do WhatsApp.
O que você precisa antes
- Phone Number ID do número que vai enviar. Não é o número em si; é o ID que a Meta gera para ele. Veja o glossário da API.
- Token de acesso com a permissão
whatsapp_business_messaging. Para produção, use um token de usuário do sistema, como em token permanente. - Número do destinatário com
+e código do país, por exemplo+5584999999999. - Template aprovado, se o cliente não escreveu nas últimas 24 horas.
Sobre o formato do número: a documentação de envio recomenda sempre incluir o + e o código do país. Sem o +, a Meta prefixa o código do país do seu número comercial, o que pode mandar a mensagem para o número errado. A mesma página avisa que, para Brasil e México, a API pode ajustar o prefixo do número, e isso é comportamento esperado.
Estrutura do payload
Todo envio usa o mesmo endpoint. O que muda é o type e o objeto com o mesmo nome:
| Campo | Obrigatório | Valor |
|---|---|---|
messaging_product | Sim | Sempre "whatsapp" |
recipient_type | Não | "individual" (padrão) ou "group" |
to | Sim | Número do destinatário, de preferência com + |
type | Sim | text, template, image, document, interactive etc. |
<type> | Sim | Objeto com o conteúdo, por exemplo text: { body } |
context.message_id | Não | wamid de uma mensagem do cliente, para responder citando |
Enviando texto
Mensagem de texto é uma mensagem de serviço: só funciona com a janela de atendimento aberta. O corpo aceita até 4.096 caracteres, e preview_url: true pede ao WhatsApp para gerar a prévia do primeiro link (text messages).
cURL
curl 'https://graph.facebook.com/v26.0/SEU_PHONE_NUMBER_ID/messages' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer SEU_TOKEN' \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "+5584999999999",
"type": "text",
"text": {
"preview_url": true,
"body": "Seu pedido foi enviado. Acompanhe em https://exemplo.com.br/rastreio/123"
}
}'
Node.js (18 ou superior)
const PHONE_NUMBER_ID = process.env.WA_PHONE_NUMBER_ID;
const TOKEN = process.env.WA_TOKEN;
async function enviarTexto(para, texto) {
const r = await fetch(`https://graph.facebook.com/v26.0/${PHONE_NUMBER_ID}/messages`, {
method: 'POST',
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
messaging_product: 'whatsapp',
to: para,
type: 'text',
text: { body: texto },
}),
});
const corpo = await r.json();
if (!r.ok) throw new Error(`${corpo.error?.code}: ${corpo.error?.message}`);
return corpo.messages[0].id; // wamid
}
enviarTexto('+5584999999999', 'Olá! Recebemos sua mensagem.').then(console.log);
Python
import os
import requests
PHONE_NUMBER_ID = os.environ["WA_PHONE_NUMBER_ID"]
TOKEN = os.environ["WA_TOKEN"]
def enviar_texto(para: str, texto: str) -> str:
r = requests.post(
f"https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messages",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"messaging_product": "whatsapp",
"to": para,
"type": "text",
"text": {"body": texto},
},
timeout=15,
)
corpo = r.json()
if not r.ok:
raise RuntimeError(f"{corpo['error']['code']}: {corpo['error']['message']}")
return corpo["messages"][0]["id"] # wamid
print(enviar_texto("+5584999999999", "Olá! Recebemos sua mensagem."))
Enviando template
Template é o que permite iniciar a conversa. Ele precisa estar com status APPROVED, e você informa o nome e o código de idioma exatos com que foi criado. A categoria (marketing, utilidade ou autenticação) foi definida na criação e determina o preço; o tema está em categorias de template e em templates da API do WhatsApp.
Suponha um template de utilidade confirmacao_pedido com o corpo “Olá, {{1}}! Seu pedido {{2}} foi confirmado.”:
cURL
curl 'https://graph.facebook.com/v26.0/SEU_PHONE_NUMBER_ID/messages' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer SEU_TOKEN' \
-d '{
"messaging_product": "whatsapp",
"to": "+5584999999999",
"type": "template",
"template": {
"name": "confirmacao_pedido",
"language": { "code": "pt_BR" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Maria" },
{ "type": "text", "text": "#12345" }
]
}
]
}
}'
Node.js
async function enviarTemplate(para, nome, variaveis, idioma = 'pt_BR') {
const r = await fetch(`https://graph.facebook.com/v26.0/${PHONE_NUMBER_ID}/messages`, {
method: 'POST',
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
messaging_product: 'whatsapp',
to: para,
type: 'template',
template: {
name: nome,
language: { code: idioma },
components: [
{ type: 'body', parameters: variaveis.map((v) => ({ type: 'text', text: v })) },
],
},
}),
});
const corpo = await r.json();
if (!r.ok) throw new Error(`${corpo.error?.code}: ${corpo.error?.message}`);
return corpo.messages[0].id;
}
enviarTemplate('+5584999999999', 'confirmacao_pedido', ['Maria', '#12345']).then(console.log);
Python
def enviar_template(para: str, nome: str, variaveis: list[str], idioma: str = "pt_BR") -> str:
r = requests.post(
f"https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messages",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"messaging_product": "whatsapp",
"to": para,
"type": "template",
"template": {
"name": nome,
"language": {"code": idioma},
"components": [
{"type": "body", "parameters": [{"type": "text", "text": v} for v in variaveis]}
],
},
},
timeout=15,
)
corpo = r.json()
if not r.ok:
raise RuntimeError(f"{corpo['error']['code']}: {corpo['error']['message']}")
return corpo["messages"][0]["id"]
print(enviar_template("+5584999999999", "confirmacao_pedido", ["Maria", "#12345"]))
Se o template foi criado com parâmetros nomeados ({{nome_cliente}}), cada parâmetro leva também parameter_name, e a ordem deixa de importar (templates):
{ "type": "text", "parameter_name": "nome_cliente", "text": "Maria" }
Para templates com imagem, documento ou vídeo no cabeçalho, inclua um componente header com o arquivo. O envio de mídia está em como enviar imagem, PDF, áudio e vídeo.
O que a API devolve
Quando a Meta aceita o pedido, a resposta é esta:
{
"messaging_product": "whatsapp",
"contacts": [{ "input": "+5584999999999", "wa_id": "5584999999999" }],
"messages": [{ "id": "wamid.HBgLNTU4NDk5OTk5OTk5FQIAERgS..." }]
}
Três cuidados:
- Guarde o
wamid. É ele que aparece nos webhooks de statussent,delivered,readefailed. Veja status de mensagem via webhook e webhooks da API. - 200 não é entrega. Erros como limite de marketing por usuário chegam depois, no webhook, com status
failed. - A ordem não é garantida. Se você manda várias mensagens seguidas, a Meta não garante que cheguem na ordem dos pedidos. Para garantir a sequência, espere o status
deliveredde uma antes de mandar a próxima.
A Meta tenta entregar a mensagem por até 30 dias (10 minutos para templates de autenticação). Se o status delivered não chegar nesse prazo, considere a mensagem descartada.
Erros mais comuns no primeiro envio
| Código | O que significa | Como resolver |
|---|---|---|
131047 | Mais de 24 horas desde a última mensagem do cliente | Envie um template. Ver janela de 24 horas |
132001 | Template não existe nesse idioma ou não está aprovado | Confira nome, código de idioma e status |
132000 | Quantidade de variáveis diferente da definida no template | Envie um valor para cada variável |
132012 | Variáveis no formato errado | Use o formato do template (posicional ou nomeado) |
131026 | Não foi possível entregar (número sem WhatsApp, versão antiga, termos não aceitos) | Confirme o número com o cliente |
130429 | Limite de mensagens por segundo atingido | Reduza o ritmo e tente de novo com espera crescente |
131056 | Muitas mensagens para o mesmo destinatário em pouco tempo | Espere antes de mandar de novo para esse número |
133010 | Número comercial não registrado na plataforma | Registre o número |
190 | Token expirado | Gere um novo token de usuário do sistema |
Fonte: códigos de erro da Meta. A lista completa, com causas e soluções, está em erros da API do WhatsApp.
Boas práticas para o código de envio
Os exemplos acima são o mínimo para a mensagem sair. Em produção, alguns cuidados evitam mensagens duplicadas, custo desnecessário e problemas de qualidade:
- Guarde cada envio antes de chamar a API. Registre o pedido no banco com um ID interno e, depois da resposta, salve o
wamid. Se o processo cair no meio, você sabe o que já saiu e não reenvia a mesma mensagem. - Só repita o que vale repetir. Tente de novo, com espera crescente, em
130429,131056e falhas de rede ou HTTP 5xx. Erros de parâmetro (132000,132012) e de template (132001) não melhoram com nova tentativa: corrija o payload. - Respeite o limite de marketing por usuário. A Meta orienta esperar pelo menos 24 horas antes de reenviar um template que voltou com
131049. - Defina timeout nas chamadas. Uma requisição sem timeout pode travar a fila inteira se a rede oscilar.
- Não exponha o token. Leia o token de variável de ambiente ou de um cofre de segredos e nunca o coloque no front-end ou no repositório.
- Valide o número antes. Padronize os números em formato internacional com
+no cadastro, e não na hora do envio. - Marque como lida. Ao processar a mensagem do cliente, você pode marcá-la como lida pela API, o que mostra os dois tiques azuis para ele.
O mesmo envio pela Vectalk
A Vectalk é um proxy da WhatsApp Cloud API oficial. Os payloads são idênticos aos da Meta; mudam só a URL base e o token, conforme a introdução da documentação.
| Meta (direto) | Vectalk | |
|---|---|---|
| URL de envio | https://graph.facebook.com/v26.0/<PHONE_NUMBER_ID>/messages | https://apis.vectalk.com.br/api/messages |
| Token | Token da Meta (usuário do sistema) | Token gerado na Vectalk |
| Phone Number ID | Na URL | Resolvido pelo token de instância, ou no corpo com token de organização |
| Payload e resposta | Formato da Meta | Iguais |
Com um token de instância, o número é resolvido automaticamente:
curl -X POST https://apis.vectalk.com.br/api/messages \
-H "Authorization: Bearer SEU_TOKEN_VECTALK" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "template",
"template": {
"name": "confirmacao_pedido",
"language": { "code": "pt_BR" },
"components": [
{ "type": "body", "parameters": [
{ "type": "text", "text": "Maria" },
{ "type": "text", "text": "#12345" }
] }
]
}
}'
Com um token de organização, que acessa todas as suas instâncias, inclua o phone_number_id no corpo:
import os
import requests
r = requests.post(
"https://apis.vectalk.com.br/api/messages",
headers={"Authorization": f"Bearer {os.environ['VECTALK_TOKEN']}"},
json={
"messaging_product": "whatsapp",
"phone_number_id": "1234567890",
"to": "5584999999999",
"type": "text",
"text": {"body": "Olá! Recebemos sua mensagem."},
},
timeout=15,
)
print(r.json()["messages"][0]["id"])
Os detalhes de cada tipo de mensagem estão em Mensagens e a diferença entre tokens em Autenticação. Para gerar seu token, crie sua conta.
Perguntas frequentes
Posso mandar texto para quem nunca falou comigo?
Não. Fora da janela de 24 horas, inclusive para quem nunca escreveu, só templates aprovados podem ser enviados. A API devolve o erro 131047 se você tentar mandar texto livre. E, pela política do WhatsApp, a pessoa precisa ter dado opt-in.
A API respondeu 200, mas a mensagem não chegou. Por quê?
O 200 só confirma que a Meta aceitou o pedido. A entrega é informada no webhook de status. Se vier failed, o código de erro explica o motivo, como 131049 (limite de marketing por usuário) ou 131026 (número sem WhatsApp).
Qual versão da Graph API devo usar?
Os exemplos da documentação atual da Meta usam a v26.0. Fixe a versão na URL e acompanhe o changelog da Meta antes de atualizar, porque cada versão tem prazo de descontinuação.
Como mandar para muitos contatos de uma vez?
Não existe endpoint de envio em lote: cada mensagem é uma chamada. Controle o ritmo, respeite o limite de mensagens do portfólio e trate o erro 130429. O guia disparo em massa pela API oficial traz um exemplo com controle de ritmo.
Como responder citando a mensagem do cliente?
Inclua "context": { "message_id": "<wamid da mensagem do cliente>" } no payload. A resposta aparece no WhatsApp como uma citação da mensagem original.
Fontes oficiais consultadas em 25/10/2026: Send messages (service messages), Text messages, Templates, Error codes, Status webhook, Access tokens, Documentação da Vectalk: introdução, mensagens e autenticação.
