Every token spend creates a cost_event row: amount_usd, agent_id, run_id, model, input_tokens, output_tokens, timestamp. How the server computes amount_usd from model pricing tables.
The complete cost_events table, how costs are calculated per model, and the two-layer budget system (per-agent and per-company).
Paperclip's cost model has one rule: every adapter invocation that uses tokens creates a cost_events row. This makes the audit trail complete and the budget enforcement reliable.
cost_events TableCREATE TABLE public.cost_events (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
company_id uuid NOT NULL REFERENCES companies(id),
agent_id uuid NOT NULL REFERENCES agents(id),
heartbeat_run_id uuid REFERENCES heartbeat_runs(id),
issue_id uuid REFERENCES issues(id),
model text NOT NULL,
input_tokens int NOT NULL DEFAULT 0,
output_tokens int NOT NULL DEFAULT 0,
cost_usd numeric(10,6) NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now()
);
Each adapter reports inputTokens, outputTokens, and model. Paperclip multiplies by the per-model rate in pricing.ts:
// packages/server/src/lib/costs/pricing.ts
export type TokenPricing = { input: number; output: number }; // per 1K tokens
export const PRICING: Record<string, TokenPricing> = {
'claude-opus-4-5': { input: 0.015, output: 0.075 },
'claude-sonnet-4-5': { input: 0.003, output: 0.015 },
'claude-haiku-3-5': { input: 0.00025, output: 0.00125 },
'gpt-4o': { input: 0.005, output: 0.015 },
'gpt-4o-mini': { input: 0.00015, output: 0.0006 },
'llama3-70b-8192': { input: 0.00027, output: 0.00027 },
};
export function calculateCost(model: string, inputTokens: number, outputTokens: number): number {
const pricing = PRICING[model];
if (!pricing) return 0;
return (inputTokens / 1000 * pricing.input) + (outputTokens / 1000 * pricing.output);
}
Company budget ($50/month)
├── Agent: CEO budget $20/month
├── Agent: Researcher budget $15/month
└── Agent: Writer budget $10/month
Agent-level enforcement: before claiming a new issue, the heartbeat checks if the agent's MTD spend < monthly_budget_usd. If not, the agent is skipped (set to paused).
Company-level enforcement: when cumulative MTD company spend exceeds companies.monthly_budget_usd, the entire company is paused — no agents run until the month rolls over or the budget is raised.
-- Current month spend per agent
SELECT
a.name,
a.monthly_budget_usd AS budget,
COALESCE(SUM(ce.cost_usd), 0) AS spent,
a.monthly_budget_usd
- COALESCE(SUM(ce.cost_usd), 0) AS remaining,
ROUND(100 * COALESCE(SUM(ce.cost_usd), 0)
/ NULLIF(a.monthly_budget_usd, 0)) AS pct_used
FROM agents a
LEFT JOIN cost_events ce
ON ce.agent_id = a.id
AND date_trunc('month', ce.created_at) = date_trunc('month', now())
WHERE a.company_id = 'your-company-id'
GROUP BY a.id, a.name, a.monthly_budget_usd
ORDER BY pct_used DESC NULLS LAST;
cost_events table columns and their purpose