# Enviar mensagens

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

```json
{
  "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`](/webhook/message.sent). Não saiu: [`message.status`](/webhook/message.status)
   com `failed` e o motivo (e o evento só de falhas, [`message.failed`](/webhook/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 volta | na hora | depois do envio |
| `status` na resposta | `queued` | `pending` |
| Falha no envio | `message.status` com `failed` e `message.failed` | a própria resposta (`500 send_failed`) |
| Inbox desconectada | a api tenta reconectar; o pedido entra na fila e sai quando a conexão voltar (ou expira). Sem como reconectar, `503 disconnected` | `503 disconnected` |
| Campos do tipo na resposta | os do pedido | a 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`)

| Para | `to` |
|---|---|
| Contato | o número com DDI, só dígitos (`5511999999999`), ou `5511999999999@s.whatsapp.net` |
| Contato pelo LID | `123456789012345@lid` |
| Grupo | `120363028782550019@g.us` |
| Newsletter do WhatsApp | `120363198765432109@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

| Campo | O que faz |
|---|---|
| `reply_to` | Responde a uma mensagem: o `id` dela. Na resposta, o resumo da citada vem em `quoted`. |
| `mentions` | Menciona 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_all` | Menciona todos do grupo, sem aparecer no texto. |
| `forwarded` | Mostra como encaminhada (texto, mídia, localização, contato e carrossel). |
| `track_source`, `track_id` | **Seu** rastreamento: volta igual na resposta, nos eventos e na busca, e dá para buscar por eles. |
| `options.delay_ms` | Espera antes de enviar, mostrando "digitando..." (ou "gravando..." em voz). |
| `options.async` | `false` espera o envio sair (veja acima). |
| `options.read_chat` | Marca a conversa como lida depois de enviar. |
| `options.read_messages` | Marca 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

| `type` | Campos obrigatórios | WhatsApp (Web) | API oficial | Instagram e Messenger | Telegram |
|---|---|---|---|---|---|
| `text` | `text` | sim | sim | sim | sim (até 4096 caracteres) |
| `image`, `video`, `document`, `audio`, `voice`, `sticker`, `video_note` | `url` ou `base64` | sim | sim (`url` por link) | sim; no Instagram, `base64` só de imagem, vídeo, áudio e PDF | sim; `video_note` sai como vídeo |
| `location` | `latitude`, `longitude` | sim | sim | **não** | sim |
| `contacts` | `contacts`: um contato, com `name` e `phones` | sim | sim | **não** | sim |
| `poll` | `text` (a pergunta), `choices` (os nomes) | sim | **não** | **não** | **não** |
| `buttons` | `text`, `choices` | sim | só resposta e link | até 13 respostas, ou até 3 com link | só resposta e link, sem mídia no cabeçalho |
| `list` | `text`, `button_text`, `choices` com `section` | sim | sim | vira respostas rápidas (até 13) | vira um botão por item |
| `carousel` | `cards` | sim | **não** | **não** | **não** |
| `location_request` | `text` | sim | **não** | **não** | **não** |
| `flow` | `text`, `flow_id`, `button_text` | sim | **não** | **não** | **não** |
| `payment` | `pix` (só a chave) ou `amount` com a forma de pagamento | sim | **não** | **não** | **não** |
| `event` | `text` (o nome), `start_at` | sim | **não** | **não** | **não** |
| `template` | `name`, `language` | **não** | sim | **não** | **nã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](/docs/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](/docs/como-funciona#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

| Limite | Quando 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.
