Chio/Docs
LOGIN · JOIN

Settlement Rails

Connect a payment rail behind the kernel, choose held or prepaid charging, and read the rail's outcome on the receipt.

Concept chio@5f63e6e

On this page

An agent can pay for a call over x402, ACP, a bank transfer, or an internal ledger, and the grant's budget bounds the spend whichever rail moves the money.

Before dispatch, the kernel holds the call's worst case against the grant and calls authorize on the configured payment adapter. The rail executes the payment, and its outcome returns as a settlement status on the signed receipt. Rails are modules outside the kernel.

Every rail plugs in behind one adapter, outside the kernel

The kernel speaks to a rail through the PaymentAdapter trait: authorize before the tool runs, capture or release after it, and refund for a later reversal.

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.

authorize reserves up to the quoted amount on the rail. After the call, metering produces the observed cost: a held authorization captures it, or releases cleanly when it is zero. The grant and the hold admit the call. A payment on the rail never authorizes one by itself, and a MustPrepay mandate with no adapter configured is refused before dispatch.

Each rail reports its own RailSettlementStatus: Authorized, Captured, Settled, Pending, Failed, Released, or Refunded. These fold into the four values of the receipt's SettlementStatus, which records whether money is moving, has moved, or has stopped; the rail keeps intermediate detail in its metadata.

Read more in Budgets and metering, which covers the hold the kernel records before dispatch.

Each adapter declares whether a charge is held or prepaid

The operator selects one adapter at kernel-host configuration with CHIO_PAYMENT_ADAPTER=sim|http-x402|http-acp. Simulation is the default, an unrecognized value is rejected as a configuration error, and the two HTTP adapters delegate money movement to a facilitator at CHIO_PAYMENT_BASE_URL.

ModeAdapterAuthorizationAfter the call
simSimPaymentAdapterPrepaid, deterministicNo key, network, or broadcast
http-x402X402PaymentAdapterPrepaidFinal, per-request stablecoinSecond capture skipped
http-acpAcpPaymentAdapterReversibleHold, shared payment tokenCapture, release, or refund over HTTP

The kernel checks that the state authorize returns, Held or PrepaidFinal, matches the adapter's declared rail mode, and a refused authorization stops the paid path. For a metered call quoted at 100 units and observed at 85, ACP authorizes the 100-unit ceiling, then captures 85 through its terminal endpoint and validates each response against the operation it bound. Budget reconciliation restores 15 units of capacity, a fact separate from the payment result.

x402 completes final prepayment at authorization, so the kernel skips a second capture, and a lower measured cost triggers no remote refund by itself. An x402 result is not settled by default: a facilitator response that omits the field reads as unsettled, and the adapter refuses to complete the prepayment on it. Both HTTP modes take a facilitator base_url and an optional bearer token; the operator supplies the facilitator service, its custody, and the payment account. A PaymentResult carries transaction_id, settlement_status, and metadata, and a pending result is a recorded state short of final payment.

Read more in x402 payments, and in the 100, 85, 15 walkthrough for the exact authorization request and terminal result.

The intent sets the payment posture before the call runs

A metered call carries one MeteredSettlementMode, chosen on the capability or the governed intent and recorded on the receipt.

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,
}
  1. Must prepay. must_prepay captures the quoted amount before execution. Prepaid-final skips capture, and a lower observed cost refunds nothing automatically. When final prepayment cannot be confirmed, or no adapter is configured, the kernel refuses the call before execution and it never runs unpaid.
  2. Hold and capture. hold_capture authorizes the quote as a hold, captures a positive actual cost at or below that ceiling, and releases a zero-cost hold. The original authorization and its terminal outcome resolve under the provider's contract.
  3. Allow then settle. allow_then_settle permits deferred settlement, and configured admission can still reserve budget and authorize payment. The host arranges and reconciles the deferred payment, keeping pending and failed outcomes distinct from final settlement.

A public agent with no track record fits must_prepay. A known high-volume partner can run on hold_capture, and an internal team rail through a ledger can use allow_then_settle, because the counterparty is your own treasury. The rail kind and the settlement mode are separate fields, so choose an adapter and admission path that support the mode.

