Core Docs

Cost & Billing

Cost & Billing



How core.ai calculates, stores, and enforces costs for LLM usage. Real USD cents tracked alongside platform credits for auditing.

Cost Model



Per-Call Billing



Each LLM API call is billed independently. A tool-use cycle with 2 API calls produces 2 cost records. Cost belongs to the API call, not to a message role.

Cost Categories



CategoryDescriptionExample Rate (GPT-4o)





InputTokens in the prompt (messages, system prompt, tools)$2.50 / 1M tokens
OutputTokens generated by the model$10.00 / 1M tokens
ThinkingExtended thinking tokens (o1, Claude, etc.)$15.00 / 1M tokens
CachedInput tokens that hit prompt cache (subset of input)$1.25 / 1M tokens

Note: Cached tokens are a subset of input tokens. When calculating cost, non-cached input tokens = input_tokens - cached_tokens. Cached tokens are charged at a reduced rate.

Cost Formula



input_cost    = (input_tokens - cached_tokens) × provider_input_rate
output_cost = output_tokens × provider_output_rate
thinking_cost = thinking_tokens × provider_thinking_rate
cached_cost = cached_tokens × provider_cached_rate

total_cost = input_cost + output_cost + thinking_cost + cached_cost


USD Cents Storage



All USD costs are stored as integer cents × 10000 (i.e., 4 decimal places of cents). This avoids floating-point precision issues.

Examples:

Real USDStored Value






$0.00011
$0.003232
$0.0100100
$1.500015000
$12.3456123456

Conversion:
  • Stored → USD: value / 10000

  • USD → Stored: value × 10000

  • Stored → cents: value / 100

Why Not Float?



Floating-point arithmetic introduces rounding errors. At LLM scale (millions of tokens, thousands of calls), small errors compound. Integer arithmetic is exact.

Why ×10000?



Provider rates go to 6 decimal places per token (e.g., $0.0000025/token). Multiplying by 10000 gives enough precision for per-call costs while staying in integer range. A single call costing $50 = 500000 stored units — well within INT range.

Provider Pricing Tables



Pricing is stored as a static config file (JSON), updated when providers change rates. Not in the database — this is operational config, not user data.

{
"providers": {
"openai": {
"models": {
"gpt-4o": {
"input_per_token": 0.0000025,
"output_per_token": 0.00001,
"thinking_per_token": 0.000015,
"cached_per_token": 0.00000125
},
"gpt-4o-mini": {
"input_per_token": 0.00000015,
"output_per_token": 0.0000006,
"thinking_per_token": 0.0000009,
"cached_per_token": 0.000000075
}
}
},
"anthropic": {
"models": {
"claude-sonnet-4-20250514": {
"input_per_token": 0.000003,
"output_per_token": 0.000015,
"thinking_per_token": 0.000015,
"cached_per_token": 0.0000003
}
}
},
"google": {
"models": {
"gemini-2.5-pro": {
"input_per_token": 0.00000125,
"output_per_token": 0.00001,
"thinking_per_token": 0.0000125,
"cached_per_token": 0.0000003125
}
}
}
}
}


Adding New Models



When a new model is added:
  1. Add its pricing to the config file

  2. Add its capabilities to the model registry

  3. Restart core.ai

No database migration needed.

Platform Credit Rates



Platform credits abstract away provider pricing. Users buy credits; credits are deducted per usage. Credit rates are defined in core.auth's ai_resource_costs table.

Credit rates should be set to cover provider costs with a margin. Example:


ResourceCredit RateCovers





Input token (non-cached)1 credit~$0.0000025
Output token2.5 credits~$0.00001
Thinking token3 credits~$0.000015
Cached token0.05 credits~$0.0000003

Credit Calculation



input_credits    = (input_tokens - cached_tokens) × input_rate
output_credits = output_tokens × output_rate
thinking_credits = thinking_tokens × thinking_rate
cached_credits = cached_tokens × cached_rate

total_credits = input_credits + output_credits + thinking_credits + cached_credits


Both total_credits and total_cost_cents_usd are stored in cost_ledger for every call.

Cost Ledger Queries



Daily cost for a user


