Analytics & usage API
Reporting for your organization: metered usage and quotas, billing, call analytics, and the live wallboard. Usage and call analytics are organization-scoped (API key or JWT + X-Org-Id).
Usage & quotas
GET /api/orgs/{orgId}/usage
Returns the organization's metered usage for the current billing period — the calendar month in IST (the platform's billing timezone), returned as a UTC instant — plus the configured limits, in one call. Any org member may read it.
{
"success": true,
"data": {
"period_start": "2026-06-01T00:00:00Z",
"calls": 1280,
"minutes": 4310,
"active_calls": 3,
"limits": {
"monthly_call_limit": null,
"monthly_minute_limit": 10000,
"max_concurrent_calls": 5,
"enforce": true
}
}
}
| Field | Meaning |
|---|---|
period_start | Start of the current period (the IST calendar month, returned as a UTC instant). |
calls | Billable calls so far this period. |
minutes | Connected talk minutes so far this period. |
active_calls | Live concurrent calls right now (age-bounded so a crashed session can't wedge the gate). |
limits | The configured quotas (see below). |
The snapshot fields are snake_case. A null limit means unlimited for that dimension. When there's no limits row at all, every limit reads null with enforce: true (so a later-set limit takes effect immediately).
Updating limits
PUT /api/orgs/{orgId}/usage/limits
Owners/admins only. This is a replace (PUT) — the body is the complete desired limit state, so send every field; omit a field (or send null) to make that dimension unlimited.
curl -X PUT "https://api.telenow.ai/api/orgs/{orgId}/usage/limits" \
-H "x-api-key: vai_live_…" -H "Content-Type: application/json" \
-d '{
"monthlyCallLimit": null,
"monthlyMinuteLimit": 10000,
"maxConcurrentCalls": 10,
"enforce": true
}'
| Body field (camelCase) | Type | Meaning |
|---|---|---|
monthlyCallLimit | int / null | Calls allowed per period. null = unlimited. |
monthlyMinuteLimit | int / null | Minutes allowed per period. null = unlimited. |
maxConcurrentCalls | int / null | Simultaneous live calls. null = unlimited. |
enforce | bool | true blocks calls at the limit; false meters only (observe-only). Defaults to true. |
Notes:
- The request accepts camelCase field names (snake_case aliases also work). A value of 0 hard-pauses that dimension; negative values are rejected with
400. - The response echoes the saved limits in snake_case (matching the
limitsblock in the usage snapshot).
Enforcement: 429 vs 403
Limits are enforced at dial time, before any carrier is contacted. When a call is refused:
| Limit hit | Status | Why |
|---|---|---|
Concurrency — per number, per workspace (max_concurrent_calls), or browser | 429 Too Many Requests | Transient — a slot frees the moment a live call ends. The body names which ceiling in scope and how long to wait in retryAfter. |
| Monthly call / minute quota | 403 Forbidden | Not transient — raise the limit or wait for the next billing cycle. |
| Spend cap reached or account suspended | 403 Forbidden | A billing gate that runs ahead of both the concurrency and quota checks. |
A 429 body:
{ "success": false, "error": "…", "retryAfter": 240,
"reason": "number_at_capacity", "scope": "number",
"cap": 2, "active": 2, "position": 3, "medianCallSecs": 120 }
scope is number, org or browser — it tells you which ceiling refused you, and therefore whether it is yours to raise (org, under Usage → Edit limits) or your platform admin's. retryAfter is also sent as the standard Retry-After header and differs per caller so a backlog spreads out; see Concurrency limits.
The concurrency caps also bound anonymous widget sessions, protecting your spend from public traffic.
See Usage & billing for the dashboard view.
Billing
The money endpoints behind the dashboard's Billing page, for building your own dashboards, alerts, or billing automations. Billing routes are owner/admin only (JWT).
| Method | Path | Purpose |
|---|---|---|
GET | /api/orgs/{orgId}/billing/summary | Period payable, breakdown, per-day, account balance |
GET | /api/orgs/{orgId}/billing/breakdown | Charges by agent × call type |
GET | /api/orgs/{orgId}/billing/invoices | Invoice history with line items |
GET | /api/orgs/{orgId}/billing/ledger | Ledger entries, newest first — paged with ?limit= (max 200, default 50) and ?offset=; returns { "entries": [...], "total": N, "limit": N, "offset": N } |
POST | /api/orgs/{orgId}/billing/invoices/{invoiceId}/pay | Create a Razorpay order for an unpaid invoice |
POST | /api/orgs/{orgId}/billing/topup | Buy credits (prepaid) — body { "amountInr": 1000 }, range ₹50–₹10,00,000 |
The usage snapshot and limits are under Usage & quotas above. See Usage & billing for prepaid vs. postpaid, the charge components, and invoices.
Call analytics
GET /api/orgs/{orgId}/analytics
Aggregated, org-wide call analytics over a date window: call volume per day, an outcome breakdown, total/average duration, average agent response latency, and a per-agent rollup. Any org member may read. The same endpoint powers the agent Analysis tab — the tab just passes agentId.
| Query param | Purpose |
|---|---|
from, to | Date range (YYYY-MM-DD, both optional). Defaults to the last 30 days; to defaults to today (UTC). Both dates are inclusive — calls on the to date are counted. (Internally the to date is advanced to the start of the next day and the query is start_time >= from AND start_time < to+1day, i.e. a half-open timestamp window — which is why the resolved window below shows to as an exclusive timestamp.) |
agentId | Limit to one agent (optional, UUID). Omit for all agents. |
The reportable span is capped at 366 days (a hand-crafted request for a larger range returns 400). from must be on or before to. A malformed date (not YYYY-MM-DD) or a from after to also returns 400.
curl "https://api.telenow.ai/api/orgs/{orgId}/analytics?from=2026-06-01&to=2026-06-30" \
-H "x-api-key: vai_live_…"
A response for from=2026-05-01&to=2026-05-31, in the standard { success, data } envelope:
{
"success": true,
"data": {
"from": "2026-05-01T00:00:00Z",
"to": "2026-06-01T00:00:00Z",
"totalCalls": 1280,
"completed": 1041,
"missed": 198,
"inProgress": 41,
"machineAnswered": 73,
"totalSeconds": 254880,
"avgSeconds": 244.8,
"avgResponseMs": 920,
"daily": [
{ "day": "2026-05-01", "calls": 44, "completed": 38, "missed": 5, "seconds": 9120 }
],
"agents": [
{ "agentId": "…", "agentName": "Support", "calls": 720, "completed": 612, "seconds": 150480 }
]
}
}
The data object is camelCase and includes:
| Field | Meaning |
|---|---|
from, to | The resolved window as ISO timestamps (half-open: from inclusive, to exclusive — to is the start of the day after your end date). |
totalCalls, completed, missed, inProgress | Call counts. Outcomes are derived from session status + duration (completed = ended with duration > 0; missed = ended with zero duration; in-progress = still active). |
machineAnswered | Calls the carrier's answering-machine detection flagged (0 when AMD was never enabled). A subset of totalCalls, not a fourth category. |
totalSeconds | Total talk time of every call in the window. |
avgSeconds | Mean duration of completed calls only. |
avgResponseMs | Mean agent response latency (ms) across measured calls (0 when none in range was measured). |
daily[] | Per-day series, one point per calendar day: { day, calls, completed, missed, seconds }. |
agents[] | Per-agent rollup: { agentId, agentName, calls, completed, seconds }. agentName is null for a deleted agent. |
For the dashboard view, the KPIs, charts, and how each outcome is derived, see Analytics dashboard. Per-call AI insights are documented under Post-call analysis.
Post-call analysis
One call's post-call analysis, and what analysis has cost your organization:
| Method | Path | Returns |
|---|---|---|
GET | /api/orgs/{orgId}/analysis/result/{sessionId} | The call's analysis object, or data: null when the call has none |
GET | /api/orgs/{orgId}/analysis/cost?from&to | { from, to, costUsd }: the platform-AI cost billed to the organization in the window |
- Signed-in users only. Both need a user JWT from a member of the organization; an API key gets
401. A server-to-server integration gets each analysis from thecall.analyzedwebhook instead. fromandtoare ISO timestamps.todefaults to now, andfromto 30 days beforeto.dataisnulluntil the analysis has run, for a call whose agent has analysis off, and for a call in another organization.- Every field of the analysis object, its empty values, and how the webhook's shape differs: API responses → The post-call analysis object.
Agent analysis
The routes behind the agent Analysis tab's post-call-analysis insights and its Caller memory list. These are dashboard routes — { success, data } envelope. Authenticate with an API key, or a user JWT plus the X-Org-Id header (the same one /agents/:id/stats uses):
| Method | Path | Returns |
|---|---|---|
GET | /api/agents/{id}/analysis/rollup?from&to | KPIs + sentiment trend, disposition breakdown, top objections, top topics/keywords, talk-ratio word totals |
GET | /api/agents/{id}/analysis/calls?from&to&limit | Recent analyzed calls, newest first (limit 1–200, default 50) |
GET | /api/agents/{id}/analysis/memory | Org-shared caller memory rows |
DELETE | /api/agents/{id}/analysis/memory?caller={callerKey} | Erase one caller's memory → { removed } |
from and to are ISO timestamps (not date strings). to defaults to now, and from to 30 days before to. Only finished analyses count.
The rollup response is { from, to, rollup }, where rollup carries:
| Field | Meaning |
|---|---|
total | Analyzed calls in the window |
avgScore | Mean judge score, or null when no call has one |
hallucinationCalls | Calls with at least one hallucination flag |
positive, neutral, negative | Calls per sentiment |
agentWords, customerWords | Talk-ratio word totals |
sentimentTrend[] | { day, positive, neutral, negative } per day |
dispositionBreakdown[] | { disposition, count } |
topObjections[] | { objection, count } |
topTopics[], topKeywords[] | { tag, count } |
A calls row is { sessionId, createdAt, sentiment, disposition, score, summary, hallucinationCount }. Both read empty until post-call analysis has run on at least one call in the window. For the per-field meaning of the underlying analysis, see Post-call analysis.
Wallboard
GET /api/orgs/{orgId}/wallboard?since={ISO8601}
The live-ops snapshot behind the dashboard's Wallboard: team presence, calls in progress, and today's totals. Readable by any org member.
since is the client's local midnight (the Wallboard page sends it automatically); omit it and the backend falls back to UTC midnight, which may not match your timezone.
{
"success": true,
"data": {
"now": "2026-06-13T10:22:05Z",
"members": [
{
"userId": "…", "name": "Sarah Chen", "email": "[email protected]",
"role": "admin", "receiveCalls": true,
"live": true, "lastSeenMs": 1749810120000, "onCallId": null
}
],
"activeCalls": [
{
"id": "…", "agentName": "Support", "callMode": "ai",
"from": "+14155550100", "to": "+14155550111",
"startedAt": "2026-06-13T10:19:40Z", "initiatedByEmail": null
}
],
"today": {
"total": 88, "completed": 71, "missed": 12,
"inProgress": 5, "totalSeconds": 21340, "avgSeconds": 300.6
}
}
}
| Field | Meaning |
|---|---|
members[].live | true when the member is present within the freshness window. |
members[].lastSeenMs | Last-seen epoch ms (drives the "Seen N ago" label), or null. |
members[].onCallId | Session id of the manual call this member is on, or null. |
activeCalls[].callMode | manual for a softphone call; anything else is an AI agent call. |
today | The same of-today totals shown in the Wallboard's KPI band. |
Dial aggregates
/api/orgs/{orgId}/dial
Aggregates that span calls, backing the Dial page. User JWT + X-Org-Id.
| Method | Path | Purpose |
|---|---|---|
GET | /stats | Outcome breakdown — total, answered, machine, not-answered, rejected |
GET | /followups | Follow-ups across all of this caller's calls, each with light call context |
GET | /number-summary | Per-number rollup |
GET | /number-calls | Calls for one number |
GET | /missed-calls | Missed inbound calls |
These default to the calling user's own activity, not the organization's. The page they back is "my dialling", so a report built on them will silently under-count unless you account for that. Both /stats and /followups take an optional YYYY-MM-DD date window applied to the call start time. For org-wide figures use the analytics endpoints above.
Related
- Analytics dashboard — the in-app reporting page.
- Usage & billing — quotas, credits, invoices, and the charge breakdown.
- Catalog & providers API — provider pricing behind cost estimates.
- Call history and Post-call analysis.