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

# API de Analítica

> Consulte a analítica das suas conversas: métricas agregadas por agente e canal, e o detalhe de cada conversa

A API de Analítica é um endpoint só de leitura que devolve os dados de conversas da sua empresa. Tem dois modos:

* **`type=metrics`**: valores agregados por agente e canal (conversas, transferências para um humano, contenção da IA, duração das chamadas, notificações de WhatsApp, recontactos e séries diárias e horárias). Utilize-o para relatórios e dashboards.
* **`type=chats`**: uma linha por conversa com todos os seus parâmetros, paginada. Utilize-o para exportar ou reconciliar os dados em bruto.

## 🔑 Autenticação

Cada pedido necessita de uma chave de API com a permissão **`analytics_api`**. Peça-a à equipa da Lovi; é diferente da chave utilizada pelo widget web.

Envie a chave no cabeçalho `x-api-key` (recomendado) ou como parâmetro de query `access_key`.

```bash theme={null}
curl -H "x-api-key: YOUR_API_KEY" \
  "https://cloud.lovi.ai/functions/v1/analytics?type=metrics&date_from=2026-10-01&date_to=2026-10-31"
```

> [!NOTE]
>
> A empresa é sempre obtida a partir da chave. Uma chave só pode ler os dados da sua própria empresa e não existe nenhum parâmetro para escolher outra.

## Endpoint

```
GET https://cloud.lovi.ai/functions/v1/analytics
```

### Parâmetros de query

| Parâmetro | Obrigatório | Descrição |
| - | - | - |
| `type` | Sim | `metrics` ou `chats`. |
| `date_from` | Sim | Primeiro dia, `YYYY-MM-DD`, inclusive. |
| `date_to` | Sim | Último dia, `YYYY-MM-DD`, inclusive. O intervalo pode abranger até **31 dias**. |
| `tz` | Não | Fuso horário IANA utilizado para interpretar os dias e construir as séries diárias e horárias. Por omissão `Europe/Madrid`. Exemplos: `UTC`, `America/Bogota`. |
| `channels` | Não | Canais separados por vírgulas: `voice`, `whatsapp`, `web`, `instagram`, `facebook`… |
| `agents` | Não | Ids de agentes (UUID) separados por vírgulas. |
| `languages` | Não | Códigos de idioma do utilizador separados por vírgulas, p. ex. `es,pt`. |
| `limit` | Não | Apenas `chats`. Conversas por página, 1–100. Por omissão 100. |
| `cursor` | Não | Apenas `chats`. O `next_cursor` devolvido pela página anterior. |

Os dias são interpretados em `tz`: `date_from=2026-10-01&date_to=2026-10-31` com `tz=Europe/Madrid` abrange desde 1 de outubro às 00:00 até 1 de novembro às 00:00, hora de Madrid. As mudanças de hora (horário de verão) são tratadas automaticamente.

Todos os timestamps nas respostas estão em **UTC** (ISO 8601 terminado em `Z`).

## Métricas (`type=metrics`)

Devolve uma entrada por agente e canal, ordenada por número de conversas.

```bash theme={null}
curl -H "x-api-key: YOUR_API_KEY" \
  "https://cloud.lovi.ai/functions/v1/analytics?type=metrics&date_from=2026-10-01&date_to=2026-10-31&channels=voice"
```

```json theme={null}
{
  "tz": "Europe/Madrid",
  "from": "2026-09-30T22:00:00.000Z",
  "to": "2026-10-31T23:00:00.000Z",
  "data": [
    {
      "agent": { "id": "2fef9696-0662-4e49-bb83-7564ff25c7ef", "name": "Voice assistant" },
      "channel": "voice",
      "conversations": 12480,
      "escalated": 4120,
      "contained_pct": 67.0,
      "avg_duration_s": { "all": 109, "without_transfer": 120, "with_transfer": 86 },
      "whatsapp_notifications": 1210,
      "unique_users": 7950,
      "recontacts": 2480,
      "daily": [
        { "date": "2026-10-01", "conversations": 410, "escalated": 151 }
      ],
      "hourly": [
        { "hour": 9, "conversations": 1320 },
        { "hour": 10, "conversations": 1544 }
      ]
    }
  ]
}
```

| Campo | Descrição |
| - | - |
| `agent` | Agente de IA que atendeu as conversas (`id`, `name`). `null` se a conversa não tiver agente. |
| `channel` | Canal da conversa. `n/a` se não tiver sido registado. |
| `conversations` | Conversas iniciadas no intervalo (na voz, chamadas). |
| `escalated` | Conversas transferidas para um humano (têm um evento `chat_escalated`). |
| `contained_pct` | `(conversations − escalated) / conversations × 100`. Percentagem atendida pela IA sem transferência. **Não é uma taxa de resolução.** |
| `avg_duration_s` | Duração média da chamada em segundos: global, sem transferência e com transferência. `null` nos canais sem duração (só a voz a regista). |
| `whatsapp_notifications` | Notificações de WhatsApp enviadas a partir dessas conversas (eventos `notification_sent`). |
| `unique_users` | Utilizadores distintos (na voz, números de telefone distintos). |
| `recontacts` | Utilizadores com 2 ou mais conversas no intervalo. Taxa de recontacto = `recontacts / unique_users`. |
| `daily` | Conversas e transferências por dia, em `tz`. Os dias sem conversas são omitidos. |
| `hourly` | Conversas por hora do dia (0–23), em `tz`. As horas sem conversas são omitidas. |

