Chio/Docs
LOGIN · JOIN

Tool Pricing

Lower a service’s published price and follow the charge from the signed offer, through the agent’s grant, to one signed receipt.

Guide chio@5f63e6e

On this page

A provider signs its price into the tool manifest. An issuer signs how much an agent may spend. The kernel admits each call within the grant, and the receipt records what the call cost.

Continue the metered discount service from Budgets & Metering. Lower its price from 20 to 15 USD minor units, and the same 50-unit allowance buys three discount checks.

Start

1. Lower the price to 15 units

Before you begin

You need the pricing-metered project from Budgets & Metering and the Chio CLI. The project supplies the service and the key that signs its manifest, an issuer that sets monetary limits on the grant, a connection that reports realized cost, durable admission, and a local-credit payment adapter. Amounts are minor currency units: 100 USD units is one dollar.

In the service, replace the price constant. The service uses it both in its signed manifest and in the cost its connection reports.

src/service.rs · replace the price constantrust
pub const PRICE_UNITS: u64 = 15;

In the host, lower the per-call ceiling to match and keep the total allowance at 50. The local adapter captures exactly the authorized amount, so price and ceiling move together.

src/host.rs · per-call authority and total allowancerust
pub const CALL_CEILING: u64 = 15;
pub const TOTAL_BUDGET: u64 = 50;

2. Run three calls under the same allowance

Run the service again from the project directory. The run issues a fresh capability with the new ceiling.

Terminal · pricing-meteredbash
cargo run --release --locked -- sku-204 15
Output
call-1: ALLOW
  result: {"currency":"USD","percent":15,"price":"49.30","sku":"sku-204"}
  charge: 15 USD; settlement: settled
  budget: 35 available; 15 spent (USD)
  reserved: 0
  handler invocations: 1
call-2: ALLOW
  result: {"currency":"USD","percent":15,"price":"49.30","sku":"sku-204"}
  charge: 15 USD; settlement: settled
  budget: 20 available; 30 spent (USD)
  reserved: 0
  handler invocations: 2
call-3: ALLOW
  result: {"currency":"USD","percent":15,"price":"49.30","sku":"sku-204"}
  charge: 15 USD; settlement: settled
  budget: 5 available; 45 spent (USD)
  reserved: 0
  handler invocations: 3
evidence: runs/3750e077-fe09-4160-a5dd-ccee3ba7c0cd

You should see three calls admitted, 45 units spent, and 5 available. Editing the constants never mutates a capability already issued; the next run signs a new one. The local ledger records credits and moves no external money.

3. Read the offer the provider signed

Open manifest.json in the printed evidence directory and find the tool's pricing block.

manifest.json · tools[0].pricingjson
{
  "pricing_model": "per_invocation",
  "unit_price": {
    "units": 15,
    "currency": "USD"
  },
  "billing_unit": "invocation"
}

You should see per_invocation at 15 USD units. Three values now agree because this application connects them: the manifest advertises 15, the grant permits 15 per call, and the connection reports 15. Each comes from a different actor at a different moment.

Price and authorityChoose an outcome.
pricedispatchceilingqquoteκgrantmax_cost_per_invocationKkerneldtoolrefusedno dispatchρReceiptamount, settlement statepricedispatchceilingqquoteκgrantKkerneldtoolrefusedno dispatchρReceiptamount, settlement state

Spending authority comes from the grant

max_cost_per_invocation and max_total_cost bound what the agent may commit, independently of any price the provider names. Budgets and metering.

One priced call: a provider quote and a grant's spending ceilings meeting at the kernel. The amounts follow the 40-cent example and are illustrative; billing units and reconciliation are left out.Pricing models

The kernel enforces the grant. An issuer reads the provider's price and decides what budget to sign, so widening an agent's spending takes that issuer. After execution, the connection reports cost, the payment module reconciles it, and the receipt records the result.

Read more in Pricing models.

4. Verify the charge in the signed receipt

The first receipt of the run carries this financial block. It records the charge, the remaining allowance, and the identifiers that join the authorization to its completed local capture.

