
# O que muda da uazapi

A **API uazapi** e a **API Orkesio** falam com a mesma inbox e o mesmo token. A uazapi continua funcionando como
hoje, sem data para sair: as rotas e os eventos dela não mudam. A Orkesio é o caminho daqui para frente: **a API
oficial do WhatsApp, os canais novos e as melhorias saem só nela**. Dá para migrar uma parte por vez.

Esta página compara as duas lado a lado. Para entender a Orkesio do zero, comece por
[Como funciona](/docs/como-funciona).

## A ideia em uma frase

Um **modelo de mensagem único** (`OrkesioMessage`), igual nos eventos, na busca e na resposta do envio, dentro de um
**envelope único** para todos os eventos. Os eventos chegam por [`GET /events`](/docs/eventos) e por
[webhook](/docs/eventos#webhook). Os webhooks da Orkesio ficam em `/webhooks`; os da uazapi (`/webhook`) seguem iguais e
separados. Quem quiser os dois formatos cria um webhook em cada rota (pode ser o mesmo link).

## Envelope

| | uazapi (hoje) | Orkesio |
| --- | --- | --- |
| Nome do evento | `EventType` (`messages`, `messages_update`...) | `type` no formato `recurso.ação` (`message.received`) |
| Id do evento | não tem | `id` (`evt_` + ULID), igual ao header `webhook-id`: serve para descartar repetidos |
| Quando aconteceu | não tem (cada evento tem seu formato de data) | `created_at`, sempre ISO 8601 em UTC com milissegundos |
| Inbox | `owner`, `instanceName`, `BaseUrl` e o **`token` no corpo** | `inbox{id, name, phone, channel}`, sem token (`phone` no WhatsApp; `account_id` no Messenger, no Instagram e no Telegram) |
| Canal | não tem | `inbox.channel`: `whatsapp_web`, `whatsapp_cloud`, `instagram`, `messenger` ou `telegram` |
| Dados | espalhados no topo (`message`, `chat`, `event`, `type`, `state`...) | tudo em `data`, com formato fixo por `type` |
| Payload original | só o campo `cloud` na API oficial | não vem: o conteúdo já chega tipado em `data` |
| Nomes | PascalCase, camelCase, snake_case e minúsculo junto misturados | sempre `snake_case` |

```json
{
  "id": "evt_01JAB3K9ZQ7V0M4T8R2X6WYD5H",
  "type": "message.received",
  "created_at": "2026-10-17T12:00:00.000Z",
  "inbox": { "id": "r0269625d29bfbd", "name": "loja", "phone": "5511888888888", "channel": "whatsapp_web" },
  "data": { "message": { "...": "a mensagem" }, "chat": { "...": "a conversa" }, "lead": { "...": "o lead" } }
}
```

Nos eventos de mensagem, o `data` traz a mensagem (`message`) e a conversa (`chat`); `message.received` e `message.sent`
trazem também o `lead` (só em conversa direta). Outros eventos têm o `data` próprio: veja a página de cada um.

**As listas de valores podem crescer** (`type`, `status`, `sent_by`, `channel`). Trate valor desconhecido sem quebrar.

## Eventos

| uazapi | Orkesio |
| --- | --- |
| `messages` (do contato) | [`message.received`](/webhook/message.received) |
| `messages` (da inbox) | [`message.sent`](/webhook/message.sent) |
| `messages_update` ReadReceipt / FailedMessage | [`message.status`](/webhook/message.status), um por recibo como hoje (várias mensagens da conversa em `messages`) |
| `messages_update` GroupReceipts | [`group.receipts`](/webhook/group.receipts) (lote de 60 s, por participante) |
| `messages_update` Pinned / FileDownloaded / Transcribed, edição | [`message.updated`](/webhook/message.updated) com `change` |
| `messages_update` DeletedMessage | [`message.updated`](/webhook/message.updated) com `change: deleted` |
| reação dentro de `messages` | [`message.reaction`](/webhook/message.reaction) |
| `messages_update` ExpiredMessage | [`message.status`](/webhook/message.status) com `failed` e `error.error: expired` |
| `chats`, `chat_labels` (chat inteiro, sem dizer o que mudou) | [`chat.updated`](/webhook/chat.updated) com `changes[]` |
| (lead junto do chat em toda mensagem) | `data.lead` em toda mensagem de conversa direta e, quando muda sem mensagem, [`lead.updated`](/webhook/lead.updated) com `changes[]` |
| `presence` | [`chat.presence`](/webhook/chat.presence) |
| `contacts` / `labels` | [`contact.updated`](/webhook/contact.updated) / [`label.updated`](/webhook/label.updated) |
| `groups` | [`group.updated`](/webhook/group.updated), [`group.participants`](/webhook/group.participants), [`group.joined`](/webhook/group.joined) |
| `call` | [`call.updated`](/webhook/call.updated) (com `status`) e [`call.log`](/webhook/call.log) |
| `connection` | [`inbox.status`](/webhook/inbox.status) |
| `history` | [`history.batch`](/webhook/history.batch) e [`history.completed`](/webhook/history.completed) |
| `status_posts` / `newsletter_messages` | [`status_post.received`](/webhook/status_post.received) / [`newsletter.message`](/webhook/newsletter.message) |
| `sender` | [`campaign.updated`](/webhook/campaign.updated) (com `status`) |
| `others` (reflexão do whatsmeow) | sai |

## A mensagem (`OrkesioMessage`)

O mesmo objeto nos eventos (`message.received`, `message.sent`), na busca e na resposta do envio. A explicação campo a
campo está em [A mensagem](/docs/mensagem).

| O que | uazapi (`message` de hoje) | Orkesio |
| --- | --- | --- |
| Ids | `id` (`owner:id`), `messageid` | `id` (o id do canal: o do WhatsApp, `wamid` na oficial; o antigo `messageid`) |
| Conversa | `chatid`, `chatlid`, `isGroup`, `groupName` + o `chat` inteiro (cerca de 60 campos) | `chat_id`: o id da conversa (em grupo, o do grupo). A conversa (`id`, `lid`, `type`, `name`...) vem ao lado da mensagem, em `data.chat` |
| Quem escreveu | `sender`, `sender_pn`, `sender_lid`, `senderName`, `fromMe` | `author{id, phone, username, lid, name, picture_url}` e `direction` (`in`/`out`) |
| Por onde saiu | `wasSentByApi`, `ai_metadata` | `sent_by` (`api`, `automation`, `campaign`, `phone`, `web` e, em breve, `user`) e `automation{agent_id, flow_id}` |
| Data | `messageTimestamp` (ms) | `sent_at` (ISO UTC) |
| Tipo | `messageType` (nome do proto), `mediaType`, `type` | `type` (lista fechada); os campos do tipo vêm soltos na mensagem, com `text` como texto principal |
| Conteúdo | `content` = proto cru do WhatsApp; `text` | os campos do tipo, soltos na mensagem: `text` (texto, legenda, corpo), `url`, `mime_type`, `latitude`...; e `display_text` com tudo num texto só |
| Status | `status` (`Read`, `ServerAck`...) | `status` normalizado (`scheduled`, `queued`, `pending`, `sent`, `delivered`, `read`, `played`, `failed`) e `error` |
| Citação | `quoted` (só o id) | `reply_to` (o id) e `quoted{author, direction, type, preview}` (o resumo) |
| Edição | `edited` (id da original) | `edited` (true/false) e o evento `message.updated` |
| Apagada | `messageType: DeletedMessage` | `deleted` (true/false) |
| Rastreamento | `track_source`, `track_id` | `track_source`, `track_id` (**seus**, iguais) e `referral` (**da Meta**, anúncio) |
| CRM | `lead_*` dentro do `chat`, sempre | `lead`, sempre na conversa direta, ao lado da mensagem (`data.lead`) |

### Classes (campo `category`)

Na uazapi cada uso deduzia do `messageType`. Na Orkesio a api manda a classe em cada mensagem (`category`), e a busca
filtra por ela.

| Classe | Como reconhecer na mensagem | Sai em `message.received`/`message.sent`? |
| --- | --- | --- |
| `conversation` | o resto dos `type` | sim |
| `call` | `type: call` | evento próprio (`call.log`) |
| `reaction` | `type: reaction` | evento próprio (`message.reaction`) |
| `event` | `type: group_participant` ou `system` (e edição, voto, fixar) | **não**: fica gravado para a busca e tem evento próprio (`group.participants`), para não disparar automação |
| `unsent` | `status` agendado, na fila, enviando, com falha ou cancelado | na busca |

### Regras decididas

- **Miniatura:** na mídia (foto, vídeo, documento, figurinha) vem **só no aviso em tempo real** (`/events` e webhook); não
  é gravada, a busca traz o `url`. Em link, localização, anúncio e convite vem também na busca.
- **Reações e votos:** continuam uma mensagem cada; na busca, a mensagem reagida traz a soma em `reactions` e a
  enquete traz o resultado em `results`.
- **Grupo:** `chat_id` identifica o grupo (e `data.chat.name` traz o nome); `author` traz quem escreveu. Sem a informação de admin.
- **Status de grupo:** `message.status` leva só o `sent`/`failed` da mensagem; cada participante vem em
  `group.receipts`.
- **Identidade sem telefone:** `author.id` é opaco (no Instagram e no Messenger não há número). O número vem em
  `phone`, e o nome de usuário em `username`.
- **Anúncio:** `referral` traz a origem (anúncio, título, link, `ctwa_clid`), igual na Web e na oficial, e filtra a busca
  (`referral=true` ou `referral_source_id` em `GET /messages`).

## Envio e busca

Duas rotas novas, com o mesmo modelo de mensagem. As rotas atuais (`/send/*`, `/message/find`) continuam iguais.

Uma página por tipo, com a uazapi ao lado da Orkesio: [Enviar mensagem](/docs/envio) e [Buscar mensagens](/docs/busca).

| | uazapi (hoje) | Orkesio |
| --- | --- | --- |
| Enviar | 11 rotas (`/send/text`, `/send/media`, `/send/menu`...), cada uma com seus campos | **`POST /messages`**: uma rota, o corpo é plano, com o formato da mensagem recebida (`to`, `type` e os campos do tipo) |
| Resposta do envio | o `message` da uazapi + `response{status, message}` | a própria `OrkesioMessage`, na hora, com `status: queued`: **fila por padrão** só nesta rota (`options.async: false` espera o envio e volta com `pending` ou o erro) |
| Buscar | `POST /message/find` com `offset` | **`GET /messages`** com cursor (não pula nem repete quando chega mensagem nova) |
| Erro | `{"error": "texto"}` | `{"error": "código", "message": "texto"}` (o `error` vira um código estável) |

| uazapi | Orkesio (`POST /messages`) |
| --- | --- |
| `number` | `to` (número, id do WhatsApp, LID, grupo, canal ou `@bsuid`; depois `@username` de outros canais) |
| `text`, `linkPreview*` | `type: text`, com `text` e `link_preview` |
| `/send/media` `type` + `file` + `text` + `docName` | `type`: `image`, `video`, `video_note`, `audio`, `voice`, `document` ou `sticker`, com `url` ou `base64`, `text`, `mime_type`, `filename`, `thumbnail` e `view_once` |
| `/send/menu` (`button`, `list`, `poll`), `/send/carousel`, `/send/location-button` | `type`: `poll`, `buttons`, `list`, `carousel` ou `location_request`, com `choices` no formato da mensagem recebida (não mais `"texto\|id"`) |
| `/send/request-payment`, `/send/pix-button` | `type: payment` com as formas que vierem (`pix`, `boleto`, `link`, juntas ou não; PIX sem `amount` = só o botão) |
| `/send/contact`, `/send/location`, `/send/event` | `type`: `contacts`, `location` ou `event` |
| — (a API oficial é só da Orkesio) | `type: template`, com `name`, `language` e `components` |
| `replyid`, `mentions` (texto com vírgulas), `"all"` | `reply_to` (o `id`), `mentions[]`, `mention_all` |
| `track_source`, `track_id` | `track_source`, `track_id` (iguais) |
| `delay`, `async`, `readchat`, `readmessages` | `options{delay_ms, async, read_chat, read_messages}` |

O que um canal não tem responde 501 com `error: unsupported_for_channel` (`template` no Web; enquete, carrossel, `event` e `payment` na oficial). O detalhe está em [Enviar mensagens](/docs/enviar).
Campanhas (`/sender`) e status (`/send/status`) têm rotas próprias: [`/campaigns`](/docs/campanhas) e
[`/status-posts`](/endpoint/get/status-posts).

## O que já funciona

| Parte | Estado |
| --- | --- |
| `GET /messages` (busca) | disponível |
| `POST /messages` (envio, todos os tipos) | disponível (na API oficial, sempre envio na hora, sem fila) |
| `GET /events` (todos os eventos, em tempo real) | disponível |
| Webhook no formato da Orkesio (todos os eventos) | disponível |
