Chio/Docs
LOGIN · JOIN

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.

VariantWire nameBehavior
PricingModel::FlatflatOne fixed base price; no parameter sensitivity.
PricingModel::PerInvocationper_invocationSame fixed unit price every call; billing unit is invocation.
PricingModel::PerUnitper_unitPrice scales with a declared billing unit such as 1k_tokens, MB, or row.
PricingModel::HybridhybridFixed base plus a per-unit variable component.

Results-priced counterpart

The four models above price a call on its inputs. For a call priced on results instead, see Outcome-Based Pricing: the payment is held and captured in full only if the output satisfies a declared predicate, and otherwise released to zero.

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.

crates/core/chio-core-types/src/capability/scope.rs80-91rust
/// 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:

crates/platform/chio-manifest/src/lib.rs146-157rust
/// 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:

Modelbase_priceunit_pricebilling_unit
flatSetAbsentAbsent or invocation
per_invocationAbsentSetinvocation
per_unitAbsentSetNames the scaling dimension
hybridSetSetNames the scaling dimension
tool-manifest-fragment.jsonjson
{
  "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

The shipped 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:

crates/economy/chio-listing/src/discovery.rs113-121rust
/// 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:

crates/economy/chio-listing/src/discovery.rs48-74rust
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_bps MUST sit in [0, 10000].
  • expires_at > issued_at; past-expiry hints are stale and the listing falls out of the marketplace.
  • availability_bps MUST sit in (0, 10000].
  • max_latency_ms and throughput_rps MUST 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.

crates/economy/chio-appraisal/src/marketplace_pricing.rs50-60rust
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:

crates/economy/chio-appraisal/src/marketplace_pricing.rs155-162rust
/// 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.

crates/core/chio-core-types/src/capability/governance.rs868-906rust
/// 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:

crates/core/chio-core-types/src/capability/governance.rs166-179rust
/// 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

VariantWire nameConfiguration
MustPrepaymust_prepayAction MUST NOT execute unless the quoted amount is prepaid.
HoldCapturehold_captureAction MAY execute against a hold and settle later via capture or release.
AllowThenSettleallow_then_settleAction MAY execute first and settle later with truthful pending state.

MeteredBillingQuote

crates/core/chio-core-types/src/capability/governance.rs76-95rust
/// 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.

crates/core/chio-core-types/src/capability/governance.rs97-102rust
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.

LayerSourceRole
Quoted costMeteredBillingQuote.quoted_costPre-execution estimate from the metering provider, bound to the intent.
Observed costTool server post-execution metering payloadWhat the tool actually consumed (token count, bytes, etc.) translated to monetary units.
Charged costReceipt metadata, capped by capability budgetWhat 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.

FieldTypeEffect
max_cost_per_invocationOption<MonetaryAmount>Per-call ceiling. Rejected if a quote or receipt charges more.
max_total_costOption<MonetaryAmount>Aggregate ceiling across all invocations under this grant.
max_invocationsOption<u32>Maximum number of invocations under this grant.

Where the kernel actually enforces

Manifest pricing is operator input, not enforcement. The kernel enforces capability caps. Set 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_evidence field 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

manifest-pricing-fragment.jsonjson
{
  "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

capability-grant.jsonjson
{
  "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

governed-intent.jsonjson
{
  "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.

rendering
Agent obtains a metered billing quote, binds it to the governed transaction intent, kernel verifies validity and capability ceiling, places hold, dispatches the call, captures actual usage on settlement.

Failure modes

  • Stale quote: quote expires_at has 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.

Picks up from previous station. The buyer kernel has admitted Vanguard's signed manifest for the 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.

crates/core/chio-core-types/src/capability/governance.rs64-74rust
/// 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,
}
lattice-soc2-review.governed-intent.jsonjson
{
  "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.

Continue at next station, where the buyer kernel resolves Vanguard as a pinned federation peer before issuing the call.