Analytics and reports
Jelliu analyzes every call and text conversation your agents handle: outcome, sentiment, frustration, response latency, talk share, cost, and the fields each agent is configured to extract. The analytics API exposes those numbers as aggregates you can pull into your own BI, CRM or reporting stack.
There are four surfaces, each for a different job:
How it works
Analytics and reports are computed from the rows Jelliu stores for each call and conversation. Two consequences follow from that:
- Numbers appear after analysis. A call counts in volume as soon as it exists, but its outcome, sentiment and extracted fields only appear once post-call analysis has written them.
- Averages are over the rows that carry the measurement. A call with no sentiment score does not pull the average toward zero; it is simply not in the sample. Most responses include the sample size next to the average (
sentimentCallCount,sampleSize,sentimentSampleSize) so you can tell a fleet-wide signal from a three-call one.
null means “no data”, never zero. Sentiment runs from -1 to 1, where 0 is a real, neutral reading. When nothing in the window was scored, analytics endpoints return avgSentiment: null, not 0. Treat null as “not measured” in your charts. (The generated reports under /api/reports are the exception: they report 0 when there is no data.)
Authentication and access
All analytics routes are reads, so a read key is enough for every GET on this page. The exceptions:
See Authentication for how scopes map to roles.
Common query parameters
Most analytics endpoints share the same window and filters:
Size of the window, counted back from now, from 1 to 365. An out-of-range or non-numeric value falls back to 30 rather than failing.
inbound, outbound or all. Applies to call metrics.
Scope every call metric to one campaign.
Scope every call metric to one agent. The agent is the one that actually handled the call, so manual calls and calls from reassigned campaigns are attributed correctly.
IANA timezone such as America/Bogota, used to bucket days and hours. An unknown timezone falls back to UTC.
A malformed campaignId or agentId (not a UUID) returns 400 VALIDATION_FAILED with the message Invalid query parameters. Filters are never silently dropped.
Dashboard bundle
GET /api/dashboard returns what the dashboard’s home page needs in one request.
Timezone used to decide what “today” means for callsToday.
Read per-agent performance from agentRanking, not from the metric fields on the agents list: those list fields are placeholders and are not computed here.
Call analytics
Workspace overview
GET /api/analytics/overview accepts days, direction, campaignId and agentId.
- Rates such as
successRateare percentages from 0 to 100, rounded to two decimals. - A “success” depends on the campaign category (a sale for
sales, a booking forscheduling, and so on).successRateLabelnames it. avgDurationSecondsisnullwhen no voice channel is involved, andexcludedMetricslists metrics that do not apply to a channel or category.
Overview by direction
GET /api/analytics/overview/by-direction?days=90 splits call metrics into inbound, outbound and total. Each has totalCalls, avgDuration (null without voice calls), successRate, avgSentiment and sentimentCallCount.
Time series
All three accept days, direction, timezone, campaignId and agentId. Days with no calls are omitted, not returned as zero rows; fill the gaps on your side if your chart needs a continuous axis.
Conversation quality
GET /api/analytics/quality describes how calls went rather than how they ended. It accepts days, direction, campaignId and agentId. Every block carries its own sampleSize, because older calls do not have every measurement.
Cost figures in analytics describe what the calls consumed on the voice platform. They are operational telemetry, not your Jelliu invoice. For plan usage, see Billing and usage.
Agent ranking
GET /api/analytics/agent-ranking returns up to 50 agents, ordered by successes and then by call volume. It accepts days, direction, campaignId and agentId.
Campaign analytics
A campaign analytics response may include _degraded, a list of sections that could not be computed (aggregates, outcomeBreakdown). The rest of the response is still valid. An unknown campaign returns 404 CAMPAIGN_NOT_FOUND.
Evaluation criteria and extracted data
Agents can be configured with success criteria and data-collection fields. These two endpoints roll up the results across both voice calls and text conversations:
Both accept days (1 to 365, default 30) and agentId. Here an invalid agentId returns 400 with the message Invalid agentId: expected a UUID. A verdict that is not success or failure is counted as unknown, so totals always add up.
Text conversation analytics
Call analytics read calls only. These endpoints cover WhatsApp, web chat, email, Instagram and Messenger threads:
channelis one ofwhatsapp,webchat,email,instagram,messenger.fieldmust be a simple identifier (a letter, then letters, digits or underscores, up to 64 characters).pendingthreads are still being analyzed and clear by themselves.notAnalysablethreads will never be analyzed, so they are a permanent gap in the denominator.- In the conversation criteria endpoint,
successRateis a ratio from 0 to 1 over decided verdicts only (unknownis excluded), ornullif nothing was decided. This differs from the 0 to 100 percentages used by call analytics. - In the field breakdown, a thread where the agent extracted the field but found no value is reported with the literal value
(sin dato).
Invalid parameters here return 400 VALIDATION_FAILED with the message Request validation failed and an issue list in details.
Page bundle
GET /api/analytics/page-load computes the whole analytics page in one request. It accepts days, limit, direction, timezone, campaignId and agentId, and returns:
Each section is computed independently. If one fails, it comes back as null (objects) or [] (lists) and the rest of the bundle is unaffected, so check for null before reading nested fields. The campaign filter is not applied to the conversations sections, because text threads do not belong to campaigns.
Campaign KPIs
Each campaign category has business KPIs derived from the data the agent collects (for example revenue_total and avg_ticket for sales, appointments_booked for scheduling, nps_average for surveys).
unit is one of currency, count, percentage, score or seconds. category is one of sales, support, scheduling, surveys, collections, retention, notifications, interview, language_assessment, personal, general.
Custom events
You can record your own product events against the workspace and query them back.
Each event row has id, tenant_id, user_id, event_name, properties, source, timestamp and created_at.
Reports
Reports compute a finished analysis over an explicit date range and return it inline.
GET /api/reports/types lists the available types:
Generate a report
POST /api/reports
One of the types above.
daily, weekly, monthly, quarterly or custom. A label for the report; the range is set by date_from and date_to.
ISO 8601 date-time with offset (2026-08-01T00:00:00-05:00) or a date (2026-08-01).
Same format. The range cannot exceed one year.
Restrict roi_analysis and campaign_comparison to these campaigns. contact_funnel uses only the first ID. cost_per_call and agent_performance ignore it.
Accepts json or pdf. Reports are always returned as JSON.
Report shapes
Costs and ROI in reports are estimates: they multiply minutes by a platform-wide cost per minute and compare against a reference hourly rate for human agents. Use them for trends and comparisons, not as billing figures.
A plain date such as "2026-08-31" means the start of that day, so a range ending on "2026-08-31" excludes August 31. To include the whole last day, send a date-time such as "2026-08-31T23:59:59Z" or the next day’s date.
Exports
Exports produce a file of raw rows asynchronously. You create a job, poll it until it completes, then download the file.
Export types and filters
A filter the type cannot apply is rejected, never ignored:
Create an export
POST /api/exports
calls, contacts, conversations, analytics, agent_actions or audit_log.
csv, json (one array) or jsonl (one JSON object per line; best for large files and SIEM ingestion).
date_from and date_to (ISO 8601 date-times), campaign_id, agent_id (UUIDs), status, outcome. Only the filters supported by the type.
Poll until it finishes
GET /api/exports/{id} returns the same object. Poll every few seconds until status is completed or failed. When completed, row_count, file_size_bytes, completed_at and expires_at are set. When failed, error explains why. file_url is internal bookkeeping: always download through the endpoint below.
GET /api/exports?limit=50&offset=0 lists jobs, newest first (limit 1 to 100).
Export limits
audit_log and agent_actions exports contain your compliance record. Creating, reading and downloading them requires a full key, checked against the job’s type on every request, so a lower-scoped key cannot download a file an administrator created.
A job that is not completed yet returns 404 with Export file is not ready for download. An expired file returns 410 with Export file has expired, or 404 with Export file has expired or is no longer available. Create a new export in either case.
Errors
See Errors for the envelope.
Limits and caching
- Rate limits. Analytics endpoints are limited to 30 requests per minute per workspace, on top of the general limit. Use
/api/analytics/page-loador/api/dashboardinstead of many individual calls. See Rate limits. - Freshness. Aggregates are cached on the server for up to a few minutes and responses carry
Cache-Control: private, max-age=30, stale-while-revalidate=900. A call that just ended may take a short while to show up. Do not poll analytics faster than once a minute. - Conditional requests.
overview,agent-ranking,quality,top-campaignsand/api/dashboardreturn a weakETag. Send it back inIf-None-Matchto receive304 Not Modifiedwhen nothing changed. - Windows.
daysis capped at 365 for analytics and at 90 for custom events. Reports cover at most one year.
For real-time pipelines, do not poll analytics. Subscribe to the call.completed webhook, which carries each call’s outcome, sentiment, summary and extracted data as soon as analysis finishes, and aggregate on your side. See Webhooks.

