Chio/Docs
LOGIN · JOIN

x402 Payments

Bound an agent's x402 payments with its grant and budget, and keep each payment on the signed receipt of its call.

Guide chio@5f63e6e

On this page

An agent that buys HTTP resources over x402 spends only what its grant allows. The kernel checks the grant and holds the budget before the payment is authorized, then signs one receipt that carries both the decision and the money outcome.

x402 plugs in as an optional payment module behind the kernel's PaymentAdapter port. You run a governed prepayment locally first, with no key, chain, or facilitator, then point the adapter at the facilitator you operate.

Start

The kernel holds the budget before x402 moves money

The kernel decides whether the agent may spend, calls the payment adapter for one external authorization, and signs the receipt. The agent then sends the paid request to the x402 endpoint itself.

Rails outside the kernelChoose a moment.

Holds and caps prevent spend before dispatch

The kernel checks the grant and records the hold against its budget and caps, then asks the configured adapter to authorize up to the quoted amount. A payment on the rail never authorizes a call by itself: the grant and the hold do. PaymentAdapter lifecycle.

Payment rails as optional modules attached to the kernel through the PaymentAdapter lifecycle. Holds and caps prevent spend before dispatch; what the rail does is observed and recorded. The modules are off in default builds; custody and the facilitator service are left out.Settlement rails

Before dispatch, the kernel checks the grant, records the hold against the budget and caps, and calls authorize on the adapter for up to the quoted amount. The grant and the hold admit the call; a payment on the rail never authorizes one by itself. When a governed intent carries MustPrepay and no adapter is configured, the kernel refuses before dispatch.

After the call, the rail reports a RailSettlementStatus that folds into the receipt's SettlementStatus (NotApplicable, Pending, Settled, or Failed) beside the payment_reference. The kernel observes and records that outcome. The x402 HTTP exchange runs between the agent and the endpoint, and no Chio component terminates it.

Read more in Settlement rails.

The grant names the paying tool and caps what it spends

An agent pays only through a tool its capability grants. Each grant names a server_id, a tool_name, and a list of operations (invoke for a payment call), with the optional ceilings max_invocations, max_cost_per_invocation, and max_total_cost. The tool grant is the whole scope, with no dotted permission namespace beside it.

  • Budgets. The kernel evaluates the upcoming payment against per-call and cumulative budgets, and refuses a call over a limit before any payment is sent.
  • Revocation. Once a revocation is visible at the enforcement point, the kernel refuses new governed payment dispatches, even while the agent still holds the token. Revocation does not reverse a payment an external service already completed.
  • Cross-currency pricing. When the settlement currency differs from the budget currency, the kernel prices the call through Chainlink or Pyth, applies the configured FX margin, and attaches the oracle evidence to the receipt.
  • One record. The receipt that records the verdict carries the money outcome on metadata.financial, so a receipt log ties each payment to the capability that permitted it without a join.

Route payment traffic through the kernel, and set host egress controls so the agent reaches the facilitator only through that path.

Read more in Capabilities and Budgets and metering.

1. Run a governed prepayment with no key or chain

Install the chio CLI, then put one governed call that mandates prepayment (MustPrepay) through an ephemeral kernel. The command wires the deterministic no-broadcast sim adapter and writes the signed receipt to --out.

Terminalbash
curl -fsSL https://chio.computer/install.sh | sh
A governed prepayment through the sim adaptertranscript
$ chio mcp governed-sim \
  --payment-adapter sim \
  --governed-mustprepay \
  --out ./receipt.json
exit 0

You should see the command exit 0 with no output and one receipt in receipt.json. CI runs this same no-key call through examples/governed-x402-sim/smoke.sh.

2. Read the payment on the call's receipt

Print the decision and the financial metadata from the receipt.