Read more in Tool pricing, which computes the quoted cost before the rail sees the authorization.

A governed payment carries the intent and approval that allowed it

Chio initiates outbound payments as well as recording inbound ones. A bound governed intent, with approval evidence when policy requires it, drives the same adapter through authorize before the tool runs and capture or release after.

Before dispatch, the kernel derives a GovernedPaymentContext from the intent's binding hash and the approval token's id, or the threshold proposal standing in for it, and hands it to authorize beside an optional CommercePaymentContext. After execution it stamps payment_reference and settlement_status into the receipt's FinancialReceiptMetadata, so the settlement binds back to the authority that allowed it.

The governed payment context in Rust

The kernel builds the context in authorize_payment_if_needed, from request.governed_intent through binding_hash() and from request.approval_token.id.

crates/kernel/chio-kernel/src/kernel/validation.rs2560-2589rust
let governed = request
    .governed_intent
    .as_ref()
    .map(|intent| {
        intent
            .binding_hash()
            .map(|intent_hash| GovernedPaymentContext {
                intent_id: intent.id.clone(),
                intent_hash,
                purpose: intent.purpose.clone(),
                server_id: intent.server_id.clone(),
                tool_name: intent.tool_name.clone(),
                approval_token_id: request
                    .approval_token
                    .as_ref()
                    .map(|token| token.id.clone())
                    .or_else(|| {
                        request
                            .threshold_approval_proposal
                            .as_ref()
                            .map(|proposal| proposal.body.proposal_id.clone())
                    }),
            })
            .map_err(|error| {
                PaymentError::RailError(format!(
                    "failed to hash governed intent for payment authorization: {error}"
                ))
            })
    })
    .transpose()?;

Two shapes carry the authorization. The HTTP shape posts to a facilitator's /authorize endpoint, for an x402 per-request stablecoin payment or an ACP shared payment token. The EIP-3009 shape calls prepare_transfer_with_authorization, which computes the EIP-712 transferWithAuthorization digest and returns a PreparedTransferWithAuthorization. An ApprovalBinding derives that digest's chain, payee, amount, and token from a verified GovernedApprovalToken, which folds them into one governed_intent_hash, and Eip3009NonceStore keeps each nonce single-use.

No step in this path holds funds. Chio computes and authorizes, a wallet signs, and an external facilitator broadcasts; the EIP-3009 lane returns a digest and leaves signing and submission to the external executor. For these off-chain rails the configuration offers simulation or facilitator delegation, with no operator-managed custody or broadcast mode.

Read more in Authoritative Spend for the authority and outcome evidence a payment requires, and in Verify Commerce Evidence, whose fixture checks recorded projections.

Whichever rail moves the money, the receipt records the same fields

The host observes provider notifications and appends outcome evidence beside the receipt, which keeps the fields it was signed with.

  • payment_reference: the rail's stable identifier, such as a card charge id, an ACH trace number, or a transaction hash, for downstream reconciliation.
  • settlement_status: NotApplicable, Pending, Settled, or Failed.
  • metered_billing: the quoted units, the observed units, and the MeteredSettlementMode.

A CapitalExecutionRail instruction is a separate execution record linked to the receipt; tool-call receipts do not carry it as a field. Rail settlement outcomes are observed and recorded, while the holds and caps on the grant prevent spend before dispatch.

A company can authorize an agent to buy work without giving the provider control of its budget, and the provider can retain the receivable, sell it to a factor, or agree to channel settlement. Four records answer the four questions about one paid call:

  1. May this call incur a charge? The capability, admission, budget hold, and execution nonce, in Authoritative Spend.
  2. Who is owed? An immutable obligation and its current authenticated creditor, in Obligations.
  3. How will value move? The selected adapter or contract execution; payment channels and clearing are alternative arrangements.
  4. Can the books close? The original rail result, its financial linkage, and any required finality evidence, in Reconciliation.

Rail and adapter reference

PaymentAdapter trait

