Enviar mensagens

Uma rota para todos os tipos: POST /messages. O corpo tem o formato da mensagem: type diz o tipo, e os campos do tipo vão soltos no corpo, com text como texto principal.

{
  "to": "5511999999999",
  "type": "text",
  "text": "Olá, {{name}}! Seu pedido saiu para entrega.",
  "track_source": "crm",
  "track_id": "pedido-42"
}

O caminho do pedido

  1. Conferência. A api confere o corpo inteiro antes de enviar qualquer coisa. Campo desconhecido, campo obrigatório faltando ou to vazio respondem 400 na hora, e nada sai; um tipo que o canal não tem responde 501.
  2. Fila. O pedido entra na fila da inbox. A resposta volta na hora, com a mensagem e status: "queued". O id da resposta já é o definitivo: guarde-o.
  3. Envio. A fila envia uma mensagem por vez, na ordem de chegada. Com o intervalo padrão da inbox (1 a 3 s), a mensagem desta rota não espera esse intervalo; se a inbox tem um intervalo configurado, ele vale entre as mensagens. Na vez da mensagem, saem o "digitando..." (se pedido) e a mensagem.
  4. Resultado. Saiu: evento message.sent. Não saiu: message.status com failed e o motivo (e o evento só de falhas, message.failed, com a mensagem inteira). Depois, entrega e leitura chegam em message.status.

Por que fila por padrão: o envio no WhatsApp leva de frações de segundo a vários segundos (mídia, "digitando...", intervalo entre mensagens). Com a fila, o seu sistema não fica esperando, e a ordem das mensagens é sempre a do pedido.

Na API oficial o envio é sempre na hora, sem fila. A Meta não bloqueia o número pelo ritmo, então não há o que espaçar, e a resposta já traz o wamid como id (pending) ou o erro (janela de 24 horas fechada, modelo reprovado, rate_limited quando a Meta limita; aí o envio pode ser repetido). options.async: true é ignorado.

Esperar o envio sair

Com options.async: false, a resposta só volta depois do passo 3: com status: "pending" (saiu, ainda sem confirmação de entrega) ou com o erro (send_failed, disconnected). Use quando precisa saber na hora se saiu, por exemplo num fluxo de atendimento que depende do envio.

Padrão (fila)options.async: false
A resposta voltana horadepois do envio
status na respostaqueuedpending
Falha no enviomessage.status com failed e message.faileda própria resposta (500 send_failed)
Inbox desconectadaa api tenta reconectar; o pedido entra na fila e sai quando a conexão voltar (ou expira). Sem como reconectar, 503 disconnected503 disconnected
Campos do tipo na respostaos do pedidoa mensagem montada

Mensagens para newsletters do WhatsApp (@newsletter, o que o app chama de Canais) sempre esperam o envio, porque a newsletter precisa do id do servidor.

O destino (to)

Parato
Contatoo número com DDI, só dígitos (5511999999999), ou 5511999999999@s.whatsapp.net
Contato pelo LID123456789012345@lid
Grupo120363028782550019@g.us
Newsletter do WhatsApp120363198765432109@newsletter
API oficial, pelo BSUID<BSUID>@bsuid
Instagram<id>@instagram (o chat_id da conversa)
Messenger<id>@messenger (o chat_id da conversa)
Telegram, conversa com o bot<id>@telegram (o chat_id da conversa)
Telegram Business, conversa da sua conta<id>@telegram-business (o chat_id da conversa)

No WhatsApp, o número é conferido antes do envio, com a mesma regra do nono dígito da uazapi. No Instagram, no Messenger e no Telegram não há número: use o chat_id da conversa, que só existe depois que o contato escreveu. No Telegram, a mesma pessoa pode ter as duas conversas (com o bot e com a sua conta): responda na que ela usou.

Campos de qualquer tipo

CampoO que faz
reply_toResponde a uma mensagem: o id dela. Na resposta, o resumo da citada vem em quoted.
mentionsMenciona pessoas num grupo: lista de números ou ids. Todo @<número> escrito no text (texto, legenda de mídia, enquete, botões, lista e carrossel) já vira menção sem estar na lista, e @todos ou @all menciona todos. Em grupo que usa LID, a menção e o @número saem com o LID do participante, como o WhatsApp faz; evento, localização e contato não leem o texto. Escreva @<número> no texto para a menção aparecer destacada.
mention_allMenciona todos do grupo, sem aparecer no texto.
forwardedMostra como encaminhada (texto, mídia, localização, contato e carrossel).
track_source, track_idSeu rastreamento: volta igual na resposta, nos eventos e na busca, e dá para buscar por eles.
options.delay_msEspera antes de enviar, mostrando "digitando..." (ou "gravando..." em voz).
options.asyncfalse espera o envio sair (veja acima).
options.read_chatMarca a conversa como lida depois de enviar.
options.read_messagesMarca como lidas as mensagens recebidas da conversa.