The payment on the receipttranscript
$ jq '{id, decision, receipt_kind, financial: .metadata.financial}' ./receipt.json
{
  "id": "05cb8d13de28bf96c7220d74f45c1563f4d9f36fc8b0ac672f8d7c7ff9337a25",
  "decision": {
    "verdict": "allow"
  },
  "receipt_kind": "mediated_decision",
  "financial": {
    "budget_remaining": 900,
    "budget_total": 1000,
    "cost_breakdown": {
      "payment": {
        "adapter_metadata": {
          "adapter": "sim",
          "commerce": null,
          "governed": {
            "approvalTokenId": "governed-sim-approval-1",
            "intentHash": "1ee415c06b0bb092d5c3fec07097fb41d192ee06d43649f404b543d31dd22f18",
            "intentId": "governed-sim-intent-1",
            "purpose": "no-key CI smoke",
            "serverId": "governed-sim-srv",
            "toolName": "compute"
          },
          "mode": "prepaid_no_broadcast"
        },
        "authorization_id": "sim-268afdc9129342029708eb795c87da08",
        "preauthorized_units": 100,
        "recorded_units": 100
      }
    },
    "cost_charged": 100,
    "currency": "USD",
    "delegation_depth": 0,
    "grant_index": 0,
    "payment_reference": "sim-268afdc9129342029708eb795c87da08",
    "root_budget_holder": "66c2cd97052842de29bda51688fcb33c87f6a74bd90a6c4546fe03b7479271b5",
    "settlement_status": "settled"
  }
}
exit 0allow

You should see allow and a settlement_status of settled. The payment is a branch of the receipt's cost breakdown, metadata.financial.cost_breakdown.payment, carrying the adapter that moved the money, the authorization_id it returned, and the governed intent the approval was bound to. Beside it sit cost_charged, budget_remaining, and settlement_status.

The sim adapter is deterministic, so authorization_id repeats across runs. The kernel keypair is ephemeral, so id, intentHash, and root_budget_holder change on every run.

3. Remove the adapter, and the kernel refuses before dispatch

A call that mandates prepayment never falls back to running unpaid. Run the identical intent with --payment-adapter none.

The same intent with no payment adaptertranscript
$ chio mcp governed-sim \
  --payment-adapter none \
  --governed-mustprepay \
  --out ./deny.json
error[CHIO-ECONOMY-PREPAYMENT-REQUIRED]: the governed intent requires prepayment (settlement_mode=MustPrepay) and no payment adapter is configured
  help: pass --payment-adapter sim for a local run, or --payment-adapter http-x402 with a facilitator URL
  code: urn:chio:error:economy:prepayment-required
  docs: https://chio.computer/docs/reference/errors#error-economy-prepayment-required
exit 1

You should see exit status 1 and the kernel's reason: the intent mandates prepayment and no payment adapter is configured. The refusal still writes --out, and the record is a signed receipt.

The refusal on its receipttranscript
$ jq '{id, decision, financial: .metadata.financial}' ./deny.json
{
  "id": "fa761f9eb9d61ede8b6a3e320471e50c9d1eb4e4e234ede0ffd0079ae291e67d",
  "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": "d982de219319344a1f643e7fa1eda75e91cb47f1324ae9b4e45a7c1cf0eb23db",
    "settlement_status": "not_applicable"
  }
}
exit 0deny

The receipt records what the call would have cost on attempted_cost and 0 on cost_charged, and budget_remaining still equals budget_total. It carries no cost_breakdown, because no adapter ran, and settlement_status reads not_applicable.

4. Point the adapter at your x402 facilitator

Select the http-x402 mode at kernel-host configuration, with the facilitator's base_url and an optional bearer token. You supply the facilitator service, its custody, and the payment account.

crates/kernel/chio-kernel/src/payment.rs108-113rust
pub struct X402PaymentAdapter {
    base_url: String,
    authorize_path: String,
    bearer_token: Option<String>,
    http: ureq::Agent,
}

X402PaymentAdapter makes one remote call, the authorization, posted to the facilitator's /authorize endpoint. x402 completes final prepayment there (PrepaidFinal), so capture, release, and refund synthesize their results locally as prepaid bookkeeping and send no HTTP. A lower measured cost triggers no remote refund by itself.

The http-x402 mode is a payment-rail module that runs outside the kernel. With it enabled, the receipt keeps the shape you read in step 2, with the facilitator's authorization in place of the simulated one.

Each adapter mode makes a different number of remote calls

The host selects one adapter at configuration. Simulation is the default; the two HTTP modes delegate money movement to a facilitator you configure.

