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
    }
  }
}
FieldMeaning
period_startStart of the current period (the IST calendar month, returned as a UTC instant).
callsBillable calls so far this period.
minutesConnected talk minutes so far this period.
active_callsLive concurrent calls right now (age-bounded so a crashed session can't wedge the gate).
limitsThe 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)TypeMeaning
monthlyCallLimitint / nullCalls allowed per period. null = unlimited.
monthlyMinuteLimitint / nullMinutes allowed per period. null = unlimited.
maxConcurrentCallsint / nullSimultaneous live calls. null = unlimited.
enforcebooltrue 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 limits block 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 hitStatusWhy
Concurrency — per number, per workspace (max_concurrent_calls), or browser429 Too Many RequestsTransient — 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 quota403 ForbiddenNot transient — raise the limit or wait for the next billing cycle.
Spend cap reached or account suspended403 ForbiddenA 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 paramPurpose
from, toDate 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.)
agentIdLimit 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:

FieldMeaning
from, toThe resolved window (half-open: from inclusive, to exclusive).
totalCalls, completed, missed, inProgressCall counts. Outcomes are derived from session status + duration (completed = ended with duration > 0; missed = ended with zero duration; in-progress = still active).
machineAnsweredCalls the carrier's answering-machine detection flagged (0 when AMD was never enabled).
totalSeconds, avgSecondsTotal talk time and mean duration of completed calls only.
avgResponseMsMean 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.

Dial aggregates

/api/orgs/{orgId}/dial

Aggregates that span calls, backing the Dial page. User JWT + X-Org-Id.

MethodPathPurpose
GET/statsOutcome breakdown — total, answered, machine, not-answered, rejected
GET/followupsFollow-ups across all of this caller's calls, each with light call context
GET/number-summaryPer-number rollup
GET/number-callsCalls for one number
GET/missed-callsMissed 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.