Tool-enabled AI routes change the budget problem.
A plain chat-completion estimate usually starts with input tokens, output tokens, model id, and retry policy. That is already enough to make finance and engineering compare notes. Once the route can call web search, fetch documents, process images, create media, or run long tool chains, a token-only estimate becomes too thin.
The request may still look OpenAI-compatible from the client. The operational evidence behind it is wider:
- which model route was admitted;
- which tools were allowed;
- whether the tool result was billed separately or folded into model usage;
- which pricing source was checked;
- which account group or route group applied;
- which release approved the tool policy;
- and which runtime receipt proves the actual usage.
For Tier 1 and Tier 2 teams, the useful question is not "can the model call tools?" It is "can the platform prove which tool-enabled route was allowed to spend, why it was allowed, and what evidence will be available after the run?"
This article gives a compact tool-fee budget gate for AI API routes. I use AIWave as the concrete example because it exposes dated public pricing JSON and OpenAI-compatible route metadata, but the pattern applies to any multi-model gateway or internal model platform.
For this run, I rechecked AIWave's public pricing endpoints on September 16, 2026. The static customer-facing endpoint at https://aiwave.live/api/v1/pricing returned 64 model rows, checked: 2026-09-10, updated_at: 2026-09-10, and pricing version 8c7a0c0b30661ccbc13d142cb54d1e4ae445fe774b2c6fa501080db97c7a3e56. The dynamic route table at https://aiwave.live/api/pricing returned 64 rows, pricing version a42d372ccf0b5dd13ecf71203521f9d2, route groups default, vip, and svip, and supported endpoint type openai. Treat those as dated source facts, not evergreen commercial copy.
Separate route price from tool policy
The first mistake is putting the model price and tool policy into the same unlabeled field.
Model pricing answers one question: what is the dated base rate for text tokens on a route?
Tool policy answers a different question: what extra capabilities may this workload invoke, under what limits, and with what receipt fields?
Keep them separate in code and in review packets. A route may be acceptable for normal chat but blocked for search-enabled work. Another route may be acceptable for search only when the application stores a source list, a query count, a redacted tool receipt, and an output cap.
A useful policy record can be small:
{
"policy_id": "support-search-summary-v3",
"model": "glm-5",
"endpoint_shape": "openai_chat_completions",
"pricing_source": "https://aiwave.live/api/v1/pricing",
"pricing_checked": "2026-09-10",
"pricing_view": "public_base_rate",
"allowed_tools": ["web_search"],
"max_tool_calls": 2,
"max_output_tokens": 900,
"requires_receipt": true,
"owner": "platform-ai"
}
Notice what is absent: no prompt, no response body, no reusable key, no customer name, and no private account detail. The record is a release contract, not a debug transcript.
Admit the run before the first call
The budget gate should run before the first model call, not after the invoice review.
It should combine four inputs:
| Input | Example field | Why it matters |
|---|---|---|
| Route |
model, endpoint_shape
|
Prevents unsupported request shapes from shipping |
| Pricing |
pricing_checked, pricing_version
|
Keeps estimates tied to dated source evidence |
| Tools |
allowed_tools, max_tool_calls
|
Stops hidden search or media work from expanding silently |
| Workload |
input_tokens_estimate, output_cap
|
Creates a bounded envelope before execution |
Here is a minimal Python shape:
from dataclasses import dataclass
from decimal import Decimal
@dataclass(frozen=True)
class RoutePrice:
model: str
input_per_1m: Decimal
output_per_1m: Decimal
cache_hit_per_1m: Decimal | None
pricing_checked: str
pricing_version: str
@dataclass(frozen=True)
class ToolPolicy:
policy_id: str
allowed_tools: tuple[str, ...]
max_tool_calls: int
max_output_tokens: int
receipt_required: bool = True
@dataclass(frozen=True)
class RunEstimate:
input_tokens: int
cached_input_tokens: int
output_tokens_cap: int
requested_tools: tuple[str, ...]
def estimate_text_usd(price: RoutePrice, run: RunEstimate) -> Decimal:
fresh_input = max(run.input_tokens - run.cached_input_tokens, 0)
total = Decimal(fresh_input) * price.input_per_1m / Decimal(1_000_000)
if price.cache_hit_per_1m is not None:
total += Decimal(run.cached_input_tokens) * price.cache_hit_per_1m / Decimal(1_000_000)
total += Decimal(run.output_tokens_cap) * price.output_per_1m / Decimal(1_000_000)
return total
def admit(policy: ToolPolicy, run: RunEstimate) -> list[str]:
problems: list[str] = []
unknown_tools = sorted(set(run.requested_tools) - set(policy.allowed_tools))
if unknown_tools:
problems.append(f"tools_not_allowed:{','.join(unknown_tools)}")
if len(run.requested_tools) > policy.max_tool_calls:
problems.append("too_many_tool_calls")
if run.output_tokens_cap > policy.max_output_tokens:
problems.append("output_cap_exceeds_policy")
return problems
The important part is not the exact function. The important part is that admission produces a decision before the route spends. A rejected run should fail with a policy reason. An admitted run should carry the policy id, pricing version, and estimate id into the runtime receipt.
Add a separate tool-fee envelope
Some tool usage has a simple unit, such as number of searches. Some has a route-specific charge. Some is bundled by the platform. Some depends on upstream behavior that your application cannot safely infer from a model name.
Do not hide that uncertainty in a token estimate.
Add a separate envelope:
{
"tool_fee_envelope": {
"billing_view": "policy_enforced_limit",
"web_search": {
"allowed": true,
"max_calls": 2,
"pricing_source": "route_policy",
"runtime_receipt_field": "tool_calls.web_search"
},
"media_input": {
"allowed": false,
"reason": "not_approved_for_this_workload"
}
}
}
That envelope makes the review honest. If the team has not approved media processing for a workflow, the runtime should not get to enable it just because the model can accept media. If search is allowed, the receipt should record how many searches happened, which policy admitted them, and whether the result was used.
Record the runtime receipt
After execution, the platform should export a receipt that can be reviewed without raw prompts or private user data.
A compact receipt can include:
request_id_hashpolicy_idmodelpricing_sourcepricing_checkedpricing_versionaccount_group_labelinput_tokenscached_input_tokensoutput_tokenstool_callstool_policy_resultretry_countfinal_status
The receipt should not include the API key, prompt text, response body, payment identifier, IP address, or customer name. If a support engineer needs to debug content quality, use a separate redacted fixture process. The budget receipt only proves route, price, tool, and usage facts.
This separation helps during incident review. A run may fail because a tool was blocked by policy, because the output cap was too small, because a route was unavailable, or because a request exceeded a token budget. Those outcomes should produce different support messages and different engineering actions.
Run a canary when the policy changes
The last gate is a canary.
Run it whenever a route policy changes, a model is added to a tool-enabled workflow, a pricing snapshot changes, or an SDK example starts using a new tool.
The canary should check:
- The model exists in the approved pricing snapshot.
- The route supports the endpoint shape the SDK will call.
- The policy lists every requested tool.
- The output cap and tool-call cap are bounded.
- The receipt schema can store the expected tool evidence.
- The public example uses an obvious placeholder such as
YOUR_API_KEY_HERE, never a realistic key shape.
This is a small test, but it catches the quiet failures: stale model names, unlabeled pricing dates, examples that imply unsupported tools, and routes that accidentally allow a tool without a receipt.
What to ship
A tool-fee budget gate does not need to be a large platform. Start with four artifacts:
- a dated pricing snapshot;
- a route policy file;
- an admission function;
- and a redacted runtime receipt.
That is enough to make tool-enabled AI routes reviewable. Engineering can explain what shipped. Finance can see which dated pricing source was used. Support can tell whether a tool was allowed or blocked. Security can verify that the shared evidence does not contain prompts, reusable credentials, or customer-identifying data.
The model route is still useful. The tools are still useful. The difference is that each tool-enabled run now leaves evidence before and after it spends.
That is the contract production buyers actually need.


Top comments (0)