call-1-receipt.json · complete financial blockjson
{
  "budget_remaining": 35,
  "budget_total": 50,
  "cost_breakdown": {
    "payment": {
      "authorization_id": "hold:d6ffa273e4882e6a0454ef6f25bf01582dd488a368ca09015c9ea989e6b2ec1e",
      "preauthorized_units": 15,
      "rail": "finding-operator-ledger",
      "rail_mode": "reversible_hold",
      "recorded_units": 15,
      "transaction_id": "capture:d6a78ca355ecb41e407513f49efe01fcea77bdefd253cfd0b511c16364584340"
    }
  },
  "cost_charged": 15,
  "currency": "USD",
  "delegation_depth": 0,
  "grant_index": 0,
  "payment_reference": "capture:d6a78ca355ecb41e407513f49efe01fcea77bdefd253cfd0b511c16364584340",
  "root_budget_holder": "4806f7a942076d27d3652d6d74b861455fce7eaeb7e235957ea26d8e87cb075d",
  "settlement_status": "settled"
}

In the evidence directory, verify the receipt against the host key you selected before any receipt arrived.

Verify your priced callbash
cargo run --release --locked --bin verify-receipt -- call-1-receipt.json kernel-public-key.txt
Output
{
  "authorized": true,
  "crypto_error": null,
  "crypto_floor": "allow_classical",
  "decision": {
    "verdict": "allow"
  },
  "ok": true,
  "parameter_hash_valid": true,
  "receipt_id": "607a245ba9f85470eb7277a30754527deeb47e8233880f583d0f8c53b50169ed",
  "receipt_id_valid": true,
  "signature_valid": true,
  "signer_trusted": true
}

You should see signature_valid and signer_trusted both true, and the receipt's recorded decision. For a remote operator, take its expected key from trusted configuration. Settlement reads the payment identifiers in this block.

A price reaches the kernel only through the grant

The kernel reads the grant at admission and the reported cost at finalization; it reads the manifest price at neither. Raise only the advertised price to 30 while the grant and the reported charge stay at 20, and the two admitted calls still charge 20 each.

The service reported more than the grant authorized

A higher report from the service is a different event: reporting 30 against an authorization of 20 fails during finalization, after the service ran.

Reported cost above the durable authorizationtext
Error: durable admission failed: reported cost exceeds the durable payment authorization

The process returns no tool response and no billable receipt, and the local payment stays held. Keep the run's stores for diagnosis and resolve the hold before a new run; a retry runs the service again. Align the charge and the ceiling, or use a host and adapter built for variable charges.

The call is unpayable

A governed intent that mandates prepayment with no payment adapter configured is refused before the tool runs. The kernel signs a deny receipt for the refusal and charges nothing.

The signed refusal of an unpayable prepaid calltranscript
$ jq '{decision, financial: .metadata.financial}' ./bundle-deny.json
{
  "decision": {
    "verdict": "deny",
    "reason": "governed transaction denied: governed intent mandates prepayment (settlement_mode=MustPrepay) but no payment adapter is configured",
    "guard": "kernel"
  },
  "financial": {
    "attempted_cost": 100,
    "budget_remaining": 1000,
    "budget_total": 1000,
    "cost_charged": 0,
    "currency": "USD",
    "delegation_depth": 0,
    "grant_index": 0,
    "root_budget_holder": "95dd6964b2c519849a0f53ded5c810fc87791bb622ace9b887537de4c51a2c59",
    "settlement_status": "not_applicable"
  }
}
exit 0deny

Pricing models, manifests, quotes, and currencies

Pricing models

Manifests support four models, spelled Flat, PerInvocation, PerUnit, and Hybrid in the PricingModel enum. Each has a builder method on NativeTool in Rust. Every price is declared in minor currency units, matching MonetaryAmount.

ModelRust builderBehavior
flat
flat_price
(units, currency)
One fixed base price; no parameter sensitivity
per_invocation
per_invocation_price
(units, currency)
Same fixed price per call; billing unit "invocation"
per_unit
per_unit_price
(units, currency, billing_unit)
Scales with a declared unit such as 1k_tokens or MB
hybrid
hybrid_price
(base_units, unit_units, currency, billing_unit)
Fixed base plus a per-unit component

