> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mindosoftware.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Exportar mensagens

> Exporta mensagens de chat como stream NDJSON para análise qualitativa com IDs anonimizados

## Descrição

Exporta mensagens de chat como um stream NDJSON (Newline-Delimited JSON) para análise qualitativa. Todos os IDs de pessoas são anonimizados automaticamente com HMAC-SHA256.

<Warning>
  A resposta é um stream NDJSON (`application/x-ndjson`), não JSON padrão. Cada linha é um objeto JSON independente.
</Warning>

## Header de autenticação

<ParamField header="X-API-Key" type="string" required>
  Sua API Key do Mindo. Aceita dois tipos:

  * **Global (cross-company):** `mindo_global_<key>` — acesso a todas as empresas
  * **De empresa:** `mindo_xxxxxxxxxxxxxxxxxxxxxxxx` — acesso limitado à empresa associada
</ParamField>

## Query parameters

<ParamField query="company_id" type="string" required>
  ID da empresa. Aceita um inteiro ou lista separada por vírgulas: `42` ou `42,57`.
</ParamField>

<ParamField query="from" type="string" required>
  Data/hora de início (ISO-8601, inclusivo). Exemplo: `2026-04-01T00:00:00Z`.
</ParamField>

<ParamField query="to" type="string" required>
  Data/hora de fim (ISO-8601, exclusivo). Deve ser maior que `from`. Máximo de 31 dias de intervalo.
</ParamField>

<ParamField query="channel" type="string" default="all">
  Filtro de plataforma: `whatsapp`, `instagram`, `messenger`, `all`.
</ParamField>

<ParamField query="agent_id" type="string">
  Filtrar por agente IA. Um inteiro ou lista CSV: `5` ou `5,12`.
</ParamField>

<ParamField query="direction" type="string" default="all">
  Direção da mensagem: `incoming`, `outgoing`, `all`.
</ParamField>

<ParamField query="include" type="string" default="classifications">
  Campos extras a incluir (CSV): `classifications`, `extractions`, `tool_calls`, `trace_id`.
</ParamField>

<ParamField query="cursor" type="string">
  Cursor de paginação obtido de `next_cursor` na resposta anterior.
</ParamField>

<ParamField query="limit" type="integer" default="5000">
  Quantidade máxima de linhas por chamada. Intervalo: 1–10000.
</ParamField>

## Resposta

A resposta é um stream NDJSON. Cada linha é um objeto JSON com os seguintes campos:

<ResponseField name="message_id" type="string">
  ID anonimizado da mensagem (`msg_<24 hex chars>`).
</ResponseField>

<ResponseField name="conversation_id" type="string">
  ID anonimizado do chat (`conv_<24 hex chars>`).
</ResponseField>

<ResponseField name="occurred_at" type="string">
  Timestamp ISO-8601 de quando a mensagem foi enviada.
</ResponseField>

<ResponseField name="channel" type="string">
  Plataforma: `META_WHATSAPP`, `WHATSAPP_EVOLUTION`, `INSTAGRAM`, `MESSENGER`, `MANYCHAT_WHATSAPP`, `MANYCHAT_INSTAGRAM`.
</ResponseField>

<ResponseField name="direction" type="string">
  `"inbound"` (do contato) ou `"outbound"` (do sistema/agente/operador).
</ResponseField>

<ResponseField name="author" type="object">
  Quem enviou a mensagem.

  <Expandable title="Propriedades de author">
    <ResponseField name="type" type="string">
      Tipo de autor: `contact`, `agent`, `human_operator`, `system`.
    </ResponseField>

    <ResponseField name="id" type="string">
      ID anonimizado do autor (`ctc_`, `agt_`, `op_` conforme o tipo). `null` para `system`.
    </ResponseField>

    <ResponseField name="display_name" type="string">
      Nome visível do autor. Presente apenas para `agent`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="agent" type="object">
  Dados do agente IA (somente se a mensagem foi gerada por um agente).

  <Expandable title="Propriedades de agent">
    <ResponseField name="id" type="integer">
      ID real do agente IA (não anonimizado).
    </ResponseField>

    <ResponseField name="name" type="string">
      Nome do agente.
    </ResponseField>

    <ResponseField name="version" type="string">
      Versão do agente.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="content" type="object">
  Conteúdo da mensagem.

  <Expandable title="Propriedades de content">
    <ResponseField name="type" type="string">
      Tipo: `text`, `image`, `video`, `audio`, `file`, `sticker`, `location`, `contact`, `system`, `notification_template`, `carousel`, `story_mention`, `story_reply`, `ice_breaker_response`.
    </ResponseField>

    <ResponseField name="text" type="string">
      Conteúdo textual da mensagem.
    </ResponseField>

    <ResponseField name="media_url" type="string">
      URL do arquivo multimídia (se aplicável).
    </ResponseField>

    <ResponseField name="media_mime" type="string">
      MIME type da mídia (ex: `image/jpeg`).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="context" type="object">
  Contexto da conversa.

  <Expandable title="Propriedades de context">
    <ResponseField name="reply_to_message_id" type="string">
      ID anonimizado da mensagem citada (`msg_<hash>`).
    </ResponseField>

    <ResponseField name="thread_position" type="null">
      Reservado para uso futuro.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="metadata" type="object">
  Dados de exportação.

  <Expandable title="Propriedades de metadata">
    <ResponseField name="client_id" type="string">
      ID anonimizado da empresa (`cmp_<hash>`).
    </ResponseField>

    <ResponseField name="company_id" type="integer">
      ID real da empresa.
    </ResponseField>

    <ResponseField name="exported_at" type="string">
      Timestamp UTC de quando este registro foi gerado.
    </ResponseField>

    <ResponseField name="schema_version" type="string">
      Versão do schema (`"1.0"`).
    </ResponseField>
  </Expandable>
