← Blog

·

Como enviar mensagem pela API do WhatsApp: exemplos em cURL, Node.js e Python

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:

CampoObrigatórioValor
messaging_productSimSempre "whatsapp"
recipient_typeNão"individual" (padrão) ou "group"
toSimNúmero do destinatário, de preferência com +
typeSimtext, template, image, document, interactive etc.
<type>SimObjeto com o conteúdo, por exemplo text: { body }
context.message_idNãowamid 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:

  1. Guarde o wamid. É ele que aparece nos webhooks de status sent, delivered, read e failed. Veja status de mensagem via webhook e webhooks da API.
  2. 200 não é entrega. Erros como limite de marketing por usuário chegam depois, no webhook, com status failed.
  3. 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 delivered de 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ódigoO que significaComo resolver
131047Mais de 24 horas desde a última mensagem do clienteEnvie um template. Ver janela de 24 horas
132001Template não existe nesse idioma ou não está aprovadoConfira nome, código de idioma e status
132000Quantidade de variáveis diferente da definida no templateEnvie um valor para cada variável
132012Variáveis no formato erradoUse o formato do template (posicional ou nomeado)
131026Não foi possível entregar (número sem WhatsApp, versão antiga, termos não aceitos)Confirme o número com o cliente
130429Limite de mensagens por segundo atingidoReduza o ritmo e tente de novo com espera crescente
131056Muitas mensagens para o mesmo destinatário em pouco tempoEspere antes de mandar de novo para esse número
133010Número comercial não registrado na plataformaRegistre o número
190Token expiradoGere 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, 131056 e 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 enviohttps://graph.facebook.com/v26.0/<PHONE_NUMBER_ID>/messageshttps://apis.vectalk.com.br/api/messages
TokenToken da Meta (usuário do sistema)Token gerado na Vectalk
Phone Number IDNa URLResolvido pelo token de instância, ou no corpo com token de organização
Payload e respostaFormato da MetaIguais

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.

Integre o WhatsApp oficial ao seu sistema

A Vectalk entrega a Cloud API da Meta com os mesmos payloads e webhooks, um token para todas as contas e documentação completa.