# A mensagem

Toda mensagem da Orkesio tem o mesmo formato, venha de onde vier: a resposta do envio, cada item da busca e o `message`
dentro do `data` dos eventos de mensagem. Quem entende este objeto entende toda a API de mensagens.

## Exemplo comentado

Uma foto recebida num grupo, respondendo a uma mensagem da inbox:

```json
{
  "id": "3EB0A1B2C3D4E5F60718",
  "chat_id": "120363028782550019@g.us",
  "author": {
    "id": "5511999999999@s.whatsapp.net",
    "phone": "5511999999999",
    "lid": "123456789012345@lid",
    "name": "Maria"
  },
  "direction": "in",
  "sent_at": "2026-10-17T12:00:00.000Z",
  "type": "image",
  "text": "Segue a foto",
  "mime_type": "image/jpeg",
  "size": 111193,
  "width": 738,
  "height": 1599,
  "display_text": "Segue a foto",
  "reply_to": "3EB05DCE1D4036FCE241C6",
  "quoted": {
    "direction": "out",
    "type": "text",
    "preview": "Seu pedido saiu para entrega."
  },
  "device": "android"
}
```

Lendo de cima para baixo:

1. **`id`:** o id da mensagem no WhatsApp (guarde junto com a inbox).
2. **`chat_id`:** em que conversa. Aqui, um grupo. Os dados da conversa (nome, atendimento, lead) não vão na mensagem:
   vêm em `GET /chats/{chat_id}` e, nos eventos, ao lado da mensagem (`data.chat`).
3. **`author`:** quem escreveu. Num grupo é o participante; numa conversa direta, o contato (ou a própria inbox,
   nas enviadas).
