
# Analytics & Reports API

Met deze alleen-lezen eindpunten kunt u de activiteit van uw account ophalen voor uw eigen dashboards en rapporten: aantallen berichtgebeurtenissen, kredietverbruik, AI-uitgaven en dezelfde grafieken en inzichten die het in-app dashboard toont. Deze gids behandelt:

- **Samenvatting** — tellers voor berichtvolume (verzonden, afgeleverd, gelezen, beantwoord, geboekt, aangemaakte contacten, tegoeden).
- **Tegoeden** — een gedetailleerd, gepagineerd grootboek van kredietgebruik met totalen en uitsplitsingen.
- **AI-kosten** — een dagelijks overzicht van AI-uitgaven.
- **Metriekreeksen** — een tijdreeks die klaar is voor grafieken voor een of meer metrieken, gegroepeerd op campagne, kanaal, AI-agent of nummer.
- **Gespreksresultaten** — hoe gesprekken eindigden, per door AI toegewezen resultaatlabel.
- **Dashboard-inzichten** en **Dashboard AI-inzichten** — de volledige gegevens achter het in-app dashboard, inclusief door AI geschreven samenvattingen.
- **Entiteitsactiviteit** — de tijdlijn van een enkel contact, deal of taak.
- **Geaggregeerde gebeurtenistellingen** — een verouderde, camelCase-vorm van Samenvatting die behouden is voor bestaande integraties.

Elk eindpunt op deze pagina vereist een exact bereik, niet beide: geef maximaal één van `campaign_id` (verouderd) of `agent_id` door waar het eindpunt dit accepteert. Het verzenden van beide retourneert `400`, en een id die niet op uw account staat retourneert `404` in plaats van `403`, zodat de id's van andere accounts niet te raden zijn.

Alle onderstaande paden zijn relatief ten opzichte van de API-basis-URL:

```
https://api.youraiconnector.com/v1
```

Elk verzoek moet worden geverifieerd. Zie [Authenticatie](authentication.md) voor de vier geaccepteerde methoden. De voorbeelden hier gebruiken de `X-API-Key`-header (en één queryparameter-vorm voor cURL).

---

## Datumbereik

Alle drie de endpoints accepteren dezelfde optionele datumfilters:

| Parameter | Beschrijving |
|---|---|
| `from` | Begin van het bereik, `YYYY-MM-DD`, inclusief. Standaard ingesteld op 30 dagen geleden. |
| `to` | Einde van het bereik, `YYYY-MM-DD`, inclusief. Standaard ingesteld op vandaag. |

Datums worden geïnterpreteerd in UTC. Het bereik is standaard ingesteld op de **laatste 30 dagen** en is begrensd op **366 dagen** — een breder bereik retourneert `400`. `from` mag niet na `to` liggen.

### De `truncated`-vlag

