Tether
DocsGitHub

Operations

Cost accounting

ai_usage_record is the billing-grade record: one append-only row per model call, written off the request path, with the cost computed at write time.

The row

ColumnMeaning
organization_idThe tenant that spent it
location_idThe scope, when the turn had one
user_idThe acting user. 0 when unattributed
surfaceWhich path spent it — chat, agent, embeddings
modelThe model actually used
prompt_tokensInput tokens, as the provider reported
completion_tokensOutput tokens, including reasoning tokens
total_tokensThe sum of the two above
cached_input_tokensCache reads. On a warm turn, almost all of the input
cache_write_tokensCache writes. Billed above ordinary input on some models
reasoning_tokensTelemetry only. Already inside completion_tokens
estimated_cost_usdnumeric(12,6), computed at write time
request_idCorrelates with the telemetry row
created_atUTC

One turn can write several rows: each model call is its own row, and a turn that loops over tools calls the model more than once.

Queries worth having

Spend by tenant, this month.

SELECT organization_id,
       SUM(estimated_cost_usd) AS usd,
       SUM(total_tokens)       AS tokens,
       COUNT(*)                AS calls
FROM ai_usage_record
WHERE created_at >= date_trunc('month', now() AT TIME ZONE 'utc')
GROUP BY organization_id
ORDER BY usd DESC;

Daily trend, one tenant.

SELECT date_trunc('day', created_at) AS day,
       SUM(estimated_cost_usd)       AS usd
FROM ai_usage_record
WHERE organization_id = 42
  AND created_at >= now() - interval '30 days'
GROUP BY 1
ORDER BY 1;

Cache effectiveness. Cache reads are an order of magnitude cheaper than ordinary input, so this ratio is the single biggest lever on unit cost.

SELECT model,
       SUM(cached_input_tokens)::numeric / NULLIF(SUM(prompt_tokens), 0) AS cached_share,
       SUM(cache_write_tokens)::numeric  / NULLIF(SUM(prompt_tokens), 0) AS write_share
FROM ai_usage_record
WHERE created_at >= now() - interval '7 days'
GROUP BY model;

Reasoning share of output. If this is high, the output ceiling is doing more work than you think — see Plans and ceilings.

SELECT model,
       SUM(reasoning_tokens)::numeric / NULLIF(SUM(completion_tokens), 0) AS reasoning_share
FROM ai_usage_record
WHERE created_at >= now() - interval '7 days'
GROUP BY model;

Cost per turn, using the request id to collapse a turn's calls:

SELECT request_id, SUM(estimated_cost_usd) AS usd, COUNT(*) AS calls
FROM ai_usage_record
WHERE organization_id = 42 AND created_at >= now() - interval '1 day'
GROUP BY request_id
ORDER BY usd DESC
LIMIT 20;

That last one is the query to run when a customer asks why their bill moved. The expensive turns are rarely the ones anyone expected.

Two ways the numbers lean high

The practical consequence: a new model or a provider that stops reporting breakdowns makes month-to-date spend jump without anything actually costing more. Check cached_input_tokens = 0 AND cache_write_tokens = 0 before believing a spike.

SELECT model,
       COUNT(*) FILTER (WHERE cached_input_tokens = 0 AND cache_write_tokens = 0) AS no_breakdown,
       COUNT(*) AS calls
FROM ai_usage_record
WHERE created_at >= now() - interval '1 day'
GROUP BY model;

Reconciling with the provider invoice

Expect Tether's total to be at or above the invoice. Sources of drift, in order of size:

  1. Calls with no reported token breakdown, priced at the worst rate.
  2. A model matched by prefix to a family whose rates have since changed.
  3. Rounding — each row is rounded to six decimal places.
  4. Provider-side discounts, batch rates and regional pricing, none of which the table models.

If Tether's total is below the invoice, something is wrong. That is the direction the pricing table is built to make impossible, so treat it as a missing price entry rather than a rounding artefact.

Ledger versus telemetry

Both record cost, at different grains and for different purposes:

ai_usage_recordai_request_telemetry
GrainOne model callOne request
WrittenDurably, with retriesBest-effort, loss-tolerant
Read byThe budget breakerHumans and dashboards
Use forBilling, caps, reconciliationDebugging, latency, routing

Bill from the ledger. Investigate with telemetry.

Retention