> ## 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.

# Analytics API

> Leggi le analitiche delle tue conversazioni: metriche aggregate per agente e canale, e il dettaglio di ogni conversazione

L'Analytics API è un endpoint di sola lettura che restituisce i dati delle conversazioni della tua azienda. Ha due modalità:

* **`type=metrics`**: cifre aggregate per agente e canale (conversazioni, trasferimenti a un operatore umano, contenimento dell'IA, durata delle chiamate, notifiche WhatsApp, ricontatti e serie giornaliere e orarie). Usala per report e dashboard.
* **`type=chats`**: una riga per conversazione con tutti i suoi parametri, paginata. Usala per esportare o riconciliare i dati grezzi.

## 🔑 Autenticazione

Ogni richiesta richiede una chiave API con il permesso **`analytics_api`**. Richiedila al team di Lovi; è diversa dalla chiave usata dal widget web.

Invia la chiave nell'header `x-api-key` (consigliato) o come parametro di 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]
>
> L'azienda viene sempre ricavata dalla chiave. Una chiave può leggere solo i dati della propria azienda e non esiste alcun parametro per sceglierne un'altra.

## Endpoint

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

### Parametri di query

| Parametro | Obbligatorio | Descrizione |
| - | - | - |
| `type` | Sì | `metrics` o `chats`. |
| `date_from` | Sì | Primo giorno, `YYYY-MM-DD`, incluso. |
| `date_to` | Sì | Ultimo giorno, `YYYY-MM-DD`, incluso. L'intervallo può coprire fino a **31 giorni**. |
| `tz` | No | Fuso orario IANA usato per interpretare i giorni e costruire le serie giornaliere e orarie. Predefinito `Europe/Madrid`. Esempi: `UTC`, `America/Bogota`. |
| `channels` | No | Canali separati da virgole: `voice`, `whatsapp`, `web`, `instagram`, `facebook`… |
| `agents` | No | ID degli agenti (UUID) separati da virgole. |
| `languages` | No | Codici lingua dell'utente separati da virgole, ad es. `es,pt`. |
| `limit` | No | Solo `chats`. Conversazioni per pagina, 1–100. Predefinito 100. |
| `cursor` | No | Solo `chats`. Il `next_cursor` restituito dalla pagina precedente. |

I giorni vengono interpretati in `tz`: `date_from=2026-10-01&date_to=2026-10-31` con `tz=Europe/Madrid` copre dal 1° ottobre alle 00:00 al 1° novembre alle 00:00, ora di Madrid. I cambi di ora legale sono gestiti automaticamente.

Tutti i timestamp nelle risposte sono in **UTC** (ISO 8601 con suffisso `Z`).

## Metriche (`type=metrics`)

Restituisce una voce per agente e canale, ordinata per numero di conversazioni.

```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 | Descrizione |
| - | - |
| `agent` | Agente IA che ha gestito le conversazioni (`id`, `name`). `null` se la conversazione non ha un agente. |
| `channel` | Canale della conversazione. `n/a` se non è stato registrato. |
| `conversations` | Conversazioni avviate nell'intervallo (per la voce, chiamate). |
| `escalated` | Conversazioni trasferite a un operatore umano (hanno un evento `chat_escalated`). |
| `contained_pct` | `(conversations − escalated) / conversations × 100`. Quota gestita dall'IA senza trasferimento. **Non è un tasso di risoluzione.** |
| `avg_duration_s` | Durata media delle chiamate in secondi: complessiva, senza trasferimento e con trasferimento. `null` sui canali senza durata (solo la voce la registra). |
| `whatsapp_notifications` | Notifiche WhatsApp inviate da quelle conversazioni (eventi `notification_sent`). |
| `unique_users` | Utenti distinti (per la voce, numeri di telefono distinti). |
| `recontacts` | Utenti con 2 o più conversazioni nell'intervallo. Tasso di ricontatto = `recontacts / unique_users`. |
| `daily` | Conversazioni e trasferimenti per giorno, in `tz`. I giorni senza conversazioni vengono omessi. |
| `hourly` | Conversazioni per ora del giorno (0–23), in `tz`. Le ore senza conversazioni vengono omesse. |

> [!TIP]
>
> **Confronti con il mese precedente**: chiama l'endpoint due volte, una per mese, e calcola la differenza.
> **Contenimento solo nei giorni con operatori presenti**: usa `daily` e mantieni i giorni con `escalated > 0`.

## Conversazioni (`type=chats`)

Restituisce le conversazioni dell'intervallo una per una, dalla più vecchia, 100 per pagina per impostazione predefinita.

```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 | Descrizione |
| - | - |
| `id` | ID della conversazione. |
| `created_at`, `ended_at`, `last_message_at` | Inizio, fine e ultimo messaggio, in UTC. |
| `status` | Con chi è la conversazione: `aiagent`, `human`, `queue` (in attesa di un operatore umano) o `push` (avviata da una notifica). |
| `channel`, `language` | Copiati da `parameters.channel` e `parameters.lang_user` per comodità. |
| `agent` | Agente IA della conversazione (`id`, `name`). |
| `escalated_to_human`, `escalated_at` | Se è stata trasferita a un operatore umano e quando (primo trasferimento). |
| `attended_by_human` | Se un agente umano ha scritto almeno un messaggio. |
| `human_agents` | Agenti umani che hanno scritto nella conversazione (`id`, `name`). Può essere vuoto anche quando `attended_by_human` è `true`: i messaggi inviati da un CRM esterno non vengono attribuiti a una persona. |
| `whatsapp_notifications` | Notifiche WhatsApp inviate dalla conversazione. |
| `parameters` | **Tutti** i parametri memorizzati per la conversazione, senza filtri. Le chiavi dipendono dal canale e dalla configurazione del tuo agente (per la voce: `call_length`, `phone_call`, `voice_inbound`, `session_call_id`, `conversation_recording_url`…; più qualsiasi parametro personalizzato che il tuo agente salva). |

### Paginazione

Finché `next_cursor` non è `null`, richiedi la pagina successiva con gli stessi parametri più `cursor=<next_cursor>`. Le conversazioni sono ordinate per `created_at` e `id`, quindi nessuna conversazione viene ripetuta o saltata tra una pagina e l'altra.

```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")
```

## Errori

Gli errori restituiscono un corpo JSON `{ "success": false, "message": "…" }`.

| Codice | Quando |
| - | - |
| `400` | Chiave mancante (`Missing access_key parameter`), `type` non valido, date non valide, intervallo superiore a 31 giorni, `tz`, `limit`, `agents`, `channels` o `cursor` non validi. |
| `403` | La chiave esiste ma non ha il permesso `analytics_api`. |
| `404` | Chiave non valida o revocata. |
| `405` | Metodo diverso da `GET`. |
| `500` | Errore interno. Riprova dopo qualche secondo; se persiste, contatta il supporto indicando l'ora della richiesta. |

## Limiti e buone pratiche

* Fino a **31 giorni** per richiesta e **100 conversazioni** per pagina.
* Una richiesta `metrics` per un mese intero richiede qualche secondo. Per un report mensile o giornaliero basta una chiamata per periodo: non serve fare polling.
* Per esportare un intero mese di conversazioni, scorri le pagine una dopo l'altra anziché in parallelo.
* Mantieni la chiave segreta e lato server. Non usarla in un browser o in un'app mobile.


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