De endpoints **Samenvatting** en **Tegoeden** beperken het aantal records dat een enkel verzoek scant. Als uw bereik druk genoeg is om die limiet te bereiken, bevat het antwoord `"truncated": true`. Wanneer u dit ziet, zijn de cijfers gebaseerd op een gedeeltelijke scan — verklein uw datumbereik (of blader door de pagina's met een kleiner venster) om volledige cijfers te krijgen.

::: note
**Let op:** Kosten- en token-cijfers zijn alleen inbegrepen voor AI-aanroepen die worden gefactureerd aan uw eigen provider-API-sleutels. Wanneer kostencijfers voor uw account zijn verborgen, stelt het antwoord `"costs_redacted": true` in en worden de kostenvelden als nul geretourneerd.
:::


---

## Samenvatting berichtvolume

Retourneert geaggregeerde tellers voor berichtgebeurtenissen voor uw account, zowel als totalen voor het bereik als als een dagelijkse reeks. Elke dag in het bereik verschijnt in `by_date` — rustige dagen worden opgevuld met nullen. Filter optioneel naar een enkele campagne met `campaign_id`.

`GET /analytics/summary`

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `from` | Nee | Begin van het bereik, `YYYY-MM-DD`. |
| `to` | Nee | Einde van het bereik, `YYYY-MM-DD`. |
| `campaign_id` | Nee | Tel alleen gebeurtenissen die bij deze campagne horen. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/summary?from=2026-05-01&to=2026-05-31&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ from: "2026-05-01", to: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/summary?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/summary",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "totals": {
    "total": 1240,
    "sent": 800,
    "delivered": 760,
    "read": 540,
    "replied": 210,
    "booked": 35,
    "contact_created": 120,
    "credits_spent": 412.5,
    "credits_recharged": 500
  },
  "by_date": [
    {
      "date": "2026-05-01",
      "total": 40,
      "sent": 25,
      "delivered": 24,
      "read": 18,
      "replied": 7,
      "booked": 1,
      "contact_created": 4,
      "credits_spent": 13.5,
      "credits_recharged": 0
    }
  ],
  "truncated": false
}
```

Elk item in `by_date` heeft dezelfde teller-velden als `totals`, plus een `date`.

Als u een `campaign_id` doorgeeft die niet bij uw account hoort, is het antwoord `404` met `{ "success": false, "error": "Campaign not found" }`.

---

## Creditverbruik

Geeft het creditverbruik over het bereik terug: een gepagineerde lijst met individuele records, plus totalen per bereik en uitsplitsingen per reden en per campagne.

`GET /analytics/credits`

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `from` | Nee | Begin van het bereik, `YYYY-MM-DD`. |
| `to` | Nee | Einde van het bereik, `YYYY-MM-DD`. |
| `campaign_id` | Nee | Neem alleen verbruik op dat aan deze campagne is toegeschreven. |
| `limit` | Nee | Paginagrootte voor `records`, 1–100. Standaard is 50. |
| `cursor` | Nee | Geef de `next_cursor` van de vorige pagina door om de volgende pagina op te halen. |

> **Correcties versus verbruik:** Saldowijzigingen zoals bonussen, abonnementsverlengingen en correcties zijn **uitgesloten** van `totals` en de uitsplitsingen — dit is geen werkelijk verbruik. Ze verschijnen nog steeds in de `records`-lijst, gemarkeerd met `"is_adjustment": true`.

**Totalen en uitsplitsingen verschijnen alleen op de eerste pagina** (wanneer er geen `cursor` wordt meegegeven). Op latere pagina's worden `totals`, `by_reason`, `by_reason_cost` en `by_campaign` geretourneerd als `null` — alleen de `records`-array gaat door. Dit voorkomt dat het hele bereik voor elke pagina opnieuw moet worden gescand.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/credits?from=2026-05-01&to=2026-05-31&limit=50&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  from: "2026-05-01",
  to: "2026-05-31",
  limit: "50",
});
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/credits?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

// To page: pass data.next_cursor as ?cursor on the next request, until it is null.
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/credits",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31", "limit": 50},
)
data = res.json()

# To page: pass data["next_cursor"] as cursor on the next request, until it is None.
```

**Antwoord** (eerste pagina)

```json
{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "totals": {
    "credits_used": 412.5,
    "cost_usd": 1.284512,
    "records": 318
  },
  "by_reason": {
    "AI Message": 380.0,
    "Campaign Message": 32.5
  },
  "by_reason_cost": {
    "AI Message": 1.284512,
    "Campaign Message": 0
  },
  "by_campaign": {
    "Spring Promo": 250.0,
    "Reactivation": 162.5
  },
  "records": [
    {
      "id": "rec_abc123",
      "amount": 1,
      "timestamp": "2026-05-31T14:02:11.000Z",
      "reason": "AI Message",
      "is_adjustment": false,
      "campaign_id": "campaign123",
      "campaign_name": "Spring Promo",
      "contact_id": "contact456",
      "contact_name": "Jane Smith",
      "credit_type": "ai",
      "custom_keys_used": false,
      "description": null,
      "cost_usd": 0,
      "input_tokens": 0,
      "output_tokens": 0,
      "cache_read_tokens": 0,
      "cache_creation_tokens": 0,
      "ai_model": null,
      "request_id": null,
      "is_test": false
    }
  ],
  "next_cursor": "rec_abc123",
  "costs_redacted": false,
  "truncated": false
}
```

