Agent-usage ledger
The ledger is the record you use to charge your own customers for agent use, or to talk to them about it. For every Site, customer account, user and UTC day it counts the completed-work units your backend reports (Usage metering) in four classes, and keeps the evidence behind every unit. Telltale reports units; it never charges anyone.
| Class (dashboard) | API and CSV name | What it means | Chargeable? |
|---|---|---|---|
| Chargeable agent | billable (billableUnits) | An AI agent did the work, with high confidence: verified identity (a verified operator signature), an automation trace (WebDriver, an automation framework or a headless browser), injected input, sustained behaviour, a behaviour change (a handover to software input), two or more independent behaviour families agreeing with no physical-device input, or Claude's cursor driving the clicks. The same list as what counts as chargeable. | Yes, if you charge for it |
| Medium-confidence agent | behaviour (behaviourUnits) | An AI agent reading on one behaviour pattern alone (medium confidence). Shown as AI agent activity, held for review at protected actions by default, never charged. | No |
| Human | human (humanUnits) | The session showed varied trusted input from a person. | No |
| Unknown | unknown (unknownUnits) | Not enough evidence either way. Unknown is not human. | No |
The dashboard counts units: one report to POST /api/v1/usage carries units (1 or more), so the entries CSV can have fewer rows than the units it adds up to. The CSV downloads keep the API's names above; routeCalls and routeBillableCalls are the calls through your MCP server and API and the chargeable ones.
A unit is counted once, when it is reported. The ledger only counts units you report with POST /api/v1/usage; agent minutes and tool runs stay in agent usage.
Approved-route calls are counted too, as their own unit: routeCalls and routeBillableCalls on each day and account, and a route_call entry for each call. They are never added to the four classes, never sent to Stripe meters with the work units and never counted by account alerts, so a tool call that also reports completed work is not counted twice. A route call names an account only when your server named it (identify, an agent token, or the account your MCP server's or your API middleware's identify returned).
Route calls come from three channels, and each day and account also counts them per channel (routeChannels in the totals, for example {"browser": 12, "mcp": 340}): browser (reviewed tools in your pages), mcp (tool calls through your own MCP server, from Telltale for MCP servers) and api (calls to your API: verified agent tokens, and calls with agent evidence reported by Telltale for APIs). Only channels with calls are listed. An MCP call is chargeable when your server named its account and the call ran (a call your rule refused is counted, never chargeable); An API call from the middleware is a route call only when it carries agent evidence (Unknown calls are route counts, never ledger entries), and it is chargeable when your server or an agent token named its account, the evidence is high confidence (Verified by token, Verified or Published IP range; never Declared), the call ran and it was not sampled. Each entry's evidence.channel says which channel it came from. A person counts once across channels toward Telltale's own plans (Pricing).
Evidence for every unit
Each unit has a ledger entry with:
usageId: the unit (the same IDPOST /api/v1/usagereturned), andidempotencyKey(tt_<entry id>) for your billing system;evidence.sessionIds: the session the work came from (sessions with AI agent activity are kept for your plan's history period, see data location and retention);evidence.verdictandevidence.basis: the verdict and detector version when the work was reported;evidence.evidenceClass:verified-identity,automation-trace,behaviour-only,evidence-unavailable(the session was gone before the unit could be checked: never charged),humanorunknown;evidence.howWeKnow: how strongly the agent is named (Verified,Unconfirmed,Self-reportedorNot named) andevidence.controller, the agent's name when it is named. A name identifies a tool or operator, not an AI model;evidence.signals: the strong automation signals seen (for examplewebdriver).
When a customer disputes a charge, the entry is your answer: which session, what we saw, and how we know.
Corrections, adjustments and erasure
The ledger is append-only. Nothing is rewritten in place:
- When a unit's facts change after it was counted, or you move a unit to another class after a dispute (Charge › Ledger › an entry › Change class, or
POST /api/admin/ledger-correction), the ledger adds an adjustment entry that moves the units from the old class to the new one. It is counted on the day it is made, so a period you already charged for never changes. Your own correction always wins over later evidence. - Erasing a user or account (Settings › Data & privacy) removes their account ID, user ID and session IDs from every entry and day count. The counts stay, on an erased row of the same day, so totals do not change.
Retention
Day counts are kept 400 days. Entries, with their evidence, are kept as long as the Site keeps usage (Settings › Data & privacy, 90 days by default, up to 400).
Plans
The ledger and the evidence behind every unit are on every plan. On Telltale's hosted plans, the CSV downloads are part of Starter, Growth and Enterprise (CSV exports), and the Stripe meter-events downloads, sending to Stripe and account alerts are part of Growth and Enterprise. Self-hosted Telltale includes everything.
Read the ledger
curl -sS "https://app.trytelltale.dev/api/v1/usage/ledger?from=2026-10-01&to=2026-11-01&view=accounts" \
-H "Authorization: Bearer $TELLTALE_SERVER_KEY"view=days(default): one row per day, account and user withbillableUnits,behaviourUnits,humanUnits,unknownUnitsandadjustments.view=accounts: totals per account for the period, with the number of users and the first and last day.view=entries: the entries with their evidence, oldest first. AddusageIdfor one unit.fromandtoare Unix epoch milliseconds or ISO 8601 dates. The period is half-open, [from, to); without them it is the last 30 UTC days.accountIdanduserIdfilter.limit(1 to 1,000, default 200) andpage.nextCursorpage through the rows; a cursor belongs to its view, period and filters.totalsholds the four classes for the period (days and accounts views).coveragesays from when counts and entries are still kept.
For billing, read closed days (a to at a UTC midnight in the past) and use each entry's idempotencyKey so nothing is charged twice.
Charge › Ledger shows the same and downloads CSV (GET /api/admin/ledger.csv?site=SITE_ID&view=days|accounts|entries), with the route calls in their own routeCalls and routeBillableCalls columns (and routeCallId and channel on route_call entries).
Stripe meters
Map your customer accounts to your own Stripe customer IDs (Charge › Ledger › Stripe meters). Then either:
- Download the meter-events file (CSV or JSON) for a period and send it with your own tooling. Each chargeable entry is one meter event:
identifier(tt_<entry id>),event_name(your meter's event name,agent_unitsby default),timestamp,stripe_customer_idandvalue. Rows markedcredittake chargeable units away (a correction): issue those as credits in Stripe. - Let Telltale send them. Paste a restricted key with write access to meter events, and turn on sending. Telltale then sends new chargeable units every five minutes (or now, with Send to Stripe now). The
identifierdoubles as Stripe's idempotency key, so a retry never charges twice. A unit reversed within 23 hours of being sent is cancelled with a meter event adjustment; after that it is listed as a credit to issue by hand. Stripe only accepts events from the last 35 days. Sending stays off until you turn it on, and the key is stored encrypted and shown only as a hint.
Units of accounts without a mapping are not sent; they are sent once you map the account (within the 35 days).
Account alerts to sales and customer success
Turn on account alerts in Charge › Ledger › Account alerts to hear when an account starts using agents, or when its agent units in a calendar month reach a threshold you set (up to five). Choose what counts: chargeable agent units (default) or every agent unit, behaviour-only included.
- Slack: paste a Slack incoming webhook URL. It must be HTTPS and resolve to a public address, the same rules as webhook endpoints. Send test sends one message now. The URL is stored encrypted.
- Webhooks:
account.agent_usage_startedandaccount.agent_usage_thresholdgo to your webhook endpoints, signed like every event (Webhooks).
Recipe: HubSpot or Salesforce
There is no built-in CRM connection yet. Point a webhook endpoint (filtered to the two account events) at a small handler of your own, or at your automation tool, and update the company record:
// After verifyWebhook() (see Webhooks), for account.agent_usage_started and account.agent_usage_threshold:
const { type, data } = event;
await crm.updateCompany(lookupCompany(data.accountId), {
telltale_agent_use: type === 'account.agent_usage_started' ? 'started' : `over ${data.threshold} units in ${data.month}`,
telltale_agent_name: data.controller ? `${data.controller} (${data.howWeKnow.label})` : 'Not named',
});Show the agent name with how we know it, never on its own. See the Agent seat playbook for what to say to the customer.