</ResponseField>

### Campos opcionais (parâmetro `include`)

Estes campos só aparecem se solicitados no parâmetro `include`.

<ResponseField name="classifications" type="array">
  Classificações aplicadas à mensagem (incluído por padrão).

  <Expandable title="Propriedades de classifications">
    <ResponseField name="label" type="string">
      Nome do classificador.
    </ResponseField>

    <ResponseField name="value" type="any">
      Resultado da classificação.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="extractions" type="array">
  Dados extraídos da mensagem por extratores de campos personalizados.

  <Expandable title="Propriedades de extractions">
    <ResponseField name="key" type="string">
      Nome do campo personalizado.
    </ResponseField>

    <ResponseField name="value" type="any">
      Valor extraído.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="tool_calls" type="array">
  Ferramentas executadas pelo agente IA durante a geração da mensagem.

  <Expandable title="Propriedades de tool_calls">
    <ResponseField name="tool_name" type="string">
      Nome da ferramenta.
    </ResponseField>

    <ResponseField name="success" type="boolean">
      Se a execução foi bem-sucedida.
    </ResponseField>

    <ResponseField name="execution_time_ms" type="integer">
      Tempo de execução em milissegundos.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="trace_id" type="string">
  ID de rastreamento no Langfuse para debugging. Só tem valor em mensagens outgoing geradas por um agente IA.
</ResponseField>

### Paginação

A paginação é baseada em cursor. Se houver mais resultados, a última linha do stream é um sentinel:

```json theme={null}
{"_truncated": true, "next_cursor": "<cursor_string>"}
```

Envie a próxima requisição com `cursor=<next_cursor>` para obter a página seguinte. Quando não há sentinel, todos os dados foram retornados.

### Anonimização de IDs

| Prefixo | Representa                  |
| ------- | --------------------------- |
| `msg_`  | ID da mensagem              |
| `conv_` | ID da conversa              |
| `ctc_`  | ID do contato               |
| `agt_`  | ID do agente (em author)    |
| `op_`   | ID do operador humano       |
| `cmp_`  | ID da empresa (em metadata) |

A anonimização é determinística por empresa, irreversível e consistente entre exportações.