**Veldnotities:**

- `amount` — credits in rekening gebracht voor het record. Nul voor records die aan uw eigen provider-API-sleutel zijn gefactureerd.
- `is_adjustment` — `true` voor saldowijzigingen (uitgesloten van totalen/uitsplitsingen).
- `cost_usd`, `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_creation_tokens`, `ai_model`, `request_id` — alleen ingevuld bij records die aan uw eigen provider-API-sleutel zijn gefactureerd; anders nul of `null`.
- `is_test` — `true` voor playground/testruns, die nooit worden gefactureerd.
- `next_cursor` — de cursor voor de volgende pagina, of `null` wanneer er geen records meer zijn.

---

## AI-kostenoverzicht

Geeft het dagelijkse AI-uitgavenoverzicht voor uw account terug. Dit leest vooraf geaggregeerde dagtotalen, dus het is snel, zelfs over lange bereiken. Elke dag in het bereik verschijnt in `days` — rustige dagen worden met nul opgevuld.

`GET /analytics/ai-cost`

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `from` | Nee | Begin van bereik, `YYYY-MM-DD`. |
| `to` | Nee | Einde van bereik, `YYYY-MM-DD`. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/ai-cost?from=2026-05-01&to=2026-05-31&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ from: "2026-05-01", to: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/ai-cost?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/ai-cost",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "totals": {
    "total_usd": 12.4821,
    "byok_usd": 12.4821,
    "platform_usd": 0,
    "calls": 4210
  },
  "days": [
    {
      "date": "2026-05-01",
      "total_usd": 0.4012,
      "byok_usd": 0.4012,
      "platform_usd": 0,
      "input_usd": 0.18,
      "output_usd": 0.19,
      "cache_creation_usd": 0.02,
      "cache_read_usd": 0.0112,
      "calls": 140,
      "by_provider": { "anthropic": 0.4012 }
    }
  ],
  "costs_redacted": false
}
```

**Veldnotities:**

- `byok_usd` — uitgaven gefactureerd aan uw eigen provider-API-sleutels.
- `platform_usd` — het deel van de uitgaven dat via het platform liep in plaats van via uw eigen sleutel.
- `input_usd`, `output_usd`, `cache_creation_usd`, `cache_read_usd` — de kostencomponenten waaruit `total_usd` bestaat.
- `by_provider` — USD-uitgaven gegroepeerd op naam van de AI-provider.
- USD-bedragen worden alleen geretourneerd aan accounts die hun eigen providersleutel gebruiken. Voor accounts die met tegoed betalen, is elk USD-veld nul en is `costs_redacted` gelijk aan `true` (aantal aanroepen blijft zichtbaar).

---

## Metriekreeksen

Retourneert een of meer metriektijdreeksen in één aanroep, optioneel gegroepeerd op maximaal twee dimensies — het eindpunt om een grafiek aan te koppelen. Een enkel verzoek kan "verzonden en beantwoord per dag, per kanaal, voor deze campagne" beantwoorden zonder één aanroep per campagne.

`GET /analytics/series`

Elk antwoord bevat een `labels`-array (de tijdas, nul-gevuld over het hele bereik) en één item in `series` per groep, elk met één array per gevraagde metriek uitgelijnd op `labels`. Reeksen na `limit` worden niet verwijderd — ze worden samengevoegd tot `other_bucket`, berekend als het bereiktotaal minus de geretourneerde reeksen, zodat een weergegeven grafiek altijd optelt tot uw werkelijke cijfers; `truncated` is `true` wanneer dat gebeurt.

Waar de cijfers vandaan komen: `sent`, `delivered`, `read` en `replied` komen uit berichtrecords, die het kanaal en het verzendnummer bevatten. `booked`, `contact_created` en `credits_spent` komen uit de gebeurtenisstroom, die geen verzendnummer bevat, dus die metrieken belanden in de `null`-nummer-bucket wanneer u groepeert op `number`.

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `from` | Nee | Bereikstart, `YYYY-MM-DD`. Standaard 30 dagen geleden. |
| `to` | Nee | Bereikeinde, `YYYY-MM-DD`. Standaard vandaag. |
| `metrics` | Nee | Door komma's gescheiden lijst van `sent`, `ai_sent`, `human_sent`, `delivered`, `read`, `replied`, `booked`, `contact_created`, `credits_spent`. Standaard `sent,replied`. Een onbekende metriek retourneert `400`. |
| `group_by` | Nee | Door komma's gescheiden lijst van maximaal twee dimensies uit `date`, `campaign`, `channel`, `agent`, `number`. `date` wordt geaccepteerd maar heeft geen effect — elk antwoord bevat al de tijdas. Weglaten voor een enkele accountbrede reeks. |
| `granularity` | Nee | `day` (standaard), `week` of `month`. Week-buckets beginnen op maandag, maand-buckets op de 1e. |
| `limit` | Nee | Hoeveel reeksen moeten worden geretourneerd voordat de rest wordt samengevoegd tot `other_bucket`, 1–50. Standaard 12. |
| `campaign_id` | Nee | Tel alleen activiteit die bij deze campagne hoort. Verouderd; geef de voorkeur aan `agent_id`. |
| `agent_id` | Nee | Tel alleen activiteit die bij deze AI-agent hoort. |
| `channel` | Nee | Tel alleen activiteit op dit kanaal, bijvoorbeeld `whatsapp`. |

Het datumbereik van dit eindpunt is beperkt tot **92 dagen** (strenger dan de limiet van 366 dagen die elders op deze pagina wordt gebruikt).

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/series?from=2026-05-01&to=2026-05-31&metrics=sent,replied,booked&group_by=campaign,channel&limit=10&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  from: "2026-05-01",
  to: "2026-05-31",
  metrics: "sent,replied,booked",
  group_by: "campaign,channel",
  limit: "10",
});
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/series?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/series",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "from": "2026-05-01",
        "to": "2026-05-31",
        "metrics": "sent,replied,booked",
        "group_by": "campaign,channel",
        "limit": 10,
    },
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "granularity": "day",
  "labels": ["2026-05-01", "2026-05-02"],
  "group_by": ["campaign", "channel"],
  "metrics": ["sent", "replied", "booked"],
  "series": [
    {
      "key": {
        "campaign_id": "campaign123",
        "campaign_name": "Spring Promo",
        "channel": "whatsapp"
      },
      "total": 812,
      "metrics": {
        "sent": [40, 35],
        "replied": [12, 9],
        "booked": [2, 1]
      }
    }
  ],
  "other_bucket": {
    "series_count": 6,
    "total": 340,
    "metrics": {
      "sent": [18, 20],
      "replied": [5, 6],
      "booked": [0, 1]
    }
  },
  "truncated": true
}
```