The four methods are the whole adapter contract. Each terminal call names the authorization or transaction it completes and a reference that binds it to the original operation.

crates/kernel/chio-kernel/src/payment.rsrust
pub trait PaymentAdapter: Send + Sync {
    /// Authorize or prepay up to `amount_units` before the tool executes.
    fn authorize(
        &self,
        request: &PaymentAuthorizeRequest,
    ) -> Result<PaymentAuthorization, PaymentError>;

    /// Finalize payment for the actual cost after tool execution.
    fn capture(
        &self,
        authorization_id: &str,
        amount_units: u64,
        currency: &str,
        reference: &str,
    ) -> Result<PaymentResult, PaymentError>;

    /// Release an unused authorization hold.
    fn release(
        &self,
        authorization_id: &str,
        reference: &str,
    ) -> Result<PaymentResult, PaymentError>;

    /// Refund a previously executed payment.
    fn refund(
        &self,
        transaction_id: &str,
        amount_units: u64,
        currency: &str,
        reference: &str,
    ) -> Result<PaymentResult, PaymentError>;
}

A PaymentAuthorization holds the rail's authorization id and a typed state. A Held authorization requires a terminal operation; a PrepaidFinal one has already paid the quoted amount. SimPaymentAdapter exercises the prepaid lifecycle without moving funds. X402PaymentAdapter reports terminal results locally after final prepayment, so its local refund result is no evidence of a remote refund. The adapter source shows each request and the validation of terminal operations.

ACP adapter construction

This Rust fragment builds an adapter for a facilitator you operate or have configured, with its URL and credential read from your application's configuration. The default paths are /authorize, /capture, /release, /refund, and /settlement-state. Construction makes no payment.

host.rsrust
use chio_kernel::payment::AcpPaymentAdapter;
use std::time::Duration;

fn configured_adapter(url: &str, token: &str) -> AcpPaymentAdapter {
    AcpPaymentAdapter::new(url)
        .with_bearer_token(token)
        .with_timeout(Duration::from_secs(5))
}

Install it on the kernel before dispatch with set_payment_adapter(Box::new(adapter)), the single integration point for outbound payments in a host and in tests, and pair it with a compatible metered settlement mode. Keep facilitator credentials out of capability and receipt metadata.

Rail kinds

Every CapitalExecutionRail record selects one CapitalExecutionRailKind, plus a rail_id, a custody provider, and optional source and destination account references. The enum is re-exported through the capital_and_execution module and again through chio-core.

crates/economy/chio-credit/src/credit/capital_and_execution/capital_execution.rsrust
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum CapitalExecutionRailKind {
    Manual,
    Api,
    Ach,
    Wire,
    Ledger,
    Sandbox,
    Web3,
}

The seven kinds classify an execution record; an installed adapter or integration executes it. Timing, fees, reversibility, and finality come from the configured provider's contract.

Manual

Records a transfer performed outside the runtime. The operator workflow binds the external transfer reference to the capital instruction and retains the evidence used to mark it complete, alongside its reconciliation record. An invoice or payment confirmation comes from that workflow.

Api

Executes through an adapter for a payment service with defined authorization, capture, release, refund, and observation semantics. The shipped ACP adapter is one such HTTP contract; a processor-specific integration implements and checks its own mapping. Retain the bound request, the response, the transaction id, and the original rail status.

Ach

Classifies a bank-transfer execution. The bank or processor integration owns submission, any batching, provider-status observation, and reconciliation, and its contract sets payment files, collection schedules, and return windows. Retain the provider acknowledgement and the later outcome, linked to the original instruction.

Wire

Classifies an execution submitted through a wire-transfer integration. Retain its external reference and the receiving or provider evidence the integration requires; timing, fees, and recall behavior depend on that service and route.

Ledger

Records an allocation within the operator's own accounting book, with no external transfer. The accounting integration posts the debit and credit and owns reversal; retain the posted entry and its accounting scope. The signed receipt is evidence about the governed call.

Sandbox

Exercises settlement with synthetic outcomes and references and moves no funds. SimPaymentAdapter is a custody-neutral prepaid adapter; use explicit fixtures for the failure and pending cases your host handles, and keep the simulated identity in exported evidence.