Manifest pricing fields

Every ToolDefinition may carry a pricing object. The four models share one shape with different fields populated.

tool-manifest-fragment.jsonjson
{
  "name": "search",
  "description": "Search competitor prices for a SKU",
  "input_schema": { "...": "..." },
  "pricing": {
    "pricing_model": "per_invocation",
    "unit_price":    { "units": 25, "currency": "USD" },
    "billing_unit":  "invocation"
  }
}

A hybrid block also carries base_price:

hybrid-pricing.jsonjson
{
  "pricing": {
    "pricing_model": "hybrid",
    "base_price":   { "units": 100, "currency": "USD" },
    "unit_price":   { "units": 5,   "currency": "USD" },
    "billing_unit": "1k_tokens"
  }
}

For flat, the amount lives in base_price with no unit_price and no billing_unit. For per_invocation, it lives in unit_price with billing_unit set to "invocation". For per_unit, it lives in unit_price and billing_unit names the scaling dimension (1k_tokens, MB, GB, row). The manifest validator requires base_price for flat, unit_price and billing_unit for per_invocation and per_unit, and both amounts plus billing_unit for hybrid.

Declare pricing in Rust

A native service publishes pricing directly from NativeTool, as the pricing tools do for quote. .per_invocation_price(25, "USD") declares 25 minor units, USD 0.25 a call, and .tool(...) takes the definition together with its handler.