**Veldnotities:**

- `key` — de identiteit van één reeks. Alleen de sleutels voor de gevraagde `group_by`-dimensies zijn aanwezig; een dimensie waarvan de waarde voor een rij onbekend is (een bericht zonder campagne, een gebeurtenis zonder kanaal) komt terug als `null` in plaats van te worden verwijderd, zodat reeksen nog steeds optellen tot de totalen.
- `other_bucket` — `null` wanneer er niets werd samengevoegd.
- Dit eindpunt retourneert `503` met `"error_code": "analytics_unavailable"` wanneer de rapportagedatabase geen antwoord kan geven voor uw account, in plaats van een `200` vol nullen — een grafiek met nullen zou als feit worden gelezen.

---

## Gespreksresultaten

Retourneert hoe gesprekken eindigden over een datumbereik: een dagelijks aantal voor elk resultaatlabel dat de AI heeft toegewezen, plus bereiktotalen voor antwoorden, boekingen, overdrachten naar een mens en gesprekken die de AI nooit heeft geclassificeerd.

`GET /analytics/outcomes`

Geef `group_by=tag` door om de tijdas samen te voegen en alleen bereiktotalen per label te krijgen — in die modus is `labels` leeg en is de `counts`-array van elk label leeg, terwijl `total` nog steeds is ingevuld.

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `from` | Nee | Bereikstart, `YYYY-MM-DD`. Standaard ingesteld op 30 dagen geleden. |
| `to` | Nee | Bereikeinde, `YYYY-MM-DD`. Standaard ingesteld op vandaag. |
| `campaign_id` | Nee | Tel alleen conversaties met contacten die momenteel in deze campagne zitten. Verouderd; gebruik liever `agent_id`. |
| `agent_id` | Nee | Tel alleen resultaten die bij deze AI-agent horen. |
| `group_by` | Nee | `date` (standaard) behoudt de tellingen per dag; `tag` voegt deze samen tot totalen voor het bereik. |