<RequestExample>
  ```bash cURL theme={null}
  curl -s \
    -H "X-API-Key: mindo_xxxxxxxxxxxxxxxxxxxxxxxx" \
    "https://api.mindosoftware.com/api/v1/qualitative-export/messages?company_id=42&from=2026-04-01T00:00:00Z&to=2026-04-07T00:00:00Z"
  ```

  ```python Python theme={null}
  import requests
  import json

  response = requests.get(
      "https://api.mindosoftware.com/api/v1/qualitative-export/messages",
      headers={"X-API-Key": "mindo_xxxxxxxxxxxxxxxxxxxxxxxx"},
      params={
          "company_id": "42",
          "from": "2026-04-01T00:00:00Z",
          "to": "2026-04-07T00:00:00Z",
          "include": "classifications,extractions,tool_calls,trace_id",
      },
      stream=True,
  )

  for line in response.iter_lines(decode_unicode=True):
      if not line:
          continue
      obj = json.loads(line)
      if obj.get("_truncated"):
          print(f"Próxima página: {obj['next_cursor']}")
          continue
      print(obj["message_id"], obj["content"]["text"])
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.mindosoftware.com/api/v1/qualitative-export/messages?" +
      new URLSearchParams({
        company_id: "42",
        from: "2026-04-01T00:00:00Z",
        to: "2026-04-07T00:00:00Z",
        include: "classifications,extractions,tool_calls,trace_id",
      }),
    {
      headers: { "X-API-Key": "mindo_xxxxxxxxxxxxxxxxxxxxxxxx" },
    }
  );

  const text = await response.text();
  const lines = text.split("\n").filter(Boolean);

  for (const line of lines) {
    const obj = JSON.parse(line);
    if (obj._truncated) {
      console.log("Próxima página:", obj.next_cursor);
      continue;
    }
    console.log(obj.message_id, obj.content.text);
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - OK (NDJSON, uma linha por mensagem) theme={null}
  {
    "message_id": "msg_a1b2c3d4e5f6a1b2c3d4e5f6",
    "conversation_id": "conv_f6e5d4c3b2a1f6e5d4c3b2a1",
    "occurred_at": "2026-04-05T12:30:45.123456",
    "channel": "META_WHATSAPP",
    "direction": "inbound",
    "author": {
      "type": "contact",
      "id": "ctc_a1b2c3d4e5f6a1b2c3d4e5f6",
      "display_name": null
    },
    "agent": null,
    "content": {
      "type": "text",
      "text": "Olá, quero consultar o status do meu pedido",
      "media_url": null,
      "media_mime": null
    },
    "context": {
      "reply_to_message_id": null,
      "thread_position": null
    },
    "metadata": {
      "client_id": "cmp_d4c3b2a1f6e5d4c3b2a1f6e5",
      "company_id": 42,
      "exported_at": "2026-05-05T18:30:00.000000Z",
      "schema_version": "1.0"
    },
    "classifications": [
      { "label": "Intenção", "value": "consulta_pedido" }
    ]
  }
  ```

  ```json 400 - Parâmetro inválido theme={null}
  {
    "error": {
      "code": "INVALID_PARAMETER",
      "message": "from must be < to"
    }
  }
  ```

  ```json 401 - Não autenticado theme={null}
  {
    "error": {
      "code": "AUTH_REQUIRED",
      "message": "API Key required (X-API-Key header)"
    }
  }
  ```

  ```json 403 - Sem acesso theme={null}
  {
    "error": {
      "code": "COMPANY_ACCESS_DENIED",
      "message": "API key has no access to company_id=123"
    }
  }
  ```
</ResponseExample>

## Mapeamento de canais

| Valor do parâmetro `channel` | Plataformas incluídas                                   |
| ---------------------------- | ------------------------------------------------------- |
| `whatsapp`                   | META\_WHATSAPP, WHATSAPP\_EVOLUTION, MANYCHAT\_WHATSAPP |
| `instagram`                  | INSTAGRAM, MANYCHAT\_INSTAGRAM                          |
| `messenger`                  | MESSENGER                                               |
| `all`                        | Todas as plataformas                                    |

## Erros mid-stream

Se ocorrer um erro após o stream já ter iniciado, ele é emitido como uma linha NDJSON adicional:

```json theme={null}
{"_error": {"code": "ERROR_CODE", "message": "descrição do erro"}}
```

## Limites

| Restrição                       | Valor                     |
| ------------------------------- | ------------------------- |
| Intervalo máximo de datas       | 31 dias                   |
| Máximo de linhas por requisição | 10.000                    |
| Padrão de linhas por requisição | 5.000                     |
| Mensagens excluídas             | Excluídas automaticamente |

## Casos de uso

<AccordionGroup>
  <Accordion title="Análise de qualidade das respostas dos agentes">
    Exportar apenas mensagens outgoing de um agente específico com tool\_calls para avaliar quais ferramentas foram usadas e com qual sucesso:

    ```
    GET /api/v1/qualitative-export/messages?company_id=42&from=2026-04-01T00:00:00Z&to=2026-04-07T00:00:00Z&agent_id=5&direction=outgoing&include=classifications,tool_calls,trace_id
    ```
  </Accordion>

  <Accordion title="Dataset de treinamento para classificadores">
    Exportar mensagens incoming com suas classificações para avaliar ou retreinar modelos:

    ```
    GET /api/v1/qualitative-export/messages?company_id=42&from=2026-04-01T00:00:00Z&to=2026-04-30T00:00:00Z&direction=incoming&include=classifications,extractions
    ```
  </Accordion>

  <Accordion title="Auditoria cross-platform">
    Exportar todas as mensagens de múltiplas empresas para auditoria comparativa:

    ```
    GET /api/v1/qualitative-export/messages?company_id=42,57&from=2026-04-01T00:00:00Z&to=2026-04-07T00:00:00Z&include=classifications
    ```
  </Accordion>

  <Accordion title="Debugging de agentes com Langfuse">
    Exportar mensagens com `trace_id` para correlacionar com rastreamentos no Langfuse:

    ```
    GET /api/v1/qualitative-export/messages?company_id=42&from=2026-04-05T10:00:00Z&to=2026-04-05T12:00:00Z&include=tool_calls,trace_id
    ```
  </Accordion>
</AccordionGroup>
