EconomyCost & Settlement
Settlement Rails
Bind each capability to one of seven settlement-rail variants for moving capital from payer to payee.
Use this page when
PaymentAdapter trait. Don't use this if you want on-chain escrow internals (see On-Chain Settlement) or the cycle that closes pending settlements (see Reconciliation).The Seven Rails
The enum is defined in crates/economy/chio-credit/src/credit/capital_and_execution/capital_execution.rs, re-exported through the capital_and_execution module, and again through chio-core. Every CapitalExecutionRail record selects one variant, plus a rail_id, a custody provider, and optional source/destination account references.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum CapitalExecutionRailKind {
Manual,
Api,
Ach,
Wire,
Ledger,
Sandbox,
Web3,
}Manual
Human-initiated movement outside the system. The operator records the receipt, ships an invoice, and reconciles when the customer pays by check, ACH credit, or another agreed method. Chio signs and records the receipt, while the transfer remains out of band.
- Latency. Days to weeks. Bound by accounts-receivable cadence.
- Finality. Recorded on operator confirmation. No automatic dispute window.
- Cost profile. Lowest variable cost; highest staff cost.
- Use when. Counterparty is a billed enterprise, the relationship predates Chio, or the legal contract requires invoice settlement.
Api
Payment processor integrations: Stripe, Adyen, Braintree, Worldpay, and any other off-chain rail with a synchronous authorize/capture API. Settlement runs through the PaymentAdapter trait in crates/kernel/chio-kernel/src/payment.rs (covered below).
- Latency. Seconds for authorize and capture; one to three business days for funds availability.
- Finality. Provisional on capture; final after the processor's chargeback window closes.
- Cost profile. Per-transaction fees plus interchange; higher than ACH or Wire on average ticket.
- Use when. Retail-style flows, card payments, or any rail whose API maps onto authorize/capture/release/refund.
Ach
Bank-to-bank ACH transfers. Batched, slower, and substantially cheaper per transaction than card rails. The chio runtime treats ACH as an async rail: a settlement intent is recorded, the operator triggers the ACH file, and the rail eventually returns a settled-or-failed signal.
- Latency. One to three business days; same-day ACH available on some rails.
- Finality. Settled on bank confirmation; reversible during the NACHA return window (up to 60 days for consumer disputes).
- Cost profile. Cents per transfer; flat fee regardless of amount.
- Use when. Large operators settling on a monthly or weekly cadence, or any flow where unit economics make card processing untenable.
Wire
Wire transfers. Same-day domestic, T+1 international, irrevocable on the receiving bank's confirmation.
- Latency. Hours; same-day for FedWire and SWIFT GPI.
- Finality. Final on receipt. No automatic recall.
- Cost profile. Tens of dollars per wire on each end; international wires add FX spread.
- Use when. High-value, low-volume settlement where same-day finality matters more than per-transaction cost.
Ledger
Internal ledger entries inside a single operator's book. No external capital movement. The receipt records that one subaccount debited and another credited; bank balances do not change. Useful for cost allocation between teams or for transfers between business units that share a treasury.
- Latency. Synchronous; the entry posts as the receipt is signed.
- Finality. Immediate within the operator's book.
- Cost profile. Effectively zero per transaction.
- Use when. Internal chargebacks, departmental cost allocation, or any flow that never crosses an external boundary.
Sandbox
The development and test rail. Settlement is recorded in the receipt log with a synthetic reference; it does not move capital. Use it for integration tests that exercise the production interfaces.
- Latency. Synchronous and configurable.
- Finality. Synthetic; the rail returns whatever lifecycle the test fixture asks for.
- Cost profile. Zero.
- Use when. CI, devnet bring-up, contract qualification, or any flow that must not move funds.
Web3
On-chain settlement through the ChioEscrow family of contracts. The kernel writes a signed dispatch record, the chain locks funds, and a release transaction (dual-signature or merkle proof) settles the escrow against the receipt root. Full coverage is on the On-Chain Settlement page.
- Latency. Sub-second authorize, seconds-to-minutes finality depending on chain.
- Finality. Probabilistic and explicit. Captured by
SettlementFinalityStatus(AwaitingConfirmations, AwaitingDisputeWindow, Finalized, Reorged). - Cost profile. Gas plus token-transfer cost. Cheaper than wire on small amounts; chain choice matters.
- Use when. Counterparty is a smart contract, an autonomous agent, or any actor for whom an on-chain receipt is the canonical record.
PaymentAdapter Lifecycle
For off-chain rails (Api, Ach, Wire) the kernel speaks to each rail through the PaymentAdapter trait. The contract is a four-step lifecycle: pre-authorize a ceiling, run the tool, then capture or release the actual amount, with refund as a post-settlement reversal.
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>;
}Each rail's richer settlement state is reported back through RailSettlementStatus (Authorized, Captured, Settled, Pending, Failed, Released, Refunded) and folds into the canonical SettlementStatus the receipt carries (NotApplicable, Pending, Settled, Failed). The mapping is intentionally lossy: the receipt cares about whether the money is moving, has moved, or has stopped moving; the rail can carry whatever intermediate detail it likes in metadata.
Authorize, Dispatch, Capture
The flow that consumes this trait inside the kernel is:
- Pre-authorize.
authorize()reserves up to the quoted amount on the underlying rail. The returnedPaymentAuthorizationholds the rail's authorization id and a settled flag. Ifsettled = true(e.g. an x402 prepaid response), the authorization already covers the full quoted cost. - Dispatch the tool. The governed call runs. The Chio kernel observes the actual cost via metering and produces a final amount.
- Capture or release. If the observed cost is positive,
capture()finalizes the authorization at the actual amount. If the observed cost is zero (denial, no-op, error),release()unlocks the hold cleanly. Any later reversal goes throughrefund().
Two adapters ship in chio-kernel out of the box: X402PaymentAdapter for per-request stablecoin payments and AcpPaymentAdapter for shared-payment-token holds (Agentic Commerce Protocol). After the initial authorization, both model capture, release, and refund as local state changes while performing one authorization hop.
What ships vs what is simulated
PaymentAdapter that runs without a custody backend is SimPaymentAdapter: custody-neutral, with no HTTP call, key material, or on-chain broadcast. X402PaymentAdapter and AcpPaymentAdapter, the two adapters named above, are rail shapes: they define how an x402 or ACP settlement authorizes, captures, releases, and refunds. Live fund custody on either rail still requires a configured adapter behind that shape; neither ships with one. A CI guard, scripts/check-no-eip3009-broadcast.sh, fails the build on any production EIP-3009 broadcast in the settlement crates, by design. Settlement policy itself (MeteredSettlementMode, the PaymentAdapter trait) is kernel-owned; chio-settle and chio-web3 only execute what an adapter drives.The Governed Outbound Rail
The rest of this page describes payments Chio verifies and records as a receipt settles. This rail runs the other direction: Chio can initiate an outbound payment, not only verify an inbound one. A signed governed intent plus an approval token drive the same PaymentAdapter through authorize before the tool runs, then capture or release after, and the rail-side settlement reference is folded back onto the signed tool-call receipt.
The kernel does this in authorize_payment_if_needed (crates/kernel/chio-kernel/src/kernel/validation.rs). It derives the governed context from request.governed_intent (via binding_hash()) and request.approval_token.id, packs it into a GovernedPaymentContext that rides alongside an optional CommercePaymentContext, and calls adapter.authorize(...). After execution it captures the observed cost or releases the hold, then stamps payment_reference and settlement_status into the FinancialReceiptMetadata on the receipt. The governed intent hash and approval token id travel with the authorization, so the settlement binds back to the authority that allowed it.
Two shapes carry an outbound authorization. The first is an HTTP facilitator rail: X402PaymentAdapter and AcpPaymentAdapter POST to a facilitator /authorize endpoint (x402 per-request stablecoin, or ACP-Client shared payment token). The second is an EIP-3009 digest path: prepare_transfer_with_authorization computes the EIP-712 transferWithAuthorization digest and returns a PreparedTransferWithAuthorization and nothing else. That digest is bound through an ApprovalBinding whose chain, payee, amount, and token are derived from a verified GovernedApprovalToken (the token folds them into one governed_intent_hash), with a single-use nonce enforced by Eip3009NonceStore.
// Before the tool runs, the kernel derives the governed context from the
// signed intent and approval token, then asks the configured adapter to
// authorize. No adapter with a prepay mandate present is a denial, not a skip.
let governed = GovernedPaymentContext {
intent_hash: request.governed_intent.binding_hash(),
approval_token_id: request.approval_token.id.clone(),
// intent_id, purpose, server_id, tool_name ..
};
let authorization = adapter.authorize(&PaymentAuthorizeRequest {
governed,
// amount, currency, reference, optional CommercePaymentContext ..
})?;
// After execution: capture the observed cost or release the hold, then fold
// payment_reference + settlement_status into FinancialReceiptMetadata.Custody-Neutral by Construction
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 never submits transferWithAuthorization on chain; the HTTP lane delegates the actual movement to the facilitator it POSTs. The default adapter, SimPaymentAdapter, is deterministic and no-broadcast: no HTTP call, no key material, no funds held. That is the sim path, and it is the one the acceptance lane runs.
Fail-closed: a rail mandate needs an adapter
MeteredSettlementMode::MustPrepay) and no PaymentAdapter is configured, the call is denied, never executed unpaid. The productized gate replaces the older fail-open where authorize_payment_if_needed returned None and let the tool run without settling. Rail mandate present, adapter absent, no execution.Adapter Selection
The operator picks the outbound adapter at kernel-host configuration time. Three modes ship; the safe one is the default.
| Mode | Adapter | Posture |
|---|---|---|
sim | SimPaymentAdapter | Default. Deterministic, no key, no network, no broadcast. |
http-x402 | X402PaymentAdapter | POSTs to a facilitator base_url with an optional bearer token. |
http-acp | AcpPaymentAdapter | ACP-Client facilitator; same base_url plus optional bearer. |
The resolved adapter wires into the kernel through set_payment_adapter, the single integration point that makes outbound initiation available in a shipped host as well as tests. The configuration never exposes an operator-managed-custody or broadcast mode for these off-chain rails: only sim, or facilitator delegation.
Current limits. The shipped path is the deterministic no-key sim adapter, the EIP-3009 lane stays a verify-only prepare projection that emits a digest and never broadcasts, and no rail on this path ever holds funds or key material. The offline, verify-only proof of exactly these projections lives in The Wall Stops Money, and the spend authority that stands behind an outbound intent is covered in Authoritative Spend.
Settlement Modes
Each metered call selects one of three settlement modes. The enum lives in crates/core/chio-core-types/src/capability/governance.rs and rides on the receipt as MeteredSettlementMode:
#[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,
}| Mode | Pre-execution | Post-execution | Failure mode |
|---|---|---|---|
must_prepay | Quoted amount captured up front | Refund delta if observed cost lower | Deny on insufficient funds |
hold_capture | Quoted amount authorized as a hold | Capture observed amount; release remainder | Hold expires; release returns funds |
allow_then_settle | No financial commitment taken | Authorize + capture observed amount after | Receipt records pending then failed |
The mode is chosen on the capability or governed intent. A zero-trust public agent is best with must_prepay; a known high-volume integration partner can run on hold_capture; an internal team rail wired through Ledger may opt for allow_then_settle because the counterparty is your own treasury.
Choosing a Rail
Three axes drive the choice: latency, cost, and counterparty trust. The matrix below maps the seven rails to those axes. Operators can enable multiple rails and select one per-capability or per-counterparty).
| Rail | Latency | Per-tx cost | Trust requirement | Reversibility |
|---|---|---|---|---|
Manual | Days to weeks | Low (high staff cost) | High (relationship-based) | Manual |
Api | Seconds to days | Medium-high | Low (processor-mediated) | Chargeback window |
Ach | 1 to 3 days | Cents per transfer | Medium (bank-mediated) | NACHA return window |
Wire | Same-day | $15 to $50 each leg | Low (bank-mediated) | None (irrevocable) |
Ledger | Synchronous | ~0 | Single operator | Reversal entry |
Sandbox | Configurable | 0 | N/A (test only) | N/A |
Web3 | Seconds to minutes | Gas + token transfer | Trust-minimized (escrow + bond) | Refund / merkle dispute |
Rail and mode are orthogonal
Api rail can run in any of the three modes. A Web3 escrow defaults to hold_capture because the contract literally locks funds during execution, but that is convention, not a constraint of the enum.Worked Example: Two-Rail Operator
A tool platform serves both casual API users (low-trust, high-volume, small ticket) and a small set of large enterprise operators (high-trust, low-volume, large ticket). The right move is to enable two rails and bind them per-counterparty.
Api Rail for Retail Flow
For the casual users, configure an Api rail backed by Stripe. Capabilities for these agents bind settlement_mode = must_prepay so the kernel obtains a Stripe authorization before any tool runs:
use chio_core::credit::{
CapitalExecutionRail, CapitalExecutionRailKind,
};
let retail_rail = CapitalExecutionRail {
kind: CapitalExecutionRailKind::Api,
rail_id: "stripe-prod-us".to_string(),
custody_provider_id: "stripe.com".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 does not ship a Stripe adapter: the two built-in adapters are X402PaymentAdapter and AcpPaymentAdapter. For an Api rail like this one, you write your own adapter against the PaymentAdapter trait. A Stripe implementation maps cleanly onto the four methods: authorize creates a PaymentIntent with capture_method=manual; capture calls capture() on the intent; release calls cancel(); refund creates a Refund. The same trait covers card payments, bank-debit through Stripe ACH, and direct transfers.
Ach Rail for Monthly Settlement
For the enterprise operators, configure an Ach rail and bind their capabilities to settlement_mode = allow_then_settle. Each tool call accrues against an open exposure on the operator's account; the rail dispatches a single ACH file at month close to settle the cumulative balance.
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-acme".to_string()),
destination_account_ref: Some("acct:platform-collections".to_string()),
jurisdiction: Some("US".to_string()),
};At month close, the platform aggregates all reconciled receipts where the rail kind is Ach and the rail id matches, computes a single net amount per operator, and ships one ACH debit per operator. The receipts already carry the truth; the ACH file just moves the money.
Reversibility cost
Configuration Shape
The rail attaches to a capital execution instruction. The full instruction has 24 fields; the rail uses six:
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 must be stable across an operator's deployment because reconciliation joins on it. custody_provider_id names the entity holding the funds in flight (Stripe, the operator treasury bank, the on-chain escrow contract). The optional account refs are useful when one rail aggregates multiple sources or destinations; the jurisdiction hint is consumed by compliance policy when an operator scopes rails to specific legal regions.
What Each Receipt Records
Whichever rail moves the money, the receipt records the same canonical fields:
payment_reference: stable rail-side identifier (Stripe charge id, ACH trace number, on-chain tx hash) for downstream reconciliation.settlement_status: one of NotApplicable, Pending, Settled, Failed.metered_billing: the quoted units, observed units, andMeteredSettlementMode.- The
CapitalExecutionRailinstruction record, when an instruction has been emitted.
The reconciliation cycle (covered on the Reconciliation & Watchdog page) reads these fields, joins against the rail's native notifications, and flips status from Pending to Settled or Failed.
See Also
- On-Chain Settlement covers the Web3 rail in depth.
- Reconciliation & Watchdog covers the cycle that closes out each rail's pending settlements.
- Tool Pricing covers how quoted cost is computed before the rail ever sees the authorization.
- Settlement guide walks through wiring a rail.
- x402 and Chainlink are integration-level views of the prepaid HTTP and cross-chain rails.