---
title: "Mensagens"
slug: messages
group: "Referência da API"
source: https://linkzap.orion.moe/developers/messages
openapi: https://linkzap.orion.moe/openapi.json
---

# Mensagens

Enviar pelo número conectado e ler o histórico de uma conversa.

## Enviar

### `POST /api/v1/messages`
Manda texto ou mídia. Se o telefone ainda não é contato, o contato e a conversa nascem aqui.

**Corpo (JSON)**

| Campo | Tipo | Obrigatório | Descrição |
| --- | --- | --- | --- |
| `channelId` | texto | com chave de conta | Por qual número falar. Com uma **chave de canal** pode ser omitido — o escopo preenche. Com a chave da conta é obrigatório: faltando, a resposta é 422 `Informe channelId.` |
| `phone` | texto | — | Para quem. Informe `phone` **ou** `conversationId`. |
| `conversationId` | texto | — | Responder numa conversa que já existe. Precisa ser do mesmo canal, senão 422. |
| `text` | texto | — | Até 4096 caracteres. Sem mídia, é a mensagem; com mídia, vira a legenda se não houver `caption`. |
| `mediaUrl` | URL | — | Um arquivo numa URL pública, que nós baixamos e enviamos. O endereço precisa ser público de verdade: se ele aponta para rede interna (ou redireciona para lá), ou se redireciona mais de três vezes, a resposta é 422 e nada é enviado. |
| `mediaId` | texto | — | Um arquivo do Drive do LinkZap — mais confiável que URL para o que você reenvia sempre. |
| `caption` | texto | — | Legenda da mídia, até 1024 caracteres. |

Pedido:

```bash
curl -X POST https://linkzap.orion.moe/api/v1/messages \
  -H "Authorization: Bearer lz_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "channelId": "chn_9f2a...",
    "phone": "5592999990000",
    "text": "Sua consulta está confirmada para quinta, 9h."
  }'
```

Resposta:

```json
{
  "data": {
    "messageId": "msg_7bd0...",
    "conversationId": "cnv_5ad2...",
    "channelId": "chn_9f2a..."
  }
}
```

É preciso um de `text`, `mediaUrl` ou `mediaId`, e um de `phone` ou `conversationId` — faltando, a resposta é 422 dizendo qual.

> [!WARNING]
> **409 é a recusa honesta**
>
> Se o número estiver desconectado, desligado ou impedido de enviar, a resposta é `409` com o motivo real em palavras, e nada foi enviado. Isso é diferente de um erro: dá para mostrar o motivo para a sua equipe e tentar de novo depois. `GET /channels` antecipa o mesmo motivo.

A mensagem aparece na caixa creditada como **API**, e **não** pausa a Aura — quem pausa a automação é uma pessoa respondendo pelo painel ou pelo celular. Se você está montando um chatbot próprio, leia a [receita correspondente](https://linkzap.orion.moe/developers/recipes.md#4-um-chatbot-seu-respondendo) antes.

## Ler o histórico

### `GET /api/v1/messages`
As mensagens de uma conversa, da mais recente para a mais antiga.

**Parâmetros da URL**

| Campo | Tipo | Obrigatório | Descrição |
| --- | --- | --- | --- |
| `conversationId` | texto | obrigatório | Obrigatório. Ache o id em `GET /conversations`. |
| `page` | inteiro | — | A partir de 1. |
| `limit` | inteiro | — | De 1 a 200. Padrão 50. |
| `includeNotes` | booleano | — | Por padrão as notas internas da equipe **não** vêm. `true` as inclui. |

Pedido:

```bash
curl "https://linkzap.orion.moe/api/v1/messages?conversationId=cnv_5ad2...&limit=20" \
  -H "Authorization: Bearer lz_SUA_CHAVE"
```

Resposta:

```json
{
  "conversationId": "cnv_5ad2...",
  "data": [
    {
      "id": "msg_7bd0...",
      "direction": "out",
      "kind": "text",
      "text": "Sua consulta está confirmada para quinta, 9h.",
      "media": null,
      "author": { "kind": "automation", "label": "Fluxo · Confirmação" },
      "status": "read",
      "receipts": {
        "sent": "2026-09-21T14:02:59.000Z",
        "delivered": "2026-09-21T14:03:04.000Z",
        "read": "2026-09-21T14:05:22.000Z"
      },
      "externalId": "3EB0...",
      "replyToId": null,
      "transcript": null,
      "createdAt": "2026-09-21T14:02:59.000Z"
    }
  ],
  "page": 1,
  "limit": 20,
  "total": 48
}
```

- `direction` é `in` (o contato), `out` (saiu daqui) ou `note` (nota interna, só com `includeNotes=true`).
- `author` diz quem escreveu: `contact`, `team` (com o nome da pessoa) ou `automation` (com a origem — `Aura`, `Fluxo · X`, `Campanha`…). Mensagens antigas, de antes de existir esse carimbo, vêm como `Automação`.
- `media.url` só existe depois que o arquivo foi baixado para cá; enquanto `media.pending` for `true`, a URL é `null` — preferimos isso a entregar um link que não abre.
- `transcript` traz a transcrição de uma nota de voz, quando a Aura já transcreveu.

> [!CAUTION]
> **Notas internas são internas**
>
> Elas são o que a equipe escreve para a equipe, e foram escritas com essa expectativa. Se você espelha a conversa num portal que o cliente final enxerga, não peça `includeNotes=true`.