examples/hello-tool/src/lib.rs29-67rust
pub fn build_service(
    public_key_hex: String,
) -> Result<NativeChioService, chio_manifest::ManifestError> {
    NativeChioServiceBuilder::new("srv-hello", public_key_hex)
        .server_name("Hello Tool Server")
        .server_version("0.1.0")
        .server_description("A tiny native Chio service that exposes a tool, resource, prompt, and priced manifest")
        .tool(
            NativeTool::new(
                "greet",
                "Returns a personalized greeting",
                serde_json::json!({
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": "The name to greet"
                        }
                    },
                    "required": ["name"]
                }),
            )
            .output_schema(serde_json::json!({
                "type": "object",
                "properties": {
                    "greeting": { "type": "string" }
                }
            }))
            .read_only()
            .per_invocation_price(25, "USD")
            .latency_hint(chio_manifest::LatencyHint::Instant),
            |arguments| {
                let name = greeting_name(&arguments)?;
                Ok(serde_json::json!({
                    "greeting": format!("Hello, {name}! This greeting was served by a native Chio service.")
                }))
            },
        )
        .static_resource(

The other three models use a different builder method:

priced-tools.rsrust
use chio_mcp_adapter::native::NativeTool;

// Price by token count: USD 0.05 per 1k tokens.
let summarize = NativeTool::new("summarize", "Condense input text", schema.clone())
    .per_unit_price(5, "USD", "1k_tokens");

// Fixed USD 1.00 base plus USD 0.05 per MB.
let archive = NativeTool::new("archive", "Compress and store data", schema.clone())
    .hybrid_price(100, 5, "USD", "MB");

// One fixed price with no parameter sensitivity.
let ping = NativeTool::new("ping", "Liveness probe", schema)
    .flat_price(1, "USD");

NativeChioServiceBuilder::build() validates the assembled manifest and returns the service unsigned. The public-key hex passed to NativeChioServiceBuilder::new(server_id, public_key) is a manifest field, and sign_manifest refuses when it differs from the signing key. After build(), call chio_manifest::sign_manifest(service.manifest(), &keypair), which validates again and signs the whole manifest. The signature covers the pricing block, so a price change means re-signing, and callers can check the signature before they trust a price.

Flat and per-invocation shapes

flat_price and per_invocation_price can advertise the same amount, yet they emit different manifest shapes. Use flat_price when the manifest should record that the tool has no parameter sensitivity, and per_invocation_price for a fixed per-call unit. A caller branches on pricing_model to read the amount from the right field.

pricing-shapes.rsrust
// flat_price: pricing_model Flat, amount in base_price, no billing_unit.
tool.flat_price(25, "USD");
// pricing = { pricing_model: "flat", base_price: { units: 25, currency: "USD" } }

// per_invocation_price: pricing_model PerInvocation, amount in unit_price,
// billing_unit "invocation".
tool.per_invocation_price(25, "USD");
// pricing = { pricing_model: "per_invocation",
//             unit_price: { units: 25, currency: "USD" },
//             billing_unit: "invocation" }

Other authoring languages

A server in any language can publish the same signed manifest structure if it produces the canonical bytes and signature the manifest verifier expects; the Rust builder is a convenience. The TypeScript, Python, and Go SDKs parse manifests and check their invariants.

Read an offer as a caller

Before an agent, or an authority acting for it, delegates a capability for a tool, it fetches the signed manifest during discovery and reads the tool's pricing block. Once parsed, the block is plain data.

plan-delegation.tsplan_delegation.py
// signedManifest is what verifySignedManifest accepted. Pricing hangs off each
// tool: a top-level "pricing" key fails the structure check.
const tool = signedManifest.manifest.tools.find((entry) => entry.name === "search");
const pricing = tool?.pricing;

if (pricing?.pricing_model === "per_invocation" && pricing.unit_price) {
  const { units, currency } = pricing.unit_price;
  const plannedCalls = 40;
  const margin = 200; // minor units
  const totalBudget = units * plannedCalls + margin;

  console.log(`Quoted at ${units} ${currency} per call.`);
  console.log(`Planning budget of ${totalBudget} ${currency} for ${plannedCalls} calls.`);
}

MANIFEST_FIELD_SET excludes pricing and TOOL_FIELD_SET includes it, so a manifest with a top-level pricing key returns structure_valid: false. Planning a budget from the offer leaves issuance as a separate, explicit host step.

Plan a budget from a quote

ModelPer-call allowanceTotal budget
flat
Flat quote
flat * allowed_invocations + margin
per_invocation
Quoted unit price
unit_price * allowed_invocations + margin
per_unit
Conservative estimate from the expected unit ceiling
per_call_estimate * allowed_invocations + margin
hybrid
base + unit * expected_units_per_call
per_call * allowed_invocations + margin

For a search tool on web at per_invocation 25 USD units and an expected 40 calls:

Budget arithmetic in USD minor unitstext
expected_total = 40 * 25 = 1000
safety_margin  = 200
grant_total    = 1200
per_call_cap   = 25
grant.rsrust
use chio_core::capability::scope::{MonetaryAmount, Operation, ToolGrant};

let grant = ToolGrant {
    server_id: "web".to_string(),
    tool_name: "search".to_string(),
    operations: vec![Operation::Invoke],
    constraints: vec![],
    max_invocations: None,
    max_cost_per_invocation: Some(MonetaryAmount {
        units: 25,
        currency: "USD".to_string(),
    }),
    max_total_cost: Some(MonetaryAmount {
        units: 1200,
        currency: "USD".to_string(),
    }),
    dpop_required: Some(true),
};

When a tool may exceed its quote, set the ceiling from the worst case you will authorize. dpop_required binds the spend-bearing grant to its subject.

Set the budget at issuance

The monetary fields belong to the signed capability grant. Populate them in issuance or authority code, alongside the guard policy and host configuration:

  1. Publish. The tool server puts its pricing in the signed manifest.
  2. Read. Operator or authority code reads the verified manifest.
  3. Issue. The issuer signs a budgeted capability explicitly.

A price change therefore never widens a grant: an agent holds the budget its issuer signed.

Admission and cost reconciliation

For an ordinary monetary grant, admission reserves max_cost_per_invocation against the available total. The durable authority coordinates that hold with the operation and any invocation quota, and execution starts only after admission and payment authorization succeed. In the 15-unit run:

  1. Issue. The issuer permits 15 per call and 50 in total.
  2. Reserve. The host reserves and authorizes 15 before invoking the discount service.
  3. Report. The connection returns the discounted price and reports 15 USD units.
  4. Capture. The local-credit adapter captures 15, and the kernel records the charge and the available balance.

A connection that reports no cost is charged the authorized ceiling on this durable path. A report above the authorization fails during finalization, after the service has run.

Governed quotes

To bind a pre-execution quote into a governed request, put a typed governed_intent.metered_billing block on the intent:

  • settlement_mode: must_prepay, hold_capture, or allow_then_settle.
  • quote.quote_id: a stable identifier from the metering or billing system.
  • quote.provider: the billing authority that issued the quote.
  • quote.billing_unit: the unit name, such as invocation or 1k_tokens.
  • quote.quoted_units and quote.quoted_cost: the estimated units and amount.
  • quote.issued_at and expires_at: the quote's validity window.
  • max_billed_units: an explicit upper bound for the governed request.

The quote and settlement configuration live on the governed intent, the charged cost lives in the receipt's metadata.financial, and post-execution usage reconciles through a mutable trust-control sidecar keyed by receipt_id. The signed receipt stays immutable, so a report can show quote, charge, and external metering side by side.

Cross-currency transactions

A tool may quote in one currency while the agent's budget is in another. A configured oracle resolves the rate when the payment path converts the reported cost, and the receipt's metadata.financial.oracle_evidence records an OracleConversionEvidence: the rate as an integer rate_numerator / rate_denominator pair, with the feed source, address, and timestamp. The Settlement guide covers the same record from the resolver side. This fragment is illustrative, with the feed address elided.

Illustrative financial fragment with oracle evidencejson
{
  "grant_index":        0,
  "cost_charged":       250,
  "currency":           "USD",
  "budget_remaining":   950,
  "budget_total":       1200,
  "delegation_depth":   0,
  "settlement_status":  "settled",
  "oracle_evidence": {
    "schema":               "chio.oracle-conversion-evidence.v1",
    "base":                 "JPY",
    "quote":                "USD",
    "authority":            "chio_link_runtime_v1",
    "rate_numerator":       1096,
    "rate_denominator":     100000,
    "source":               "chainlink",
    "feed_address":         "0x...",
    "updated_at":           1700000125,
    "max_age_seconds":      3600,
    "cache_age_seconds":    12,
    "converted_cost_units": 250,
    "original_cost_units":  228,
    "original_currency":    "JPY",
    "grant_currency":       "USD"
  }
}

The validator requires the authority label chio_link_runtime_v1; configured degraded resolution can append :degraded to the source. Conversion uses each currency's minor-unit scale and rounds up: USD, EUR, and GBP use 100 minor units, JPY uses 1, and USDC and USDT use 1,000,000.

Reconstruct the illustrated conversiontext
228 × 1096 × 100 / (1 × 100000) = 249.888
round up → 250 USD minor units

Keep the grant and pricing currencies identical unless the capability needs a conversion. With no oracle configured, a cross-currency call fails closed.

Change a published offer

A tool's manifest is signed, so a price change takes three steps:

  1. Edit. Update the pricing fields on the ToolDefinition.
  2. Sign. Re-sign the manifest with the server's key.
  3. Publish. Publish the new manifest version with an updated version and published_at.

Callers keep the offer they fetched until they refresh it. The authority inspects the new offer and decides whether to issue a new grant. Configure the host to reject an unaffordable offer before invoking it.

FX evidence that does not reconcile

A receipt whose converted_cost_units differs from the recomputed value fails conversion-evidence validation, whatever its signature. Recompute the conversion with both minor-unit scales, then check the oracle policy and evidence. Where the error surfaces depends on which integration runs the validator.

A manifest that will not sign

sign_manifest validates before signing, so a pricing block missing a required field never reaches a caller: Flat needs base_price, PerInvocation and PerUnit need unit_price and billing_unit, and Hybrid needs all three. The builder methods set these; a hand-written manifest is where they go missing.

Operating rules

  • Treat manifest pricing as operator input. Receipts and the metered sidecar record billed usage.
  • Coordinate spending through the deployment's durable budget authority; the overrun bound depends on that authority and its consistency model.
  • Require DPoP on spend-bearing grants so quoted authority stays bound to its subject.
  • Payment rails plug in as optional modules. Holds and caps stop spend before dispatch, and the receipt records the rail outcome the kernel observes.