Metering & pricing
The meter
Section titled “The meter”Every call — success, error, cache hit, even a router-classifier call — writes one row to a SQLite ledger, priced at write time and attributed to all five principal dimensions the caller declared: tenant, project, user, agent, session.
Cache hits are recorded with zero usage and the tokens the provider would
have charged recorded as saved (tokens_saved, usd_saved) — so the
cache’s value shows up in the same ledger as the spend it avoided.
Read it back:
GET /v1/calls— recent rowsGET /v1/cost/by_principal— spend grouped by any dimensionGET /v1/cost/by_agent— the per-agent view- the dashboard at
/renders both
pricing.yaml
Section titled “pricing.yaml”Prices are keyed by model, not by provider, with resolution in order:
- exact model name (
gpt-5.6-terra) - vendor-prefix suffix match (
openai/gpt-4.1-mini≡gpt-4.1-mini) - glob patterns (
gemini-3.1-*) - a per-provider fallback rate
unpriced_default— $0/$0, and the row says so viaprice_source
Each entry can carry cache_read_multiplier / cache_write_multiplier /
batch_multiplier (vendors disagree on cache billing mechanics — that
difference is data here, not code) and a quality weight used by
cost/quality routing.
models: gemini-2.5-flash: provider: gemini input: 0.30 # $/Mtok output: 2.50 cache_read_multiplier: 0.25 quality: 0.55Guarding against pricing rot
Section titled “Guarding against pricing rot”price_source on every cost breakdown says which rule priced the call — a
paid model silently landing on unpriced_default would make every dollar
report a lie. CI runs scripts/check_pricing.py, which registers every
provider with fake keys and fails if any configured default model resolves
to the unpriced row.
The full shipped table (synced from the release) is in the configuration reference.