Skip to content
WhatIsUp.dev

Mensagens

Envie uma mensagem de WhatsApp a partir de um canal conectado. Mensagens no sentido de recebimento chegam como webhooks message.received; não existe endpoint de "listar inbox" — webhooks são o caminho de leitura.

O envio é assíncrono — a API retorna na hora, o estado de entrega volta como um webhook.

Enviar uma mensagem

POST/v1/channels/:id/messagesBearer · API key

O canal precisa estar connected. Enviar enquanto está pending / qr / disconnected retorna 409 not_connected.

Schema · Request body
FieldTypeRequiredNotes

O corpo é uma união discriminada por type. Formatos válidos:

// text
{
  "to": "5511999999999",
  "body": { "type": "text", "text": "hello" }
}
 
// image
{
  "to": "5511999999999",
  "body": { "type": "image", "media_url": "https://...", "caption": "look" }
}
 
// audio
{
  "to": "5511999999999",
  "body": { "type": "audio", "media_url": "https://..." }
}
 
// document
{
  "to": "5511999999999",
  "body": { "type": "document", "media_url": "https://...", "filename": "invoice.pdf" }
}
 
// sticker (WebP — estático ou animado)
{
  "to": "5511999999999",
  "body": { "type": "sticker", "media_url": "https://example.com/sticker.webp" }
}

Stickers precisam ser WebP. mime_type é opcional e usa image/webp por padrão.

to é um MSISDN — formato internacional sem + na frente. Aceitamos JIDs de grupo (...@g.us) por completude, mas a maioria dos fluxos é 1:1.

curl -sX POST "$WHATISUP_API/v1/channels/8d653c66-e4ff-43ee-97da-3de5ad5680d4/messages" \
  -H "Authorization: Bearer $WHATISUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"5511999999999","body":{"type":"text","text":"hi"}}'
Schema · Response
FieldTypeRequiredNotes
message_idstringrequired
client_refstringrequired · nullable
status`sent` \| `queued`required

A resposta é 202 Accepted, e status discrimina dois desfechos:

  • "sent" — o canal estava conectado e o WhatsApp aceitou a mensagem.
  • "queued" — o canal estava em recuperação; seguramos a mensagem e retentamos sob o mesmo message_id até ela sair ou o canal parar em definitivo.

Os dois são sucesso. Refazer o POST depois de um "queued" cria uma segunda mensagem no WhatsApp — não existe desduplicação de requisição.

O estado de entrega final chega em webhooks message.status carregando status: "sent" | "delivered" | "read" | "played" | "failed". Não existe evento message.delivered nem message.failed separado. Correlacione por message_id.

O que você não vê nesta API

  • Sem GET de status de mensagem. Não existe rota do tipo "consulte este id pelo estado atual" — o estado de entrega é só push, via webhooks message.status. O histórico de mensagens, porém, é legível: GET /v1/messages e GET /v1/channels/:id/messages devolvem as linhas de entrada e saída guardadas, com corpos, referências de mídia e as entregas de webhook que carregaram cada uma.
  • Sem envio em massa. Faça loop na API; o limitador de requisições por cliente (Chaves de API → Limite de requisições) é o seu throttle. Batching estilo Stripe está no roadmap.
  • Sem "agendar envio". Agende do seu lado e nos chame na hora de disparar.

Emitimos message.status com status: "read" quando o WhatsApp informa que o destinatário abriu a conversa, e "played" para áudios e vídeos — ou seja, confirmação de leitura existe. A ressalva é a configuração de privacidade do destinatário: o WhatsApp suprime a confirmação por completo quando ele desliga confirmações de leitura, então a ausência de read não prova que a mensagem está não-lida.