Analytics & usage
Analytics & usage API
Read-only reporting for your organization: call analytics and metered usage/quota. Both 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 and the billing money endpoints.
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.
| Query param | Purpose |
|---|---|
from, to | Date range (YYYY-MM-DD, both optional). Defaults to the last 30 days. 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). |
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.
curl "https://api.telenow.ai/api/orgs/{orgId}/analytics?from=2026-06-01&to=2026-06-30" \
-H "x-api-key: vai_live_…"
The data object is camelCase and includes:
| Field | Meaning |
|---|---|
from, to | The resolved window (half-open: from inclusive, to exclusive). |
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). |
totalSeconds, avgSeconds | Total talk time and 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: { day, calls, completed, missed, seconds }. |
agents[] | Per-agent rollup: { agentId, agentName, calls, completed, seconds }. |
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.
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.
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.