Placeholders: os textos aceitam {{name}}, {{first_name}} e os campos do lead ({{lead_...}}), trocados pelos dados do contato no momento do envio.

Os tipos

typeCampos obrigatóriosWhatsApp (Web)API oficialInstagram e MessengerTelegram
texttextsimsimsimsim (até 4096 caracteres)
image, video, document, audio, voice, sticker, video_noteurl ou base64simsim (url por link)sim; no Instagram, base64 só de imagem, vídeo, áudio e PDFsim; video_note sai como vídeo
locationlatitude, longitudesimsimnãosim
contactscontacts: um contato, com name e phonessimsimnãosim
polltext (a pergunta), choices (os nomes)simnãonãonão
buttonstext, choicessimsó resposta e linkaté 13 respostas, ou até 3 com linksó resposta e link, sem mídia no cabeçalho
listtext, button_text, choices com sectionsimsimvira respostas rápidas (até 13)vira um botão por item
carouselcardssimnãonãonão
location_requesttextsimnãonãonão
flowtext, flow_id, button_textsimnãonãonão
paymentpix (só a chave) ou amount com a forma de pagamentosimnãonãonão
eventtext (o nome), start_atsimnãonãonão
templatename, languagenãosimnãonão

Onde está não, a resposta é 501 com unsupported_for_channel, antes de enviar. A mesma tabela, em formato que um programa lê (com poll_vote e interactive_reply, só no WhatsApp Web), está no contrato em x-channel-types do POST /messages. Exemplos de cada tipo, com o pedido e a resposta, estão embaixo do POST /messages no menu.

Mídia

  • url (link público) ou base64 (com ou sem o prefixo data:).
  • mime_type é opcional: sem ele, a api descobre pelo conteúdo.
  • text (a legenda) em imagem, vídeo e documento; filename em documento.
  • view_once (visualização única) em foto, vídeo e voz, e animated (GIF) em vídeo: só no WhatsApp Web; nos canais oficiais, 501 unsupported_for_channel.

Botões e listas

O cabeçalho em texto é a primeira linha de text, como na uazapi: quando há texto depois dela, ela sai em negrito acima do corpo. Com uma linha só, ela vai como cabeçalho, sem corpo, também junto da mídia do cabeçalho dos botões. Rodapé em footer. Nos botões, a mídia do cabeçalho vai solta, como numa imagem: url ou base64, e mime_type e filename se quiser. Na API oficial, o texto vai inteiro no corpo. Cada item de choices é {id, title} (resposta) ou {title, action, value} com action url, call ou copy. O id volta na resposta do contato (interactive_reply, em option_id; veja a mensagem).

Nos canais oficiais

  • Janela de 24 horas. Fora dela, 400 outside_window. No WhatsApp oficial, mande um template; no Instagram e no Messenger, espere o contato escrever, ou responda como atendente com options.human_agent: true (até 7 dias). Na conversa com o bot do Telegram não há janela.
  • Envio na hora, sem fila: a resposta já traz o id do canal, ou o erro dele com código (outside_window, recipient_unavailable, conversation_taken, rate_limited, disconnected; veja os erros).
  • Legenda no Instagram e no Messenger sai numa mensagem à parte, logo depois da mídia. Se só ela falhar, a resposta vem 200 com warning (partial_send): não reenvie a mídia, mande a legenda como texto.
  • No Telegram, a legenda vai junto com a mídia em imagem, vídeo e documento (até 1024 caracteres); em áudio, voz e figurinha, ela não sai. Por url, o Telegram baixa o arquivo: até 5 MB a imagem e 20 MB o resto, e documento só PDF ou ZIP; em base64, até 10 MB a imagem e 50 MB o resto. O id de cada botão de resposta tem até 64 bytes. O bot só responde a quem já falou com ele (senão, recipient_unavailable). No Telegram Business, a mensagem sai da sua conta, com o rótulo do bot. A formatação do WhatsApp no texto e na legenda (*negrito*, _itálico_, ~riscado~, `código` e bloco com ```) sai formatada no Telegram, e a marca dobrada do markdown (**negrito**) vale como uma.

A resposta

A resposta é a própria mensagem, com o id definitivo. Na fila, a mensagem ainda não foi montada, então os campos do tipo na resposta são os do pedido: os placeholders ainda não foram trocados, e o link e o base64 do arquivo não voltam. A versão completa chega no message.sent e na busca.

Limites de hoje

LimiteQuando muda
Um contato por mensagem em contacts.Quando o envio aceitar vários contatos numa mensagem.
Um item por cobrança em payment.items, sem valor por item.Quando a cobrança aceitar vários itens.
Botões do carrossel sem aspas, \ e caracteres de controle (tab, quebra de linha).Quando o carrossel escapar esses caracteres.

Nesses casos a resposta é 400 invalid_request, em vez de cortar o conteúdo sem avisar.