Analytics & Usage Reports
Track AI, SMS, phone, voice, and member-level usage across your workspace for billing, budgeting, and cost analysis.
Analytics & Usage Reports
Usage reports let organization owners and admins see where their workspace spends time, money, and messages. You can pull AI token consumption, SMS volume, phone minutes, voice (speech-to-text and text-to-speech) usage, and per-member totals, scoped to any date range you pick.
Reports are available in two ways: through the live Detailed Reports tab in Settings → Billing (/w/{wid}/settings/billing), and via the REST API for scripted exports, BI tools, or cost-allocation pipelines.
Who Can Access Reports
Usage reports return financial and operational data, so access is gated by scope:
| Report | Required scope |
|---|---|
AI usage (/reports/ai-usage) | analytics:read |
SMS usage (/reports/sms-usage) | analytics:read |
Phone usage (/reports/phone-usage) | analytics:read |
Voice usage (/reports/voice-usage) | analytics:read |
| Member usage — your own | members:read |
| Member usage — anyone else's | analytics:read |
The analytics:read scope is what the product calls "View analytics and reports." It's included in the default workspace manager and admin roles, and it's grantable to any custom role. See Roles & Permissions for how to assign scopes to a role.
Members can always see their own member-usage report. Only members with analytics:read can look at someone else's.
Getting an API Key
Reports are served by https://api.gravityrail.com. To call the endpoints you need a scoped API key:
- Go to Apps → API Keys
- Click Create API Key
- Give it a clear name (e.g., "Monthly Billing Export")
- Pick an expiration (1–365 days)
- Grant the
analytics:readscope — andmembers:readif you want to pull other members' usage - Click Create and copy the key immediately (it won't be shown again)
Every request below uses the header Authorization: Bearer <YOUR_API_KEY>. See Apps & API Keys for more detail.
You'll also need your workspace UUID. It's the long identifier in your workspace URL (e.g., app.gravityrail.com/w/<workspace-uuid>/chats).
The AI Usage Report
The AI usage report summarizes everything your assistants generated: every chat message, every chat summary, every realtime voice turn. Use it to see which models are driving spend and which providers you lean on most.
Request
bash
| Query parameter | Default | Notes |
|---|---|---|
start_date | 30 days ago | YYYY-MM-DD |
end_date | today | YYYY-MM-DD |
What the response contains
| Field | What it means |
|---|---|
total_messages | Every message recorded in the workspace for the period — human and assistant, across all channels |
total_tokens | Sum of prompt + completion tokens used by your assistants |
total_cached_tokens | Portion of input tokens served from the provider's prompt cache. Higher is cheaper |
total_audio_input_tokens / total_audio_output_tokens | Realtime voice audio tokens, tracked separately from text |
total_credits | The Gravity Rail unit of AI spend for the period (see Understanding Credits and Costs) |
by_provider | Totals broken out by provider (OpenAI, Anthropic, Google, AWS) |
by_type | Totals split between chat and realtime models |
top_models | The 10 models with the highest credit spend |
What to look for
- Providers drifting in the wrong direction. If one provider is creeping toward 80% of spend and you expected it to be 30%, a workflow is probably routing to the wrong model.
- A model in
top_modelsthat surprises you. A development-grade model appearing in the top ten usually means a test workflow is running against real traffic. - Low
total_cached_tokensvs.total_tokens. Prompt caching cuts cost substantially. A low ratio often means system prompts are being regenerated on every turn — worth investigating.
The SMS Usage Report
Every text your workspace sends or receives is tracked. The SMS report gives you a day-by-day, phone-number-by-phone-number picture of what went out and came in.
Request
bash
| Query parameter | Default | Notes |
|---|---|---|
start_date | Start of current UTC month | YYYY-MM-DD |
end_date | End of current UTC month | YYYY-MM-DD |
What the response contains
| Field | What it means |
|---|---|
totalMessages | All SMS in the period |
totalInbound / totalOutbound | Direction split |
totalCharacters | Sum of characters sent + received |
totalSegments | Total carrier-billable segments (160 GSM chars / 70 Unicode chars per segment) |
totalInboundSegments / totalOutboundSegments | Segments by direction — useful when only outbound is metered |
rows[] | One row per date + phone number combination, each with its own inbound/outbound counts |
What to look for
- Segments, not characters. Carriers bill per segment, and a single long or emoji-laden message can be several segments. Use
totalSegmentsfor cost estimates. - Spikes on a single phone number. Drill into
rowsto see if one number is carrying the whole workspace — that's often a routing issue or a runaway campaign. - Inbound volume that dwarfs outbound. Healthy automated workflows usually send more than they receive. The reverse can point to members stuck in an unresolved loop.
The Phone Usage Report
The phone report covers voice calls in and out of your workspace numbers — the ones humans or AI answered. It only counts calls that reached a finished state with real start and end timestamps; missed and in-progress calls aren't included.
Request
bash
| Query parameter | Default | Notes |
|---|---|---|
start_date | Start of current UTC month | YYYY-MM-DD |
end_date | End of current UTC month | YYYY-MM-DD |
What the response contains
| Field | What it means |
|---|---|
totalCalls | All finished calls in the period |
totalInbound / totalOutbound | Direction split |
totalDurationSeconds | Combined talk time — divide by 60 for minutes |
rows[] | One row per date + phone number combination with inbound/outbound counts and duration |
What to look for
- Average call duration by number. Divide the row's
totalDurationSecondsby(inbound + outbound)per day. A big jump usually means a workflow isn't hanging up when it should. - Outbound volume on a number meant for inbound. It's easy for a campaign to target the wrong line. The phone report surfaces that quickly.
- Days with unusual volume. The daily breakdown makes it obvious when a single day drove a monthly spike.
The Voice Usage Report
Voice calls use two AI services under the hood: speech-to-text (STT) to transcribe what the caller says, and text-to-speech (TTS) to generate the assistant's reply. Voice usage is billed via credits at the org level, so this report shows usage volume only — durations, characters, and message counts.
Request
bash
| Query parameter | Default | Notes |
|---|---|---|
start_date | Start of current UTC month | YYYY-MM-DD |
end_date | End of current UTC month | YYYY-MM-DD |
What the response contains
| Field | What it means |
|---|---|
totalSttDurationSeconds | Total seconds of audio transcribed across all STT providers |
totalSttMessages | Number of STT calls (one per transcribed message) |
totalTtsCharacters | Total characters converted to speech |
totalTtsMessages | Number of TTS calls |
sttByProvider[] / ttsByProvider[] | Totals broken out by provider and voice model |
sttRows[] / ttsRows[] | Daily breakdowns per provider + model |
What to look for
- Which voice provider is dominating. If you set up a premium voice for one agent and a cheaper one for everything else, the provider breakdown tells you whether the split is working.
- TTS character growth. Long assistant replies burn through TTS faster than brief ones — this is often the single biggest voice-cost lever.
- Models you don't recognize. A fallback model showing up unexpectedly usually means the primary model failed to load somewhere.
Member-Level Usage
The member usage report covers a single member's activity: chats they own, messages they sent, and AI usage incurred on their behalf. Org admins use it to allocate cost, and members can use it to check their own consumption.
Request
bash
| Query parameter | Default | Notes |
|---|---|---|
start_date | Start of current UTC month | YYYY-MM-DD |
end_date | today (UTC) | YYYY-MM-DD |
What the response contains
| Field | What it means |
|---|---|
memberId | The member you queried |
totalChats | Chats created by this member in the period |
totalMessages | Messages sent by this member (any role) |
totalCredits | Total tokens consumed across the member's chats and their chat summaries |
Find member IDs from the People page or by listing members via GET /api/v2/w/{workspace_uuid}/members.
What to look for
- Cost per seat, not just total cost. Comparing
totalCreditsacross members normalizes spend by user, which is what billing and capacity conversations usually need. - Members with high message counts but low credits. That's a sign they're having short conversations — usually good. The reverse (few messages, many credits) often points to one long, expensive chat.
- Discrepancies against the workspace-level AI report. Member usage attributes only what's anchored to a member's chats. System or agent-only work (e.g., scheduled routines) appears in the workspace total but not in any one member's report. That's by design.
Understanding Credits and Costs
Gravity Rail measures AI spend in two related ways:
- Tokens are the raw unit your AI provider counts — roughly one token per four characters of English text.
- Credits are Gravity Rail's normalized unit of AI spend. Each model has a multiplier that converts its tokens into credits, so a premium model and a lightweight model consume different numbers of credits per token. Credits are what you'll see on your bill and what org-level quotas are enforced against.
Both are pre-calculated at the moment usage is recorded. That means:
- Historical reports are locked to the prices that applied at the time. If a provider lowers their prices next month, old reports won't retroactively get cheaper.
- You don't need to re-derive credits from tokens yourself. The number in the report is the authoritative one; credit multipliers can change over time and per-model, so recomputing downstream will drift.
For SMS and phone, the report gives you the raw usage primitives — messages, characters, segments, duration. Your carrier pricing converts those to dollars. Voice STT/TTS is billed via org-level credits, and the credit totals appear on your org billing, not in the voice-usage payload.
Date Ranges and the UTC Caveat
Every report accepts optional start_date and end_date parameters as YYYY-MM-DD. Both are inclusive — 2026-04-01 to 2026-04-30 covers the whole of April.
All daily breakdowns are bucketed in UTC. That is important:
- A call that starts at 10:00 PM Pacific on April 30 ends up on May 1 in the report, because that's 5:00 AM UTC May 1.
- If you're in the eastern U.S., daily totals roll over at 8:00 PM local (standard time) or 7:00 PM (daylight time) — not midnight.
- Month totals align to UTC month boundaries. If your billing cycle ends on the last calendar day in your local time zone, ask for an extra day on either side and filter the rows yourself.
Defaults follow the same rule: the SMS, phone, and voice reports default to the start-of-month and end-of-month in UTC, and member usage defaults to "start of the current UTC month." If month alignment matters, pass the dates explicitly.
Tips
- Export once a month, reconcile once a quarter. Pulling the AI, SMS, phone, and voice reports on the first of each month and parking them in a spreadsheet is the simplest path to trend analysis.
- Rotate API keys that you use for exports. A 90-day expiration on the key you feed into a BI tool is a healthy default — see Apps & API Keys.
- Scope sub-minute conversations carefully. Phone reports round to the second, but very short calls (a dial and immediate hangup) are included and can inflate call counts. Filter the rows if you want only "real" conversations.
- Use the smallest date range that answers your question. A 90-day AI report aggregates millions of rows in large workspaces. Narrow the range when you're iterating.
- Watch the realtime/chat split. In
by_type, realtime models are usually much more expensive per minute than chat models. If realtime credits climb without a corresponding increase in phone calls, that's worth a look.
Troubleshooting
"403 Forbidden" when requesting a usage report
Your API key doesn't include analytics:read. Create a new key with that scope, or update the role on the member whose API key you're using. Member-level usage for your own member ID only requires members:read; any other member requires analytics:read.
Numbers don't match my provider invoice
A few common causes:
- Time zone. The report is UTC; your invoice is almost certainly in a local time zone.
- Pending calls or messages. In-progress or not-yet-finished calls don't appear in the phone report.
- Cached tokens. AI providers typically discount cached prompt tokens.
total_cached_tokensshows how much of your input was cached — this is usually the source of the discrepancy between raw token counts and billed amounts.
A member's usage looks lower than expected
Member usage attributes activity to the member who owns the chat. Agent-only work, scheduled routines, and system-initiated chats aren't attributed to any member and won't appear in their report. Cross-check against the workspace-level AI usage report for the full picture.
The report is slow
Large date ranges across large workspaces aggregate millions of rows. Narrow the date range, or split a yearly pull into monthly requests and combine the results client-side.
Related
- Apps & API Keys — Create and manage the API keys that authenticate report requests
- Roles & Permissions — Grant
analytics:readto custom roles - Phone & Voice — Configure the phone numbers whose usage these reports cover
- Organizations — Manage org-level credits and billing
- Workspace Settings — Configure time zone and workspace defaults