SELECT
billing_date,
SUM(total_cost_cents_usd) / 10000 AS total_usd,
SUM(total_cost_credits) AS total_credits,
SUM(input_tokens) AS total_input_tokens,
SUM(output_tokens) AS total_output_tokens,
SUM(thinking_tokens) AS total_thinking_tokens,
SUM(cached_tokens) AS total_cached_tokens
FROM cost_ledger
WHERE user_id = ?
AND billing_date = '2026-07-09'
GROUP BY billing_date;


Monthly cost per project


SELECT
project_id,
SUM(total_cost_cents_usd) / 10000 AS total_usd,
SUM(total_cost_credits) AS total_credits,
COUNT(*) AS api_calls
FROM cost_ledger
WHERE billing_date BETWEEN '2026-07-01' AND '2026-07-31'
GROUP BY project_id
ORDER BY total_usd DESC;


Cost breakdown by model


SELECT
model_slug,
COUNT(*) AS calls,
SUM(input_tokens) AS input_tokens,
SUM(output_tokens) AS output_tokens,
SUM(thinking_tokens) AS thinking_tokens,
SUM(cached_tokens) AS cached_tokens,
SUM(total_cost_cents_usd) / 10000 AS total_usd
FROM cost_ledger
WHERE user_id = ?
AND billing_date BETWEEN '2026-07-01' AND '2026-07-31'
GROUP BY model_slug;


Cumulative cost for a conversation (computed, not stored)


SELECT
conversation_id,
created_at,
total_cost_cents_usd,
SUM(total_cost_cents_usd) OVER (
PARTITION BY conversation_id
ORDER BY created_at
ROWS UNBOUNDED PRECEDING
) AS cumulative_cost_cents_usd
FROM cost_ledger
WHERE conversation_id = ?
ORDER BY created_at;


No cumulative columns in the table. Compute on read with window functions. Fast enough for any realistic conversation length.

Quota Integration



Flow



1. API call completes (success or error)
2. Calculate costs (tokens × rates)
3. Insert into api_calls table
4. Insert into cost_ledger table
5. Deduct credits from user's core.auth quota
6. If quota exceeded → mark conversation as 'quota_exceeded'


Pre-check



Before making an API call, core.ai should check:
  1. Does the user have enough credits remaining?

  2. Is the project's monthly budget exhausted?

If either check fails, return an error before calling the provider. Don't waste a provider call on a user who can't pay.

Post-call deduction



After a successful (or partially successful) call:
  1. Calculate actual cost from token counts

  2. Deduct from user's credit balance in core.auth

  3. If the deduction pushes the user over quota → mark the conversation as quota_exceeded

  4. The user sees a message: "You've reached your usage limit. Upgrade your plan or wait for next month's reset."

Quota types affected



From core.auth's account_types:
  • AI credits — primary deduction target

  • Special credits — overflow pool (used when AI credits are exhausted, at defined conversion rates)

See Auth Architecture — Quotas for the full quota model.

Retention




TableRetentionNotes






conversationsUntil user deletesSoft delete (status = 'deleted')
messagesSame as conversationCascade soft delete
trace_eventsSame as conversationCascade soft delete
api_calls90 days (configurable)Purged after retention period
cost_ledger12 months (configurable)Aggregated into monthly summaries before purge

Monthly Summaries



Before purging old cost_ledger rows, core.ai aggregates them into a cost_monthly_summary table:

CREATE TABLE cost_monthly_summary (
id UUID PRIMARY KEY,
user_id INT NOT NULL,
project_id UUID NOT NULL,
month DATE NOT NULL, -- first day of month
model_slug VARCHAR(255) NOT NULL,
total_calls INT NOT NULL,
total_input_tokens INT NOT NULL,
total_output_tokens INT NOT NULL,
total_thinking_tokens INT NOT NULL,
total_cached_tokens INT NOT NULL,
total_cost_credits INT NOT NULL,
total_cost_cents_usd INT NOT NULL,
INDEX idx_user_month (user_id, month),
INDEX idx_project_month (project_id, month)
);


This keeps billing data available for historical queries without storing every individual call.

Related