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

> Read your conversation analytics: aggregated metrics per agent and channel, and the detail of every conversation

The Analytics API is a read-only endpoint that returns your company's conversation data. It has two modes:

* **`type=metrics`**: aggregated figures per agent and channel (conversations, transfers to a human, AI containment, call duration, WhatsApp notifications, recontacts, and daily and hourly series). Use it for reports and dashboards.
* **`type=chats`**: one row per conversation with all its parameters, paginated. Use it to export or reconcile the raw data.

## 🔑 Authentication

Every request needs an API key with the **`analytics_api`** permission. Ask the Lovi team for one; it is different from the key used by the web widget.

Send the key in the `x-api-key` header (recommended) or as the `access_key` query parameter.

```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]
>
> The company is always taken from the key. A key can only read its own company's data, and there is no parameter to choose another one.

## Endpoint

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

### Query parameters

| Parameter | Required | Description |
| - | - | - |
| `type` | Yes | `metrics` or `chats`. |
| `date_from` | Yes | First day, `YYYY-MM-DD`, inclusive. |
| `date_to` | Yes | Last day, `YYYY-MM-DD`, inclusive. The range can cover up to **31 days**. |
| `tz` | No | IANA time zone used to interpret the days and to build the daily and hourly series. Default `Europe/Madrid`. Examples: `UTC`, `America/Bogota`. |
| `channels` | No | Comma-separated channels: `voice`, `whatsapp`, `web`, `instagram`, `facebook`… |
| `agents` | No | Comma-separated agent ids (UUID). |
| `languages` | No | Comma-separated language codes of the user, e.g. `es,pt`. |
| `limit` | No | `chats` only. Conversations per page, 1–100. Default 100. |
| `cursor` | No | `chats` only. The `next_cursor` returned by the previous page. |

Days are interpreted in `tz`: `date_from=2026-10-01&date_to=2026-10-31` with `tz=Europe/Madrid` covers from 1 October 00:00 to 1 November 00:00 Madrid time. Daylight-saving changes are handled for you.

All timestamps in responses are **UTC** (ISO 8601 ending in `Z`).

## Metrics (`type=metrics`)

Returns one entry per agent and channel, ordered by number of conversations.

```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 }
      ]
    }
  ]
}
```

| Field | Description |
| - | - |
| `agent` | AI agent that handled the conversations (`id`, `name`). `null` if the conversation has no agent. |
| `channel` | Conversation channel. `n/a` if it was not recorded. |
| `conversations` | Conversations started in the range (for voice, calls). |
| `escalated` | Conversations transferred to a human (they have a `chat_escalated` event). |
| `contained_pct` | `(conversations − escalated) / conversations × 100`. Share handled by the AI without a transfer. **It is not a resolution rate.** |
| `avg_duration_s` | Average call duration in seconds: overall, without transfer and with transfer. `null` on channels without duration (only voice records it). |
| `whatsapp_notifications` | WhatsApp notifications sent from those conversations (`notification_sent` events). |
| `unique_users` | Distinct users (for voice, distinct phone numbers). |
| `recontacts` | Users with 2 or more conversations in the range. Recontact rate = `recontacts / unique_users`. |
| `daily` | Conversations and transfers per day, in `tz`. Days without conversations are omitted. |
| `hourly` | Conversations per hour of the day (0–23), in `tz`. Hours without conversations are omitted. |

> [!TIP]
>
> **Comparisons with the previous month**: call the endpoint twice, once per month, and compute the difference.
> **Containment on staffed days only**: use `daily` and keep the days with `escalated > 0`.

## Conversations (`type=chats`)

Returns the conversations of the range one by one, oldest first, 100 per page by default.

```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…"
}
```

| Field | Description |
| - | - |
| `id` | Conversation id. |
| `created_at`, `ended_at`, `last_message_at` | Start, end and last message, in UTC. |
| `status` | Who the conversation is with: `aiagent`, `human`, `queue` (waiting for a human) or `push` (started by a notification). |
| `channel`, `language` | Copied from `parameters.channel` and `parameters.lang_user` for convenience. |
| `agent` | AI agent of the conversation (`id`, `name`). |
| `escalated_to_human`, `escalated_at` | Whether it was transferred to a human, and when (first transfer). |
| `attended_by_human` | Whether a human agent wrote at least one message. |
| `human_agents` | Human agents who wrote in the conversation (`id`, `name`). It can be empty even when `attended_by_human` is `true`: messages sent from an external CRM are not attributed to a person. |
| `whatsapp_notifications` | WhatsApp notifications sent from the conversation. |
| `parameters` | **All** the parameters stored for the conversation, unfiltered. The keys depend on the channel and on your agent configuration (for voice: `call_length`, `phone_call`, `voice_inbound`, `session_call_id`, `conversation_recording_url`…; plus any custom parameter your agent saves). |

### Pagination

While `next_cursor` is not `null`, request the next page with the same parameters plus `cursor=<next_cursor>`. Conversations are ordered by `created_at` and `id`, so no conversation is repeated or skipped between pages.

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

## Errors

Errors return a JSON body `{ "success": false, "message": "…" }`.

| Code | When |
| - | - |
| `400` | Missing key (`Missing access_key parameter`), invalid `type`, invalid dates, range over 31 days, invalid `tz`, `limit`, `agents`, `channels` or `cursor`. |
| `403` | The key exists but does not have the `analytics_api` permission. |
| `404` | Invalid or revoked key. |
| `405` | Method other than `GET`. |
| `500` | Internal error. Retry after a few seconds; if it persists, contact support with the time of the request. |

## Limits and good practices

* Up to **31 days** per request and **100 conversations** per page.
* A `metrics` request for a full month takes a few seconds. For a monthly or daily report, one call per period is enough: there is no need to poll.
* To export a whole month of conversations, walk the pages one after another rather than in parallel.
* Keep the key secret and server-side. Do not use it in a browser or a mobile app.


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