Skip to main content

Analytics

Aggregated trends across a user's calls, built from the per-call sentiment / tone fields already produced by analysis. Nothing here is a fresh LLM call — it's SQL aggregation over stored analysis results, so it's cheap to poll.

GET /api/analytics/vibe

Sentiment and tone distribution/trend for the authenticated user's calls.

Query parameters

ParamTypeDefaultNotes
daysint30Look-back window in days.

Response — 200 OK

{
"sentimentDistribution": { "positive": 12, "neutral": 5, "negative": 2, "mixed": 1 },
"userSentimentDistribution": { "positive": 14, "neutral": 4, "negative": 2 },
"contactSentimentDistribution": { "positive": 9, "neutral": 6, "negative": 5 },
"sentimentTrend": [
{ "week": "2026-06-01", "counts": { "positive": 4, "neutral": 2, "negative": 1 } },
{ "week": "2026-06-08", "counts": { "positive": 5, "neutral": 1, "negative": 0 } }
],
"toneProfile": {
"formality": { "formal": 6, "semi-formal": 9, "informal": 3 },
"energy": { "low": 2, "medium": 11, "high": 5 },
"pace": { "slow": 1, "normal": 14, "fast": 3 }
}
}

Fields

FieldTypeNotes
sentimentDistributionmapOverall sentiment.overall value → call count, within the window.
userSentimentDistributionmapSame, but for sentiment.userSentiment.overall (the phone owner's read).
contactSentimentDistributionmapSame, for sentiment.contactSentiment.overall (the other party's read).
sentimentTrendarrayOne point per ISO week in the window; counts is the same shape as sentimentDistribution.
toneProfileobjectDistribution of tone.formality / tone.energy / tone.pace across calls in the window.

Calls with no stored sentiment/tone (e.g. still processing, or pre-v2 analysis with only {overall, reason}) are excluded from the relevant distribution rather than counted as a bucket.

Errors

StatusCause
401Missing/invalid auth.

Storage

Populated from the call_sentiment_summary table, which is written alongside action_items when analysis side effects are applied — one row per completed call, denormalising just the fields this endpoint needs so the query doesn't have to deserialise every ANALYSIS_JSON artifact on every request.