Het datumbereik van dit eindpunt is beperkt tot **92 dagen**.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/outcomes?from=2026-05-01&to=2026-05-31&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ from: "2026-05-01", to: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/outcomes?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/outcomes",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "group_by": "date",
  "labels": ["2026-05-01", "2026-05-02"],
  "by_tag": [
    { "tag": "interested", "total": 84, "counts": [3, 5] },
    { "tag": "not_interested", "total": 40, "counts": [1, 2] },
    { "tag": null, "total": 12, "counts": [0, 1] }
  ],
  "totals": {
    "sessions": 260,
    "replied": 210,
    "booked": 35,
    "human_alerted": 18,
    "unresolved": 12
  }
}
```

**Veldnotities:**

- `by_tag[].tag` — `null` voor conversaties waaraan de AI nooit een resultaatlabel heeft toegewezen.
- `totals.human_alerted` — conversaties die zijn overgedragen aan een mens; dit wordt bij elke overdracht vastgelegd en werd voorheen door geen enkel eindpunt getoond.
- Zelfde `503`/`analytics_unavailable`-houding als de metriekreeksen wanneer de rapportagedatabase geen antwoord kan geven.

---

## Dashboard-inzichten

Retourneert de volledige dashboard-payload voor een datumbereik in één aanroep: een heatmap van de antwoordfrequentie per weekdag en uur, het campagne-klassement, volume per kanaal, exacte totalen per verbinding, uitsplitsingen van metrieken per dag (accountbreed, per kanaal en per nummer), waar contacten vandaan komen, responstijd van de inbox en een feed met recente activiteiten. Dit is de meest uitgebreide rapportage-payload op de API — deze voedt direct het dashboard in de app.

`GET /analytics/dashboard-insights`

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `startDate` | Ja | Bereikstart, `YYYY-MM-DD`. |
| `endDate` | Ja | Bereikeinde, `YYYY-MM-DD`. |
| `campaignId` | Nee | Neem alleen activiteit op die bij deze campagne hoort (`campaign_id` wordt ook geaccepteerd). Verouderd; gebruik liever `agent_id`. |
| `agent_id` | Nee | Neem alleen activiteit op die bij deze AI-agent hoort (`agentId` wordt ook geaccepteerd). Binnen een agent-scope wordt het campagne-klassement uitsluitend opgebouwd uit de activiteit van die agent. |

Dit eindpunt gebruikt `startDate`/`endDate` (niet `from`/`to`) omdat het de implementatie deelt met het in-app dashboard. Het bereik is beperkt tot 92 dagen en wordt **afgekapt, niet afgewezen**, wanneer het breder is.

> **Null betekent niet beschikbaar, niet nul.** Verschillende blokken (`numberStats`, `channelDailySeries`, `metricDailyBreakdown`, `contactsByCountry`) worden berekend op basis van de rapportagedatabase en retourneren `null` wanneer deze geen antwoord kan geven voor uw account. Render een `null`-blok niet als een lege grafiek.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/dashboard-insights?startDate=2026-05-01&endDate=2026-05-31&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ startDate: "2026-05-01", endDate: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/dashboard-insights?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/dashboard-insights",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"startDate": "2026-05-01", "endDate": "2026-05-31"},
)
data = res.json()
```

**Antwoord** (ingekort — deze payload is groot; zie de [API-referentie](reference.md) voor het volledige schema)

