Economics on Every Paid Surface
Your application already knows how to spend money on a user's behalf — an LLM call, an embedding, a web search. The question is whether the platform gets to decide, before the call runs, that this user can afford it, and to charge the real cost afterward. KDCube makes that decision the same on every surface, without your application reimplementing plans, wallets, or budgets.
A paid surface is any place an application reaches a paid service: a chat turn, a standalone search box, a REST operation, a background job, a cron routine. The chat entrypoint already runs turns under the economics model. Everything else — the accountable work that runs on a user's behalf without a chat turn — used to be on its own. Now it runs under the same model through one small enforcement engine.
The model has one shape everywhere: verify, reserve, run, settle. Verify that the user's quota and funding can cover an estimate; reserve that estimate so concurrent requests cannot oversubscribe it; run the work; settle the actual cost across the funding sources. Deny before anything runs, and no money hold is left behind.
One request is one funding split
Before it can reserve anything, the engine resolves who pays. Every request is a single split. A primary funding source — an external subscription's period budget, or the project budget for everyone else — covers the part bounded by both the remaining plan quota and the primary funds. The user's wallet covers the remainder.
R = estimated cost
plan_part = min(R, Q, P) # Q = remaining plan quota, P = primary funds
wallet_part = R - plan_part # the over-quota / over-funds remainder
When the plan quota or funds run out, plan_part simply shrinks toward zero
and the wallet covers the rest in the same pass. The request only denies when there is no
wallet, or the wallet cannot cover the remainder. At settlement the same order holds — plan
quota and primary funds first, wallet for the over-quota remainder — and if the actual
spend still overshoots, the project budget absorbs the residual as a last resort.
Subscriptions and wallets never go negative.
This is the part your application does not want to rewrite, and does not have to. It resolves once, in the engine, from the request's economics subject.
Two entry points, chosen by who settles
The engine offers exactly two ways in, and the choice is about who settles the
cost. EconomicsGuard is an async context manager around one accountable
flow. On enter it resolves plan and funding, admits against quota, reserves the estimate,
and binds accounting to the flow. On exit it reads back the flow's usage and settles the
actual cost — committing or releasing the reservation.
async with EconomicsGuard(
self, subject=subject,
scope_id="report_render_42", flow="reports.render",
estimate=EconomicsEstimate(reservation_usd=0.05),
) as decision:
result = await do_the_paid_work() # accounted calls run here
economic_preflight is the same admit and funding resolution with
no reservation and no settlement — a feasibility gate for when the cost is
metered elsewhere, or the caller degrades gracefully on denial.
try:
await economic_preflight(self, subject=subject,
estimate=EconomicsEstimate(reservation_usd=0.01),
flow="reports.preview")
except EconomicsLimitException:
use_cheaper_path()
Both raise EconomicsLimitException before any work runs. The code
says why — rate_limited, no_funding_source,
quota_lock_timeout — and the surface picks its reaction.
Search degrades instead of failing
The most common paid surface is semantic search, and it has a purpose-built facade so components never touch the guard. An economics-enabled entrypoint hands a searchable component one dependency:
model_service = self.search_model_service(flow="reports.search", subject=subject)
Two methods, deliberately different in how they fail.
embed_texts([...])embeds document/index material and lets exceptions propagate — a write must not silently skip content.embed_search_query(query, flow=...)wraps the query embed in a guard and returnsNoneon economics denial, so the query layer falls back to lexical ranking (BM25, keyword) instead of failing the request.
Search under economics does not break — it gets cheaper. Memory search, canvas pin search, and task search all degrade this way.
The same work is never charged twice
A chat turn marks itself as the active parent economics scope while it runs the bundle core. A guard — or the search facade — entered inside that scope automatically degrades to verify-only: it checks feasibility but leaves the usage event in the parent turn, and the turn settles it. A guard outside any parent scope creates and settles its own operation scope. You do not wire this; a context variable carries it. The rule is simply that one unit of work settles once.
What your application actually writes
Becoming economics-enabled is extending one base
(BaseEntrypointWithEconomics), which binds the runtime primitives the engine
reuses. From there, guarding a paid surface is three decisions: resolve the subject (who
pays), size the estimate (a few cents for most non-chat flows), pick
EconomicsGuard or economic_preflight. The plans, wallets, quota
windows, reservations, shortfall absorption, and settlement math stay in the engine.
And when the paid work is your own — not an LLM, embedding, or search, but a metered API
you call — you make it countable with a tracker so the guard has a real cost to
settle. That is a short, separate move, covered in the companion journal note and recipe.
The alternate runtimes ride the same rails: a Claude Code turn is accounted as ordinary LLM
usage — same tracker, provider="anthropic", a self-reported per-turn cost —
priced right beside the built-in ReAct runtime's calls. The result is a single answer to
"may this run, and who pays?" that holds whether the paid call sits in a chat turn, a search
box, a REST handler, or a job — and an application that spends a user's money only when the
platform has agreed it can.