WhatsApp: enviar mensagens pela API
Envie texto, imagem, vídeo, áudio, documento, figurinha, localização, botões e lista no WhatsApp pela API da XBASE, na janela de 24 horas.
Atualizado em
POST /whatsapp/messages envia uma mensagem livre a um contato, dentro da janela de 24 horas. Fora dela, envie um template.
Enviar uma mensagem
POST https://api.xbase.app/api/external/v1/whatsapp/messages| Campo | Tipo | Obrigatório | Regra |
|---|---|---|---|
numberId | texto | sim | Número que envia (listar os números) |
to | texto | sim | Telefone do contato com DDI, 8 a 15 dígitos. +, espaços, hífens e parênteses são removidos |
type | texto | sim | text, image, video, audio, document, sticker, location, buttons, list ou flow |
| objeto do tipo | objeto | sim | Um campo com o mesmo nome do type, como "text": { "body": "…" }. Veja cada tipo abaixo |
curl -X POST https://api.xbase.app/api/external/v1/whatsapp/messages \
-H "Authorization: Bearer $XBASE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
"to": "5521999990000",
"type": "text",
"text": { "body": "Seu pedido saiu para entrega e chega hoje até as 18h." }
}'A resposta está em Resposta. Campo a mais no corpo é recusado com 400.
Texto
{
"numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
"to": "5521999990000",
"type": "text",
"text": { "body": "Olá, Maria! Como posso ajudar?" }
}text.body tem de 1 a 4.096 caracteres. Links no texto aparecem clicáveis no WhatsApp.
Mídia
Imagem, vídeo, áudio, documento e figurinha vão por URL https pública: a Meta baixa o arquivo dela. URL http, com usuário e senha, ou de endereço interno ou de rede privada responde 400.
{
"numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
"to": "5521999990000",
"type": "image",
"image": {
"link": "https://cdn.minhaempresa.com.br/catalogo/tenis-azul.jpg",
"caption": "Tênis azul, tamanhos 38 a 44"
}
}type | Objeto | Regras da Meta |
|---|---|---|
image | link, caption | JPEG ou PNG, até 5 MB |
video | link, caption | MP4 ou 3GP, até 16 MB |
audio | link, voice | AAC, MP3, MP4 ou OGG, até 16 MB. "voice": true mostra como nota de voz, e o arquivo tem de ser OGG com codec Opus |
document | link, filename, caption | Qualquer arquivo, até 100 MB. filename (até 240 caracteres) é o nome que o contato vê |
sticker | link | WebP de 512 × 512 pixels |
caption é opcional, com até 1.024 caracteres.
Localização
{
"numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
"to": "5521999990000",
"type": "location",
"location": {
"latitude": -23.5614,
"longitude": -46.6559,
"name": "Loja Paulista",
"address": "Av. Paulista, 1000, São Paulo"
}
}| Campo | Regra |
|---|---|
location.latitude | De −90 a 90 |
location.longitude | De −180 a 180 |
location.name · location.address | Opcionais, até 1.000 caracteres |
Botões
{
"numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
"to": "5521999990000",
"type": "buttons",
"buttons": {
"header": "Pedido 12345",
"body": "Podemos confirmar a entrega para amanhã?",
"footer": "Toque numa opção",
"buttons": [
{ "id": "confirmar", "title": "Confirmar" },
{ "id": "remarcar", "title": "Remarcar" }
]
}
}| Campo | Regra |
|---|---|
buttons.body | Texto da mensagem, até 1.024 caracteres |
buttons.header · buttons.footer | Opcionais, até 60 caracteres |
buttons.buttons | De 1 a 3 botões. id (até 256 caracteres, sem repetir) volta quando o contato toca; title tem até 20 caracteres |
Lista
{
"numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
"to": "5521999990000",
"type": "list",
"list": {
"body": "Escolha o horário da entrega.",
"button": "Ver horários",
"rows": [
{ "id": "manha", "title": "Manhã", "description": "Das 8h às 12h" },
{ "id": "tarde", "title": "Tarde", "description": "Das 13h às 18h" }
]
}
}| Campo | Regra |
|---|---|
list.body | Texto da mensagem, até 1.024 caracteres |
list.button | Texto do botão que abre a lista, até 20 caracteres |
list.rows | De 1 a 10 linhas. id (até 200 caracteres, sem repetir) volta quando o contato escolhe; title até 24 caracteres; description opcional, até 72 |
list.header · list.footer | Opcionais, até 60 caracteres |
O botão tocado ou a linha escolhida chega como whatsapp.message.received nos webhooks do workspace, com o id que você mandou.
Flow
Para abrir um WhatsApp Flow (formulário dentro do WhatsApp), use "type": "flow". Os campos e um exemplo estão em Flows.
Resposta
Resposta 200:
{
"status": "accepted",
"messageId": "wamid.HBgNNTUyMTk5OTk5MDAwMBUCABEYEjQ1QzM1RkQ2RDhGNjZBMDQ3MwA=",
"threadId": "c1d7f0b2-5a4e-4f3d-9c2b-1a0e9d8c7b6a",
"dialogId": "5a0e4c31-7b2d-4e8f-a1c3-9d6b5e4f3a21"
}| Campo | Descrição |
|---|---|
status | accepted: a Meta aceitou o envio. Entrega e leitura chegam depois, nos eventos de webhook |
messageId | Id da mensagem na Meta (wamid.…). Vai na reação e aparece nos eventos |
threadId | Conversa do contato na XBASE |
dialogId | Mensagem na conversa |
Erros
| Status | code | Quando |
|---|---|---|
| 400 | (sem code) | Campo inválido: telefone, type desconhecido, texto vazio ou longo demais, URL http ou interna, campo a mais. A message diz qual |
| 404 | NUMBER_NOT_FOUND | O número não existe neste workspace |
| 422 | NUMBER_DISCONNECTED | O número está desconectado |
| 422 | WINDOW_CLOSED | Fora da janela de 24 horas. Envie um template |
| 422 | SEND_REJECTED | A XBASE recusou antes de enviar. A message diz o motivo |
| 422 | META_ERROR | A Meta recusou, por exemplo, mídia que ela não conseguiu baixar. details.metaCode traz o código da Meta |
No WINDOW_CLOSED, details.reason diz por que a janela está fechada: no_contact_message (o contato nunca escreveu para este número), window_expired (a última mensagem dele tem mais de 24 horas) ou awaiting_template_reply (o último envio foi um template e o contato ainda não respondeu). details.lastContactMessageAt traz a data da última mensagem do contato.
{
"statusCode": 422,
"message": "Janela de atendimento de 24h expirada. Envie apenas templates aprovados pelo WhatsApp.",
"code": "WINDOW_CLOSED",
"details": { "reason": "window_expired", "lastContactMessageAt": "2026-11-03T14:02:11.000Z" }
}