> [!TIP]
>
> **Comparações com o mês anterior**: chame o endpoint duas vezes, uma por mês, e calcule a diferença.
> **Contenção apenas nos dias com atendimento humano**: utilize `daily` e mantenha os dias com `escalated > 0`.

## Conversas (`type=chats`)

Devolve as conversas do intervalo uma a uma, das mais antigas para as mais recentes, 100 por página por omissão.

```bash theme={null}
curl -H "x-api-key: YOUR_API_KEY" \
  "https://cloud.lovi.ai/functions/v1/analytics?type=chats&date_from=2026-10-01&date_to=2026-10-01&channels=voice&limit=100"
```

```json theme={null}
{
  "data": [
    {
      "id": "29d67962-119b-406c-b172-d66cf179975e",
      "created_at": "2026-10-01T05:58:40.4431Z",
      "ended_at": "2026-10-01T05:58:48Z",
      "last_message_at": null,
      "status": "aiagent",
      "channel": "voice",
      "language": "es",
      "agent": { "id": "2fef9696-0662-4e49-bb83-7564ff25c7ef", "name": "Voice assistant" },
      "escalated_to_human": true,
      "escalated_at": "2026-10-01T05:58:45.12Z",
      "attended_by_human": false,
      "human_agents": [],
      "whatsapp_notifications": 0,
      "parameters": {
        "channel": "voice",
        "call_length": 8,
        "phone_call": "+34600000000",
        "voice_inbound": true,
        "conversation_recording_url": "https://…"
      }
    }
  ],
  "next_cursor": "MjAyNi0xMC0wMVQwNjowMjoxMS4xMjM0NTZ8…"
}
```

| Campo | Descrição |
| - | - |
| `id` | Id da conversa. |
| `created_at`, `ended_at`, `last_message_at` | Início, fim e última mensagem, em UTC. |
| `status` | Com quem está a conversa: `aiagent`, `human`, `queue` (a aguardar um humano) ou `push` (iniciada por uma notificação). |
| `channel`, `language` | Copiados de `parameters.channel` e `parameters.lang_user` por conveniência. |
| `agent` | Agente de IA da conversa (`id`, `name`). |
| `escalated_to_human`, `escalated_at` | Se foi transferida para um humano e quando (primeira transferência). |
| `attended_by_human` | Se um agente humano escreveu pelo menos uma mensagem. |
| `human_agents` | Agentes humanos que escreveram na conversa (`id`, `name`). Pode estar vazio mesmo quando `attended_by_human` é `true`: as mensagens enviadas a partir de um CRM externo não são atribuídas a uma pessoa. |
| `whatsapp_notifications` | Notificações de WhatsApp enviadas a partir da conversa. |
| `parameters` | **Todos** os parâmetros guardados para a conversa, sem filtrar. As chaves dependem do canal e da configuração do seu agente (na voz: `call_length`, `phone_call`, `voice_inbound`, `session_call_id`, `conversation_recording_url`…; além de qualquer parâmetro personalizado que o seu agente guarde). |

### Paginação

Enquanto `next_cursor` não for `null`, peça a página seguinte com os mesmos parâmetros mais `cursor=<next_cursor>`. As conversas estão ordenadas por `created_at` e `id`, pelo que nenhuma conversa se repete nem é omitida entre páginas.

```python theme={null}
import requests

URL = "https://cloud.lovi.ai/functions/v1/analytics"
HEADERS = {"x-api-key": "YOUR_API_KEY"}
params = {"type": "chats", "date_from": "2026-10-01", "date_to": "2026-10-31", "channels": "voice"}

chats = []
while True:
    page = requests.get(URL, headers=HEADERS, params=params, timeout=60).json()
    chats.extend(page["data"])
    if not page["next_cursor"]:
        break
    params["cursor"] = page["next_cursor"]

print(len(chats), "conversations")
```

## Erros

Os erros devolvem um corpo JSON `{ "success": false, "message": "…" }`.

| Código | Quando |
| - | - |
| `400` | Chave em falta (`Missing access_key parameter`), `type` inválido, datas inválidas, intervalo superior a 31 dias, `tz`, `limit`, `agents`, `channels` ou `cursor` inválidos. |
| `403` | A chave existe mas não tem a permissão `analytics_api`. |
| `404` | Chave inválida ou revogada. |
| `405` | Método diferente de `GET`. |
| `500` | Erro interno. Tente novamente após alguns segundos; se persistir, contacte o suporte indicando a hora do pedido. |

## Limites e boas práticas

* Até **31 dias** por pedido e **100 conversas** por página.
* Um pedido `metrics` de um mês completo demora alguns segundos. Para um relatório mensal ou diário, basta uma chamada por período: não é necessário fazer polling.
* Para exportar um mês inteiro de conversas, percorra as páginas uma a seguir à outra, em vez de em paralelo.
* Mantenha a chave secreta e do lado do servidor. Não a utilize num navegador nem numa aplicação móvel.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.