Mensagens
Todo envio é um POST /v1/messages com instance_id, to, type e um campo com o mesmo nome do type.
{
"instance_id": "ins_...",
"to": "5537999990000",
"type": "text",
"text": {"body": "Olá!"}
}
to é o telefone com DDI e DDD, só dígitos, ou o JID completo de um contato ou grupo (...@s.whatsapp.net, ...@g.us).
Como o envio funciona
- A API valida, aplica as políticas e responde
202comstatus: queued. - A mensagem sai em seguida. Mensagens para a mesma conversa saem na ordem em que foram aceitas; conversas diferentes não esperam umas pelas outras.
- Se a conexão estiver fora, o Recadu tenta de novo por até 15 minutos. Depois disso a mensagem falha com
expired. - Cada mensagem sai uma vez só, mesmo se uma conexão cair no meio do envio.
Use Idempotency-Key para repetir um pedido com segurança (veja Primeiros passos).
Tipos
Texto
"type": "text",
"text": {"body": "Seu pedido saiu. Acompanhe: https://exemplo.com/p/4821", "preview_url": true}
Até 65.536 caracteres. A formatação do WhatsApp vale: *negrito*, _itálico_, ~riscado~, ```mono```.
Imagem, vídeo e documento
"type": "image",
"image": {"media": {"url": "https://exemplo.com/cardapio.jpg"}, "caption": "Cardápio de hoje"}
"type": "document",
"document": {"media": {"media_id": "med_...", "filename": "boleto-outubro.pdf"}, "caption": "Seu boleto"}
A mídia vem de um destes:
url: um link público HTTPS que o Recadu baixa na hora do envio;media_id: um arquivo enviado antes porPOST /v1/media(abaixo). Útil para mandar o mesmo arquivo para muitos contatos.
filename e mimetype são opcionais; servem para dar nome ao documento e corrigir o tipo quando o servidor de origem informa errado. Legenda até 1.024 caracteres.
Áudio e mensagem de voz
"type": "audio",
"audio": {"media": {"url": "https://exemplo.com/aviso.mp3"}, "voice": true}
Com voice: true, chega como mensagem de voz (a bolinha com o microfone). O Recadu converte para o formato do WhatsApp (OGG/Opus) qualquer áudio ou vídeo (MP3, M4A, WAV...) e informa a duração. Sem voice, chega como arquivo de áudio.
Figurinha
"type": "sticker",
"sticker": {"media": {"url": "https://exemplo.com/figurinha.webp"}}
WebP, 512×512.
Localização
"type": "location",
"location": {"latitude": -19.9167, "longitude": -43.9345, "name": "Loja Centro", "address": "Av. Afonso Pena, 1000"}
Contato
"type": "contact",
"contact": {"name": "Atendimento", "phones": ["+5537999990000"], "organization": "Loja Centro"}
Até 5 telefones.
Botões, listas e enquetes
Citação e menções
Qualquer tipo aceita:
reply_to: responde citando uma mensagem (msg_...enviada pela API, ou owa_message_idde uma recebida);mentions: lista de telefones mencionados com@(em grupos), até 50.
Arquivos
curl -X POST https://api.recadu.com.br/v1/media \
-H "Authorization: Bearer $GW_API_KEY" \
-F file=@boleto-outubro.pdf
{
"id": "med_...",
"mimetype": "application/pdf",
"size": 48213,
"sha256": "9f2c...",
"filename": "boleto-outubro.pdf",
"expires_at": "2026-11-12T14:00:00Z",
"url": "https://midia.recadu.com.br/..."
}
Até 64 MB por arquivo. O arquivo vale por 30 dias; depois, usar o media_id dá 422 media-not-found e GET /v1/media/{id} dá 410 media-expired. GET /v1/media/{id} também devolve um link temporário novo para baixar.
Mídia recebida (foto, áudio, PDF que o contato mandou) chega no webhook com um media_id e o mesmo prazo: baixe e guarde no seu sistema se precisar dela depois.
Status
curl https://api.recadu.com.br/v1/messages/msg_... -H "Authorization: Bearer $GW_API_KEY"
status |
Significa |
|---|---|
queued |
Aceita, esperando a vez (ordem da conversa, limite por minuto, janela de horário, conexão fora) |
sending |
Saindo agora |
sent |
O WhatsApp aceitou; wa_message_id preenchido |
delivered |
Chegou ao celular do contato |
read |
O contato abriu (só se ele não desligou a confirmação de leitura) |
failed |
Não saiu; veja error.code |
Outros campos úteis: scheduled_for (mensagem agendada para a abertura da janela de horário), rendered_as (como um interativo saiu de fato: native_flow_buttons, native_flow_list, legacy_buttons, legacy_list, text_menu), sent_at, delivered_at, read_at, failed_at.
error.code |
Causa |
|---|---|
recipient_not_on_whatsapp |
O número não tem WhatsApp |
media_unavailable |
Não deu para baixar ou converter a mídia |
not_connected, not_paired |
A instância estava fora |
opted_out |
O contato pediu para sair (SAIR) depois que a mensagem foi aceita |
canceled |
Agendamento cancelado (DELETE) |
expired |
15 minutos tentando sem sucesso |
wa_error, timeout, internal |
Erro do WhatsApp ou do Recadu: tente de novo; se repetir, fale com o suporte |
Para acompanhar sem consultar, use os eventos message.sent, message.status_updated e message.failed (webhooks).
GET /v1/messages lista com filtros instance_id, status, to e since. O texto das mensagens não fica guardado na consulta: só os metadados.
Editar, apagar e reagir
# editar o texto (só mensagens de texto, até 15 minutos depois do envio)
curl -X PATCH https://api.recadu.com.br/v1/messages/msg_... \
-H "Authorization: Bearer $GW_API_KEY" -H "Content-Type: application/json" \
-d '{"text": "Seu pedido saiu às 14h10."}'
# apagar para todos (ou cancelar, se ainda estiver agendada)
curl -X DELETE https://api.recadu.com.br/v1/messages/msg_... -H "Authorization: Bearer $GW_API_KEY"
# reagir (emoji vazio remove a reação)
curl -X POST https://api.recadu.com.br/v1/messages/msg_.../reactions \
-H "Authorization: Bearer $GW_API_KEY" -H "Content-Type: application/json" \
-d '{"emoji": "👍"}'
| Erro | Quando |
|---|---|
409 message-not-sent |
A mensagem ainda não saiu |
409 not-editable |
Não é texto |
409 edit-window-closed |
Passaram os 15 minutos |
409 message-sending |
Cancelar uma que já está saindo |
"Digitando"
A política da instância já mostra "digitando..." antes de cada texto, por um tempo proporcional ao tamanho (veja políticas). Para controlar à mão:
curl -X POST https://api.recadu.com.br/v1/instances/ins_.../chats/5537999990000/presence \
-H "Authorization: Bearer $GW_API_KEY" -H "Content-Type: application/json" \
-d '{"state": "typing"}'