ModeAdapterRemote calls per paymentSettlement
simSimPaymentAdapterNone; no key or broadcastPrepaid, deterministic
http-x402X402PaymentAdapterOne: authorizePrepaidFinal, per-request stablecoin
http-acpAcpPaymentAdapterOne per operationReversibleHold, shared payment token

Choose http-acp when an external facilitator must confirm every transition: its adapter carries a path for authorize, capture, release, refund, and settlement state.

x402 protocol and interop reference

HTTP 402 exchange

The server uses HTTP 402 to request payment. In x402 V2, PAYMENT-REQUIRED carries the requirements, PAYMENT-SIGNATURE carries the retry's payment payload, and PAYMENT-RESPONSE carries the settlement response. The values are Base64-encoded JSON. The exchange below is illustrative, with symbolic payloads; the x402 HTTP documentation defines their schemas.

Illustrative x402 V2 exchange · payloads omittedhttp
GET /v1/quote?sku=sku-301 HTTP/1.1
Host: pricing.larkfield.example

HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: <Base64 PaymentRequired JSON>

GET /v1/quote?sku=sku-301 HTTP/1.1
Host: pricing.larkfield.example
PAYMENT-SIGNATURE: <Base64 PaymentPayload JSON>

HTTP/1.1 200 OK
PAYMENT-RESPONSE: <Base64 SettlementResponse JSON>
Content-Type: application/json

{ "sku": "sku-301", "price": "140.00", "currency": "USD" }

V1 used X-PAYMENT and X-PAYMENT-RESPONSE, with x402Version: 1; V2 uses x402Version: 2. The protocol migration guide covers the change. These public wire versions are distinct from the source-version literal that Chio's offline claim validator accepts.

Inbound payment claims

For the receiving direction, Chio ships an offline claim validator; it parses no payment headers and builds no 402 Payment Required response. It checks an external.x402.payment.v1 subject inside a proof envelope for a matching source version, four SHA-256 digests, a transaction passport reference that matches the envelope, an order id, network, asset, a non-zero amount, a status of authorized or settled, and a payment receipt reference bound into the envelope.

The validator accepts the source-version literal 0.5. A bound x402 claim whose source_protocol_version is any other value is rejected as an unsupported source version, and a claim carrying a refunded status is a hard claim failure.

Governed interoperability API

x402 has no crate of its own. It is one of four bounded payment-interop capabilities in chio-settle, each built on governed dispatch and settlement truth, with signed receipts staying the record:

  • x402 payment-requirement projection. build_x402_payment_requirements turns one governed settlement dispatch into an x402 payment-requirement object, bound to a facilitator URL, a resource identifier, and an explicit accepted-token list.
  • EIP-3009 gasless transfer. prepare_transfer_with_authorization prepares one transferWithAuthorization digest for review, and an Eip3009NonceStore keeps each nonce single-use.
  • Circle nanopayment evaluation. evaluate_circle_nanopayment evaluates one nanopayment candidate, only when operator-managed custody is declared explicitly.
  • ERC-4337 paymaster compatibility. prepare_paymaster_compatibility evaluates one paymaster compatibility record, bounded by explicit reimbursement ceilings.

Each function projects, prepares, or evaluates one record for an external system. The layer runs no payment-facilitator marketplace, takes no implicit custody handoff, and offers no universal gas sponsorship. Off-protocol facilitator state never changes a signed Chio receipt.

Where the payment code lives

The caller never assembles a payment envelope. Authorization, budget reservation, adapter dispatch, and receipt signing happen inside the kernel, so the caller uses the same capability, intent, and verdict as any other tool call. The payment-specific parts live in the four chio-settle::payments functions above and in chio-kernel::payment::X402PaymentAdapter. The ACP adapter carries one path per operation:

crates/kernel/chio-kernel/src/payment.rs121-130rust
pub struct AcpPaymentAdapter {
    base_url: String,
    authorize_path: String,
    capture_path: String,
    release_path: String,
    refund_path: String,
    settlement_state_path: String,
    bearer_token: Option<String>,
    http: ureq::Agent,
}

Next: settle the payments you recorded