EconomyPrices
Pricing Models
Signed manifests advertise prices. The kernel checks each call against its capability budget, and metered billing binds usage-priced calls to a quote.
The four pricing models
The PricingModel enum in chio-manifest has four variants. The wire spelling is snake_case.
| Variant | Wire name | Behavior |
|---|---|---|
PricingModel::Flat | flat | One fixed base price; no parameter sensitivity. |
PricingModel::PerInvocation | per_invocation | Same fixed unit price every call; billing unit is invocation. |
PricingModel::PerUnit | per_unit | Price scales with a declared billing unit such as 1k_tokens, MB, or row. |
PricingModel::Hybrid | hybrid | Fixed base plus a per-unit variable component. |
Results-priced counterpart
All prices are denominated in minor currency units (cents for USD, the smallest denomination for other currencies), matching the MonetaryAmount type from chio-core-types::capability::scope.
/// A monetary amount with currency denomination.
///
/// Uses minor-unit integers to avoid floating-point precision issues.
/// For USD, 1 dollar = 100 units (cents). For JPY, 1 yen = 1 unit.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MonetaryAmount {
/// Amount in the currency's smallest unit (e.g. cents for USD).
pub units: u64,
/// ISO 4217 currency code. Examples: "USD", "EUR", "JPY".
pub currency: String,
}ToolPricing
Each ToolDefinition in a manifest may carry an optional ToolPricing block. The fields are flat:
/// Optional pricing metadata advertised by a tool server.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ToolPricing {
pub pricing_model: PricingModel,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_price: Option<MonetaryAmount>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_price: Option<MonetaryAmount>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub billing_unit: Option<String>,
}Which fields are populated depends on the model:
| Model | base_price | unit_price | billing_unit |
|---|---|---|---|
flat | Set | Absent | Absent or invocation |
per_invocation | Absent | Set | invocation |
per_unit | Absent | Set | Names the scaling dimension |
hybrid | Set | Set | Names the scaling dimension |
{
"name": "summarize",
"description": "Returns a model-generated summary",
"input_schema": { "...": "..." },
"pricing": {
"pricing_model": "hybrid",
"base_price": { "units": 100, "currency": "USD" },
"unit_price": { "units": 5, "currency": "USD" },
"billing_unit": "1k_tokens"
},
"has_side_effects": false,
"latency_hint": "moderate"
}No SLA fields on ToolPricing
ToolPricing struct in chio-manifest does not include SLA guarantees, currency caps, or per-call ceilings. Those values live on the marketplace listing (ListingSla,ListingPricingHint) or on the capability grant (max_cost_per_invocation,max_total_cost) rather than the manifest pricing block.SLA on marketplace listings
Service-level commitments live on the marketplace listing, signed by the operator and paired with a price hint. The struct is ListingSla in chio-listing::discovery:
/// Service-level advertisement paired with a pricing hint.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ListingSla {
pub max_latency_ms: u64,
/// Availability SLA expressed in basis points. `10_000` means 100.00%.
pub availability_bps: u32,
pub throughput_rps: u64,
}The signed pricing hint ListingPricingHint attaches one SLA, one price-per-call, and a recent-activity snapshot to a published listing:
pub struct ListingPricingHint {
pub schema: String,
/// Listing this hint applies to.
pub listing_id: String,
/// Namespace of the listing (must match the listing body).
pub namespace: String,
/// Provider / operator advertising the price (must match the listing
/// publisher).
pub provider_operator_id: String,
/// Capability scope prefix covered by this hint (e.g.
/// `"tools:search"` or `"tools:search:*"`). Queries filter against this.
pub capability_scope: String,
/// Fixed price charged per invocation under the advertised scope.
pub price_per_call: MonetaryAmount,
/// Advertised SLA for invocations under this hint.
pub sla: ListingSla,
/// Rolling revocation rate over recent invocations, in basis points.
/// `0` means "no revocations in the window"; `10_000` means "100%".
pub revocation_rate_bps: u32,
/// Number of receipts the provider has produced in the recent window.
pub recent_receipts_volume: u64,
/// Unix seconds when the hint was issued.
pub issued_at: u64,
/// Unix seconds when the hint expires. Past expiry, the hint is stale
/// and the listing falls out of the marketplace.
pub expires_at: u64,
}The schema field must equal LISTING_PRICING_HINT_SCHEMA, which is chio.marketplace.listing-pricing-hint.v1 (crates/economy/chio-listing/src/discovery.rs:30).
Validation rules from ListingPricingHint::validate and ListingSla::validate:
price_per_call.units > 0; a zero-unit price is rejected.revocation_rate_bpsMUST sit in[0, 10000].expires_at > issued_at; past-expiry hints are stale and the listing falls out of the marketplace.availability_bpsMUST sit in(0, 10000].max_latency_msandthroughput_rpsMUST be greater than zero.
Reputation-tier pricing
chio-appraisal derives a marketplace invocation price from an advertised base price and the caller's reputation tier. The model is deterministic: equal inputs produce equal outputs, and the discount math is integer-only.
pub enum MarketplaceReputationTier {
/// Default tier. Highest invocation price (no discount).
#[default]
Tier0,
/// Trusted publisher tier with a small discount.
Tier1,
/// High-trust tier.
Tier2,
/// Highest-trust tier with the largest discount.
Tier3,
}The tier indexes a discount table held in the same module:
/// Discount table consumed by [`compute_marketplace_invocation_price`].
///
/// The table is intentionally small and deterministic. Tier0 pays the
/// full sticker price (no discount). Higher tiers receive monotonic
/// discounts. Discounts are expressed as units per hundred so the
/// resolution is one percent: a value of `5` means a five-percent
/// discount applied via integer arithmetic.
pub const TIER_DISCOUNT_PER_HUNDRED: [u32; 4] = [0, 5, 10, 20];Tier0 pays the full sticker price. Tier1, Tier2, and Tier3 take 5%, 10%, and 20% off respectively. A zero-priced base stays zero regardless of tier, and rounding is half-down by truncation to preserve minor-unit semantics.
Settlement-facing callers use compute_checked_marketplace_invocation_price, which validates the MarketplaceBasePrice and MarketplacePricingContext before applying the discount and returns a MarketplaceInvocationPrice. The unchecked compute_marketplace_invocation_price helper remains for callers that already validate their inputs. The discount is layered on top of the advertised ToolPricing; the manifest price is the sticker, the tier is the caller's standing.
Metered billing on the governed intent
For tools where the cost depends on something the kernel cannot see ahead of time (token counts, transferred bytes, outbound third-party fees), the agent attaches a MeteredBillingContext to its GovernedTransactionIntent. This binds the call to a quote from a billing or metering provider with a stated settlement configuration.
/// Canonical intent attached to a governed transaction request.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GovernedTransactionIntent {
/// Unique intent identifier (UUIDv7 recommended).
pub id: String,
/// Target tool server for this governed action.
pub server_id: String,
/// Target tool name for this governed action.
pub tool_name: String,
/// Human or policy-readable purpose for the governed action.
pub purpose: String,
/// Optional maximum amount explicitly approved for this intent.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_amount: Option<MonetaryAmount>,
/// Optional commerce approval context for seller-scoped payment rails.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub commerce: Option<GovernedCommerceContext>,
/// Optional metered-billing quote and settlement context for non-rail tools.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metered_billing: Option<MeteredBillingContext>,
/// Optional runtime attestation evidence bound to this governed request.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime_attestation: Option<RuntimeAttestationEvidence>,
/// Optional delegated call-chain context for upstream transaction provenance.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub call_chain: Option<GovernedCallChainContext>,
/// Optional explicit autonomy tier and delegation-bond attachment.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub autonomy: Option<GovernedAutonomyContext>,
/// Optional structured context for downstream policy or operator inspection.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context: Option<serde_json::Value>,
/// Typed governed-action body. Tool invocation remains the wire-compatible default.
#[serde(
default,
skip_serializing_if = "GovernedTransactionIntentBody::is_tool_invocation"
)]
pub body: GovernedTransactionIntentBody,
}The intent carries no rename_all, so its wire keys are the Rust field names: metered_billing, max_amount. The nested context switches casing on its own:
/// Generic metered-billing context attached to a governed request.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MeteredBillingContext {
/// Settlement posture expected for this metered tool action.
pub settlement_mode: MeteredSettlementMode,
/// Pre-execution quote bound to the governed request.
pub quote: MeteredBillingQuote,
/// Optional explicit upper bound on billable units for the request.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_billed_units: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub verified_outcome: Option<VerifiedOutcomeRequestV1>,
}MeteredBillingContext renames its fields to camelCase, so the block nested under the snake_case metered_billing key serializes as settlementMode and maxBilledUnits. Its verified_outcome field carries the results-priced request covered on Outcome-Based Pricing.
MeteredSettlementMode
| Variant | Wire name | Configuration |
|---|---|---|
MustPrepay | must_prepay | Action MUST NOT execute unless the quoted amount is prepaid. |
HoldCapture | hold_capture | Action MAY execute against a hold and settle later via capture or release. |
AllowThenSettle | allow_then_settle | Action MAY execute first and settle later with truthful pending state. |
MeteredBillingQuote
/// Stable quote describing pre-execution metered billing expectations.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MeteredBillingQuote {
/// Stable quote identifier from the billing or metering authority.
pub quote_id: String,
/// Billing or metering provider that issued the quote.
pub provider: String,
/// Billing unit used to interpret `quoted_units` (for example `1k_tokens`).
pub billing_unit: String,
/// Quoted number of billable units for the pre-execution estimate.
pub quoted_units: u64,
/// Quoted monetary amount for the estimate.
pub quoted_cost: MonetaryAmount,
/// Unix timestamp (seconds) when the quote was issued.
pub issued_at: u64,
/// Optional Unix timestamp (seconds) when the quote expires.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expires_at: Option<u64>,
}Quote validity windows
The kernel calls is_valid_at(now) before applying the quote. Quotes with no expires_at remain valid indefinitely (subject to the issuing provider's revocation), but a quote with an expiry that has passed is rejected and the agent must obtain a fresh quote.
impl MeteredBillingQuote {
#[must_use]
pub fn is_valid_at(&self, now: u64) -> bool {
now >= self.issued_at && self.expires_at.is_none_or(|expires_at| now < expires_at)
}
}Three cost layers
Pricing decisions flow through three distinct values. They live in different places and serve different roles.
| Layer | Source | Role |
|---|---|---|
| Quoted cost | MeteredBillingQuote.quoted_cost | Pre-execution estimate from the metering provider, bound to the intent. |
| Observed cost | Tool server post-execution metering payload | What the tool actually consumed (token count, bytes, etc.) translated to monetary units. |
| Charged cost | Receipt metadata, capped by capability budget | What the kernel actually deducted, after applying budget caps and max_billed_units. |
These three values are reconciled after settlement. See Reconciliation for the full flow.
Budget caps on the capability grant
The capability grant carries the runtime ceiling. Two fields on ToolGrant bound how much a single grant can spend.
| Field | Type | Effect |
|---|---|---|
max_cost_per_invocation | Option<MonetaryAmount> | Per-call ceiling. Rejected if a quote or receipt charges more. |
max_total_cost | Option<MonetaryAmount> | Aggregate ceiling across all invocations under this grant. |
max_invocations | Option<u32> | Maximum number of invocations under this grant. |
Where the kernel actually enforces
max_cost_per_invocation consistent with the advertised price plus a local safety margin, so a tool server that quietly raises its price loses the call instead of overrunning the budget.Cross-currency settlement
When the budget is denominated in one currency and the quote in another, the kernel needs an exchange rate. Chio sources rates from oracle integrations and records the source on the receipt. See Chainlink integration for the supported feed interface.
- The agent or authority requests a conversion at intent time. The kernel records the source feed, the round id, and the rate used.
- Receipts carry an
oracle_evidencefield naming the feed and the conversion that was applied. Auditors can re-verify against the same feed at the same round. - A stale or unreachable feed fails closed; the kernel refuses to invent a rate.
Worked example: per-unit pricing with a quote
A summarization tool charges 5 cents per 1,000 tokens. The operator publishes the manifest, an authority issues a capability with a USD 5.00 ceiling, and the agent requests a quote before the call.
1. Publish the pricing block
{
"name": "summarize",
"description": "Returns a model-generated summary",
"input_schema": { "...": "..." },
"pricing": {
"pricing_model": "per_unit",
"unit_price": { "units": 5, "currency": "USD" },
"billing_unit": "1k_tokens"
},
"has_side_effects": false,
"latency_hint": "moderate"
}2. Issue a capability with a ceiling
{
"tool_grant": {
"server_id": "srv-summary",
"tool_name": "summarize",
"operations": ["invoke"],
"max_invocations": 50,
"max_cost_per_invocation": { "units": 500, "currency": "USD" },
"max_total_cost": { "units": 50000, "currency": "USD" }
}
}3. Obtain a quote
Before invoking, the agent asks its metering provider for a pre-execution quote. The provider issues a stable MeteredBillingQuote with quoted_units = 8 (eight thousand-token blocks) and quoted_cost = USD 0.40.
4. Bind the quote to the intent
{
"id": "intent-2026-04-28-001",
"server_id": "srv-summary",
"tool_name": "summarize",
"purpose": "draft summary for ticket #3120",
"max_amount": { "units": 500, "currency": "USD" },
"metered_billing": {
"settlementMode": "hold_capture",
"quote": {
"quoteId": "q-2026-04-28-991",
"provider": "metering.example",
"billingUnit": "1k_tokens",
"quotedUnits": 8,
"quotedCost": { "units": 40, "currency": "USD" },
"issuedAt": 1714287000,
"expiresAt": 1714287600
},
"maxBilledUnits": 12
}
}5. Dispatch and settle
The kernel verifies the quote validity window, confirms the capability ceiling covers the quote, places a hold for USD 0.40, dispatches the call, and signs a receipt. Post-execution the metering provider observes nine thousand-token blocks; the tool reports observed cost USD 0.45. The kernel captures up to max_billed_units = 12, in this case the actual nine units, and records the charged cost on the receipt.
Failure modes
- Stale quote: quote
expires_athas passed before dispatch. Kernel refuses; agent obtains a fresh quote. - Quoted cost exceeds capability ceiling: rejected at intent verification with a budget error.
- Observed cost exceeds
max_billed_units: the kernel charges the cap, marks the receipt with overrun metadata, and pauses further dispatch under the same grant until reconciliation. - Currency mismatch with no oracle: a USD budget against an EUR quote without a configured rate source fails closed.
- Provider unknown: a quote signed by a metering provider absent from the authority's trusted set is rejected.
In the procurement tour
This is station 3 of the Procurement Tour: buyer wraps Vanguard's quote in a GovernedTransactionIntent with a metered-billing block.
soc2-review tool and has its advertised ToolPricing in hand.Lattice issues an MCP request_quote for 1000 evidence rows. Vanguard returns a quote bound to a five-minute validity window. The buyer kernel pins it as a MeteredBillingContext on the governed intent. The settlement configuration is hold_capture: an escrow hold lands before the tool runs and settles by capture or release based on observed usage.
/// Policy-visible settlement posture for quoted metered billing.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MeteredSettlementMode {
/// The action should not execute unless the quoted amount is prepaid.
MustPrepay,
/// The action may execute against a hold and settle later via capture/release.
HoldCapture,
/// The action may execute first and settle later with truthful pending state.
AllowThenSettle,
}{
"id": "intent-soc2-review-001",
"server_id": "vanguard-security",
"tool_name": "soc2-review",
"purpose": "SOC 2 evidence review for Q2 release window",
"max_amount": { "units": 110, "currency": "USD" },
"metered_billing": {
"settlementMode": "hold_capture",
"quote": {
"quoteId": "quote_vanguard_soc2_001",
"provider": "did:chio:c87a...",
"billingUnit": "evidence-row",
"quotedUnits": 1000,
"quotedCost": { "units": 100, "currency": "USD" },
"issuedAt": 1745870400,
"expiresAt": 1745870700
},
"maxBilledUnits": 1100
}
}The buyer kernel computes binding_hash() over the intent. That hash anchors the next station's federation resolution, the dispatch, and both sides of the receipt pair.
Related
- Signed Tool Manifests covers the schema that carries the pricing block.
- Capability Discovery covers the listing layer that shows SLA hints alongside prices.
- Reconciliation covers the post-settlement comparison of quoted, observed, and charged cost.
- Guide: Tool Pricing walks through declaring pricing in Rust, TypeScript, and Python.