```json
{
  "success": true,
  "data": {
    "heatmap": {
      "buckets": [
        { "weekday": 1, "hour": 9, "sent": 12, "replied": 5, "replyRate": 0.42 }
      ]
    },
    "topCampaigns": [
      { "campaignId": "campaign123", "name": "Spring Promo", "sent": 420, "replied": 180, "booked": 22, "replyRate": 0.43, "creditsSpent": 210.5 }
    ],
    "channelVolume": [
      { "channel": "whatsapp", "sent": 800, "received": 540, "lastMessageAt": "2026-05-31T14:02:11.000Z" }
    ],
    "inboxSla": { "medianFirstResponseMs": 92000, "sampleSize": 140 },
    "activityFeed": [
      { "id": "evt_1", "kind": "booked", "at": "2026-05-31T14:02:11.000Z", "contactId": "contact456", "contactName": "Jane Smith", "campaignId": "campaign123", "campaignName": "Spring Promo", "label": "Jane Smith booked an appointment" }
    ],
    "numberStats": null,
    "channelDailySeries": null,
    "metricDailyBreakdown": null,
    "contactsByCountry": null,
    "ai_human_split": null
  }
}
```

**Veldnotities:**

- `heatmap.buckets[].weekday` — `0` is zondag tot en met `6` is zaterdag.
- `numberStats`, `channelDailySeries`, `metricDailyBreakdown`, `contactsByCountry`, `ai_human_split` — elk is onafhankelijk `null` wanneer de rapportagedatabase niet beschikbaar is voor uw account; alle andere blokken worden nog steeds geretourneerd.

---

## AI-inzichten voor het dashboard

Retourneert drie korte, door AI geschreven inzichten over de berichtgeving van het account over een datumbereik: één succes, één punt om in de gaten te houden en één tip — zinnen die u direct in een rapport kunt plakken in plaats van cijfers die u zelf nog moet interpreteren. Uitsluitend gegenereerd op basis van de eigen berichtmetrieken van het account.

`GET /analytics/dashboard-ai-insights`

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `startDate` | Ja | Bereikstart, `YYYY-MM-DD`. |
| `endDate` | Ja | Bereikeinde, `YYYY-MM-DD`. |

Dit eindpunt is accountbreed — het vereist geen campagne- of agent-scope.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/dashboard-ai-insights?startDate=2026-05-01&endDate=2026-05-31&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ startDate: "2026-05-01", endDate: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/dashboard-ai-insights?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/dashboard-ai-insights",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"startDate": "2026-05-01", "endDate": "2026-05-31"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "data": {
    "insights": [
      { "tone": "win", "title": "Reply rate is up", "detail": "Your reply rate climbed to 43% this period, up from 36% the period before." },
      { "tone": "watch", "title": "Bookings slowed midweek", "detail": "Wednesday bookings dropped to a third of Monday's, worth a look at your Wednesday follow-up timing." },
      { "tone": "tip", "title": "Re-send to non-repliers", "detail": "212 contacts received a message but never replied — a short follow-up template often recovers 10-15% of them." }
    ]
  }
}
```

Het ontbreken van `startDate` of `endDate` resulteert in `400`.

---

## Tijdlijn van entiteitsactiviteit

Geeft de activiteit van een enkele contactpersoon, deal of taak terug als één tijdlijn, van nieuw naar oud: wat er is gebeurd en wanneer, verdeeld over berichten, afspraken, notities en statuswijzigingen. Gebruik dit om de vraag "wat is er met deze persoon gebeurd" te beantwoorden zonder verschillende lijsteindpunten aan elkaar te hoeven knopen.

`GET /analytics/entity-activity`

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `entityType` | Ja | `contact`, `deal` of `task`. |
| `entityId` | Ja | ID van het record waarvan de tijdlijn moet worden opgehaald. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/entity-activity?entityType=contact&entityId=contact456&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ entityType: "contact", entityId: "contact456" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/entity-activity?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/entity-activity",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"entityType": "contact", "entityId": "contact456"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "evt_9",
        "kind": "appointment_booked",
        "at": "2026-05-31T14:02:11.000Z",
        "label": "Booked an appointment for June 3",
        "detail": "Consultation call, 30 minutes"
      },
      {
        "id": "evt_8",
        "kind": "message_replied",
        "at": "2026-05-31T13:58:02.000Z",
        "label": "Replied: \"Yes, that time works\""
      }
    ]
  }
}
```