4. **`direction`:** `in` (do contato) ou `out` (da inbox).
5. **`type` e os campos do tipo:** o tipo diz quais campos vêm soltos na mensagem. `text` é sempre o texto principal
   (aqui, a legenda), e `display_text` traz tudo o que aparece na tela num texto só (veja [Os campos do tipo](#os-campos-do-tipo)).
6. **`reply_to` e `quoted`:** `reply_to` é o id da mensagem respondida; `quoted` traz o resumo dela (`preview`) para
   mostrar sem buscar a outra mensagem.

## Os campos

### Identidade e lugar

| Campo | O que é |
|---|---|
| `id` | Id da mensagem no canal: o id do WhatsApp ou, na API oficial, o `wamid`. Único dentro da inbox. |
| `chat_id` | O id da conversa (o `id` de `GET /chats/{chat_id}`). Num grupo, o grupo; quem escreveu vem em `author`. O nome, o tipo (`direct`, `group`, `newsletter`, `broadcast`, `status`, `channel`, `post`) e o lead ficam na conversa, que o webhook e o `/events` mandam em `data.chat`. |
| `author` | Quem escreveu: `id` (sempre), `phone`, `lid`, `name`, `username` e `picture_url` (quando o canal informa). |
| `direction` | `in`: escrita pelo contato. `out`: escrita pela inbox. |
| `sent_at` | Quando foi enviada (ISO 8601, UTC). |

### Origem (só em `out`)

| Campo | O que é |
|---|---|
| `sent_by` | Quem mandou: `api` (rota de envio), `automation` (IA, fluxo ou resposta rápida), `campaign` (disparo em massa, com `campaign_id`), `phone` (o celular), `web` (WhatsApp Web ou outro aparelho) ou `user` (pessoa logada no painel, **em breve**: virá com o campo `user` dizendo quem). |
| `automation` | Qual automação enviou (`agent_id`, `flow_id`), quando `sent_by` é `automation`. |
| `campaign_id` | A campanha que enviou, quando `sent_by` é `campaign`. |
| `track_source`, `track_id` | **Seu** rastreamento: o que você mandou no envio, devolvido igual. |

### Estado

| Campo | O que é |
|---|---|
| `status` | Só em mensagens da inbox. Veja a tabela abaixo. |
| `error` | Só com `status: failed`: `error` (o código, como `expired`), `message` e, quando o erro é de um campo, `field`. |
| `scheduled_at` | Quando sai, num envio agendado (`status: scheduled`). |
| `edited` | Já foi editada; o conteúdo é o da última edição. |
| `deleted` | Foi apagada para todos. A mensagem continua com o conteúdo que já tinha. |
| `pinned`, `pinned_until` | Fixada na conversa, e até quando (quando o prazo é conhecido). |

| `status` | Quer dizer |
|---|---|
| `scheduled` | Agendada, ainda não saiu. |
| `queued` | Na fila da inbox, ainda não saiu. |
| `pending` | Saiu para o canal, sem confirmação de entrega ainda. |
| `sent` | O servidor do canal recebeu. |
| `delivered` | Chegou no aparelho do contato. |
| `read` | O contato leu. |
| `played` | O contato ouviu (áudio de voz) ou assistiu (vídeo redondo). |
| `failed` | Não saiu. O motivo está em `error`. |

O status só avança (`queued` → `pending` → `sent` → `delivered` → `read`). Cada mudança chega no evento
[`message.status`](/webhook/message.status).

### Contexto

| Campo | O que é |
|---|---|
| `reply_to` | A mensagem respondida: o `id` dela. |
| `quoted` | O resumo da mensagem respondida, só na leitura: `author` (em grupo), `direction`, `type` e `preview`. Vem quando a api conhece a citada. Resposta a um story do Instagram não tem `reply_to`: o story vem em `quoted.story`. |
| `mentions` | Quem foi mencionado (ids). |
| `forwarded`, `forwarded_from` | Encaminhada, e de onde, quando o canal informa (encaminhada de uma newsletter do WhatsApp). |
| `device` | Aparelho de quem enviou (`android`, `ios`, `web`, `desktop`), quando dá para saber. Só no WhatsApp Web. |
| `referral` | **Da Meta:** a pessoa chegou por um anúncio ou publicação (click-to-WhatsApp). Traz o anúncio e o `ctwa_clid`. Costuma vir só na primeira mensagem. |

## Os campos do tipo

O `type` diz o que é a mensagem, e os campos daquele tipo vêm soltos na mensagem, ao lado de `id`, `author`... Não há um
objeto `content`. Três regras valem para todos:

- **`text` é sempre o texto principal**, o que a pessoa lê: o texto, a legenda da mídia, o corpo dos botões, a
  pergunta da enquete... Para pegar "o que a mensagem diz", em qualquer tipo, é um campo só.
- **Os outros textos têm o mesmo nome em todo tipo:** `footer` (rodapé), `button_text` (o botão que abre a lista),
  `address`, `description`, `title`. O cabeçalho em texto dos botões e da lista vem na primeira linha de `text`, como se
  manda; a mídia dele vem solta, em `mime_type` e `filename`.
- **`display_text`** junta tudo o que aparece na tela num texto só, com a formatação do WhatsApp (`*negrito*`,
  `_itálico_`): cabeçalho, texto, rodapé e opções. Bom para n8n, IA e CRM. Só vem na busca e nos eventos; no envio, a
  api monta a mensagem pelos campos.

O mesmo formato vale no envio ([`POST /messages`](/endpoint/post/messages)): para reenviar uma mensagem recebida, use o
mesmo `type` e os mesmos campos do tipo, sem os campos que só existem na leitura (`display_text`, `size`, `sha256`, `width`,
`height`...).

## Várias mídias

Uma mensagem tem **uma mídia principal**, em `url` depois de baixada. Quando há mais de uma mídia, vale uma
destas duas formas:

- **Várias fotos e vídeos enviados juntos viram um álbum.** Chega uma mensagem `album` com a contagem (`images`,
  `videos`) e o texto que veio junto, e **uma mensagem por mídia**, cada uma com `album_id` apontando para o álbum.
  Vale para o álbum do WhatsApp e, quando chegarem, para as mensagens com várias fotos do Instagram e do Messenger.
  Assim cada foto tem o seu `id`, o seu evento e o seu download.
- **Mídia dentro de uma mensagem** (o cabeçalho dos botões, os cartões do carrossel): baixe com
  [`POST /messages/media`](/endpoint/post/messages~media) e diga qual em `item`:
  `"item": "header"` ou `"item": "cards.2"` (o terceiro cartão, contando do zero). Sem `item`, a mídia principal.

Depois de baixado, o link fica na resposta da rota e na própria mensagem, no lugar da mídia, e volta na busca:

| Mídia | Onde fica o link |
|---|---|
| A principal | `url` |
| O cartão N | `cards[N].url` |

O link da mídia do cabeçalho (`"item": "header"`) volta só na resposta da rota.

## Os tipos

| `type` | `text` | Outros campos |
|---|---|---|
| `text` | o texto | `link_preview` quando o link tem prévia |
| `image`, `video`, `document`, `sticker` | a legenda | `url`, `mime_type`, `size`, `filename`, `width`, `height`; a miniatura só no evento em tempo real |
| `album` | o texto que veio junto | `images`, `videos`: a contagem; cada mídia chega à parte, com `album_id` |
| `audio`, `voice` | — | o mesmo da mídia, mais `duration_seconds` e `transcription` (a transcrição, quando feita: nunca vai em `text`, que é só o que o autor escreveu). `voice` é o áudio gravado na hora |
| `video_note` | — | o mesmo da mídia (o vídeo redondo) |
| `location` | — | `latitude`, `longitude`, `name` (o nome do lugar), `address`, `url` |
| `live_location` | a legenda | `latitude`, `longitude`, `accuracy`, `sequence` |
| `contacts` | — | `contacts`: a lista (`name`, `phones`) |
| `poll` | a pergunta | `choices` (os nomes das opções), `selectable_count`; o resultado na busca vem em `results` |
| `poll_vote` | — | `poll` (a enquete votada) e `choices` (os nomes votados) |
| `buttons`, `list` | o corpo, com o cabeçalho na primeira linha | `footer`, `choices`; a mídia do cabeçalho, solta (`mime_type`, `filename`); na lista, `button_text` |
| `carousel` | o texto de abertura | `cards`, cada um com a mídia solta (`type`, `url`, `mime_type`...), `text` e `choices` |
| `location_request` | o pedido | — |
| `interactive_reply` | o título da opção escolhida | `option_id` (o `id` da opção), `description` |
| `event` | o nome do evento | `description`, `start_at`, `location` |
| `payment` | a nota | `amount`, `currency`, `items`, `pix`, `payment_status`... |
| `reaction` | — | `emoji` e a mensagem reagida (`target`). Chega em [`message.reaction`](/webhook/message.reaction) |
| `call` | — | a ligação (tipo, duração, resultado) |
| `group_participant`, `system` | — | registros do grupo e do sistema; ficam na busca, não chegam em `message.received` |
| `unavailable` | — | a mensagem não pôde ser lida (ex.: não decifrada) |
| `unknown` | — | tipo que a Orkesio ainda não traduz. Nunca é erro: trate como "mensagem não suportada" |

Onde o nome do campo do tipo colidia com um da mensagem, ele ganhou um nome próprio: `product_id` (produto), `shared_id`
(compartilhamento), `story_id` (menção em story) e `payment_status` (pagamento).

A lista completa, com todos os campos de cada tipo, está no schema `OrkesioMessage` da referência. **A lista de tipos
pode crescer:** trate tipo desconhecido como `unknown`.

## O que muda conforme o lugar

O objeto é o mesmo nos três lugares, com duas diferenças:

| | Resposta do envio | Busca | Evento em tempo real |
|---|---|---|---|
| Miniatura da mídia | não | não | **sim** |
| Campos do tipo | os do pedido (na fila, a mensagem ainda não foi montada) | completo | completo |