Web3

Executes through on-chain settlement bindings. An authorized account submits the prepared transaction, and an observer records execution and finality; retain transaction and block identities, escrow state, and finality evidence. On-Chain Settlement covers the signature and proof release paths, and Chainlink Oracles covers validated prices for currency conversion and the source, freshness, and conversion evidence settlement records.

Rail record fields

The rail attaches to a capital execution instruction, which carries this record.

crates/economy/chio-credit/src/credit/capital_and_execution/capital_execution.rsrust
pub struct CapitalExecutionRail {
    pub kind: CapitalExecutionRailKind,
    pub rail_id: String,
    pub custody_provider_id: String,
    pub source_account_ref: Option<String>,
    pub destination_account_ref: Option<String>,
    pub jurisdiction: Option<String>,
}
  • rail_id stays stable across an operator's deployment, because reconciliation joins on it.
  • custody_provider_id names the entity holding funds in flight: a processor, the operator's treasury bank, or the on-chain escrow contract.
  • source_account_ref and destination_account_ref distinguish sources and destinations when one rail aggregates several.
  • jurisdiction is a metadata hint; any policy that interprets it lives in your application.

Provider questions

Settle each with the provider before selecting an integration:

  • Authorization. Does the operation create a reversible hold or a final prepayment, and how is the exact request bound?
  • Timing and fees. Which acknowledgement, confirmation, fees, and expiry conditions apply to this account and route?
  • Uncertain outcomes. How do you observe the original operation after a lost response, and which identity makes a retry safe?
  • Reversal. Which release, refund, return, or dispute procedure applies, and where is its confirmed result?
  • Finality. What evidence establishes completion, and how are later reversals or chain reorganizations represented?

Two-rail operator example

Your pricing service serves casual callers holding a one-day grant, who are low trust, high volume, and small ticket, and contracted buyers such as an outside procurement company and the customer tenants Harbor Goods and Ostrander, who are high trust, low volume, and large ticket. It enables two rails and binds them per counterparty.

Api rail for retail calls

The record names a processor-backed Api rail for a custom integration. The configured adapter's rail mode decides whether a paid call uses a reversible hold or final prepayment.

rails.rsrust
use chio_core::credit::{
    CapitalExecutionRail, CapitalExecutionRailKind,
};

let retail_rail = CapitalExecutionRail {
    kind: CapitalExecutionRailKind::Api,
    rail_id: "card-processor-us".to_string(),
    custody_provider_id: "processor.example".to_string(),
    source_account_ref: Some("payer:agent-card".to_string()),
    destination_account_ref: Some("acct:platform-revenue".to_string()),
    jurisdiction: Some("US".to_string()),
};

Chio ships X402PaymentAdapter, AcpPaymentAdapter, and SimPaymentAdapter. A custom processor adapter implements the PaymentAdapter contract and binds terminal responses to the original operation. Exercise authorization, zero-charge release, positive capture, pending observation, and refund against that service before enabling paid dispatch.

Ach rail for monthly settlement

The outside buyer chooses a monthly collection arrangement under deferred-settlement policy, with an Ach rail record. Your bank integration runs the collection.

rails.rsrust
let enterprise_rail = CapitalExecutionRail {
    kind: CapitalExecutionRailKind::Ach,
    rail_id: "ach-batch-monthly".to_string(),
    custody_provider_id: "treasury-bank.example".to_string(),
    source_account_ref: Some("acct:operator-enterprise".to_string()),
    destination_account_ref: Some("acct:platform-collections".to_string()),
    jurisdiction: Some("US".to_string()),
};

At period close, select the eligible reconciled obligations, preserve each original identity, submit through the bank integration, and reconcile the resulting transfer. Payment is established by that reconciled transfer, and aggregation keeps per-obligation evidence. Document the configured service's submission identity, status queries, retry behavior, fees, and reversal handling.

Next: verify one payment lifecycle

Run one authorized call through the selected adapter and retain its authorization id, terminal result, and reconciled receipt.