Een ontbrekende of ongeldige `entityType`/`entityId` resulteert in `400`. Een entiteit die niet bestaat in uw account resulteert in `404`, zodat ID's van andere accounts niet te raden zijn.

---

## Geaggregeerde gebeurtenistellingen (verouderd)

Geeft dezelfde geaggregeerde gebeurtenistellingen terug als [Samenvatting berichtvolume](#message-volume-summary), maar in de camelCase-vorm (`contactCreated` in plaats van `contact_created`, `byDate` in plaats van `by_date`) waar sommige oudere integraties op gebouwd zijn. Geef de voorkeur aan `/analytics/summary` voor nieuwe integraties — dit eindpunt bestaat alleen zodat het in-app dashboard en de API dezelfde implementatie delen.

`GET /analytics/aggregate`

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `startDate` | Nee | Begin van het bereik, ISO-datum of datum-tijd. Standaard ingesteld op hetzelfde venster dat `/analytics/summary` gebruikt. |
| `endDate` | Nee | Einde van het bereik, ISO-datum of datum-tijd. |
| `campaignId` | Nee | Tel alleen gebeurtenissen die bij deze campagne horen (`campaign_id` ook geaccepteerd). Verouderd; geef de voorkeur aan `agent_id`. |
| `agent_id` | Nee | Tel alleen gebeurtenissen die bij deze AI-agent horen (`agentId` ook geaccepteerd). |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/analytics/aggregate?startDate=2026-05-01&endDate=2026-05-31&apiKey=YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "data": {
    "from": "2026-05-01",
    "to": "2026-05-31",
    "total": 1240,
    "byAnalyticType": {
      "total": 1240,
      "sent": 800,
      "delivered": 760,
      "read": 540,
      "replied": 210,
      "booked": 35,
      "contactCreated": 120,
      "creditsSpent": 412.5,
      "creditsRecharged": 500
    },
    "byDate": [
      { "date": "2026-05-01", "byAnalyticType": { "total": 40, "sent": 25, "delivered": 24, "read": 18, "replied": 7, "booked": 1, "contactCreated": 4, "creditsSpent": 13.5, "creditsRecharged": 0 } }
    ]
  }
}
```

---

## Rollup van sub-accounts voor bureaus


---

## Analytics API-fouten

Analytics-endpoints retourneren de standaard fouten-envelop:

```json
{
  "success": false,
  "error": "Date range too large. Maximum is 366 days."
}
```

Op een analyse-eindpunt resulteert een ongeldige datumnotatie of een venster buiten bereik in `400`, en een onbekende `campaign_id` of `agent_id` resulteert in `404`. Het verzenden van zowel `campaign_id` als `agent_id` naar een eindpunt dat beide accepteert, is ook een `400` — geef er maximaal één door. De rapportage-eindpunten die alleen voor PG zijn (Metriekreeksen, Conversatieresultaten, Bureau-rollup) geven `503` terug met `"error_code": "analytics_unavailable"` in plaats van een `200` vol nullen wanneer de rapportagedatabase geen antwoord kan geven voor uw account — probeer het kort daarna opnieuw. De gedeelde codes die elk eindpunt kan teruggeven — `401`, `403` (uw abonnement bevat geen API-toegang, of bij de bureau-rollup heeft uw account niet de rol Bureau/Ontwikkelaar), `429` (snelheidslimiet) en `500` — staan vermeld met instructies voor opnieuw proberen in [Fouten & Paginering](errors-and-pagination.md).

---

## Volgende stappen

- [Authenticatie](authentication.md) — de vier manieren om een verzoek te authenticeren.
- [Fouten & Snelheidslimieten](errors-and-pagination.md) — statuscodes en de limiet van 300 verzoeken per minuut.
- [Campagnes API](campaigns.md) — de campagnes waarop deze cijfers kunnen worden gefilterd.
