BuildEconomics
Settlement
Settlement transfers payment from the caller to the provider for a tool invocation, using payment adapters or on-chain escrow.
Prerequisites
- A rail to settle over. Off-chain rails need a
PaymentAdapterimplementation; the Web3 rail needs a funded chain configuration and the deployed contracts. - A governed intent carrying a
settlement_mode. A call with no settlement intent settles nothing, whatever adapter is wired. - A receipt store, because settlement state is read off signed receipts rather than out of adapter memory.
- Every amount here is in minor currency units: 100 units is one US dollar (
crates/core/chio-core-types/src/capability/scope.rs:80-91). - To reproduce this page's captures you need nothing but the CLI:
chio mcp governed-simbuilds an ephemeral kernel and a deterministic simulated adapter.
Settlement rails
A settlement rail is the mechanism used to move capital between counterparties. Each rail is represented by a CapitalExecutionRailKind variant in the chio kernel:
| Rail | Description |
|---|---|
Manual | Human-initiated transfers outside the system (invoices, checks). Settlement is recorded manually. |
Api | Payment processor integrations (Stripe, Adyen). Pre-authorization and capture via the PaymentAdapter trait. |
Ach | ACH bank transfers. Lower cost, higher latency. Suitable for batch settlement. |
Wire | Wire transfers for high-value settlements with same-day finality. |
Ledger | Internal ledger entries between accounts within the same operator. No external capital movement. |
Sandbox | Development rail that simulates settlement without moving funds. |
Web3 | On-chain settlement via the chio-settle crate. Supports EVM (Base / Arbitrum) escrow, the bounded Solana Ed25519 memo path, and Chainlink CCIP cross-chain coordination. |
The rail is selected per-invocation based on the operator's configuration, the counterparty's supported rails, and the settlement amount. Most operators start with Sandbox during development and graduate to Api or Web3 in production.
Payment Adapter Integration
For off-chain rails (Api, Ach, Wire), settlement flows through the PaymentAdapter trait. The trait is synchronous. Seven methods, four of which carry the payment lifecycle and three of which describe or query the rail:
pub trait PaymentAdapter: Send + Sync {
fn rail_id(&self) -> &'static str {
"unspecified"
}
fn rail_mode(&self) -> Option<PaymentRailMode> {
None
}
/// Authorize or prepay up to `amount_units` before the tool executes.
///
/// Implementations must be idempotent by `request.reference`: repeating the
/// same request returns the same authorization and creates at most one
/// rail-side hold or prepayment.
fn authorize(
&self,
request: &PaymentAuthorizeRequest,
) -> Result<PaymentAuthorization, PaymentError>;
/// Finalize payment for the actual cost after tool execution.
///
/// Implementations must be idempotent by `(authorization_id, reference)`.
fn capture(
&self,
authorization_id: &str,
amount_units: u64,
currency: &str,
reference: &str,
) -> Result<PaymentResult, PaymentError>;
/// Release an unused authorization hold.
///
/// Implementations must be idempotent by `(authorization_id, reference)`.
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>;
/// Return the side-effect-free rail state for a durable reference.
///
/// This query must remain answerable when `authorization_id` is absent so
/// recovery can close the crash window after authorization but before the
/// rail-assigned identifier reaches the local journal.
fn settlement_state(
&self,
reference: &str,
authorization_id: Option<&str>,
) -> Result<RailSettlementState, PaymentError> {
let _ = (reference, authorization_id);
Err(PaymentError::Unavailable(
"settlement_state query is unsupported by this payment adapter".to_owned(),
))
}
}Read the idempotency contracts, because they are the part an implementation gets wrong. authorize is idempotent by request.reference; capture and release are idempotent by the pair (authorization_id, reference). settlement_state has to stay answerable when authorization_id is absent, so recovery can close the window between authorizing and the rail identifier reaching the local journal.
The API uses request arguments instead of a Money type. The authorization object tracks the state of each payment flow, and capture, release, and refund all return one shared PaymentResult:
pub struct PaymentAuthorization {
/// Payment rail's authorization or hold identifier.
pub authorization_id: String,
/// Whether authorization created a reversible hold or completed final prepayment.
pub state: PaymentAuthorizationState,
/// Rail-specific metadata such as idempotency keys, quote IDs, or expiry.
pub metadata: serde_json::Value,
}pub struct PaymentResult {
pub transaction_id: String,
pub settlement_status: RailSettlementStatus,
pub metadata: serde_json::Value,
}There is no settled: bool. Whether the authorization created a reversible hold or completed a final prepayment is a two-variant enum, PaymentAuthorizationState with Held and PrepaidFinal, read as authorization.state.is_final()(crates/kernel/chio-kernel/src/payment/types.rs:17-29).
Pre-authorization flow
max_cost_per_invocation before the tool call, then capture the actual cost after the call completes. If the actual cost is zero (for example, a cached result), release the authorization instead of capturing it.Here is the authorize-capture-release cycle:
// 1. Pre-authorize the maximum possible cost.
let auth = adapter.authorize(&PaymentAuthorizeRequest {
amount_units: max_cost_units,
currency: "USD".to_string(),
payer: agent_id.clone(),
payee: tool_server_id.clone(),
reference: invocation_id.clone(),
governed: None,
commerce: None,
})?;
// 2. Execute the tool invocation.
let result = invoke_tool(&request)?;
// 3. Capture the actual cost, or release the whole hold if nothing was spent.
let actual_units = result.metered_cost_units();
if actual_units == 0 {
adapter.release(&auth.authorization_id, &invocation_id)?;
} else {
adapter.capture(&auth.authorization_id, actual_units, "USD", &invocation_id)?;
}Run a prepaid call, and then break it
chio mcp governed-sim drives one governed MustPrepay call through an ephemeral kernel and writes the signed receipt bundle to --out. It is the smallest way to see the settlement fields on a receipt without standing up a rail.
$ chio --session-db ./s.sqlite mcp governed-sim \
--governed-mustprepay --payment-adapter sim --out ./bundle.json
$ jq '.metadata.financial' ./bundle.json{
"budget_remaining": 900,
"budget_total": 1000,
"cost_breakdown": {
"payment": {
"adapter_metadata": {
"adapter": "sim",
"commerce": null,
"governed": {
"approvalTokenId": "governed-sim-approval-1",
"intentHash": "f57e0f00b78fe5267f81f92696a0fe9a32339cb02d57b81c9e15f59996f3062d",
"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": "0473a15a01ca05a11b0558cd9fd1edd8bddd26f3f598f1c168aaec97dd7f031b",
"settlement_status": "settled"
}crates/core/chio-core-types/src/receipt/economics.rs:77-106at fe56570Now remove the rail. Re-run with --payment-adapter none. A MustPrepay intent with no adapter configured is unpayable, so the kernel refuses before the tool runs. Every error the CLI prints is a three-line envelope: the code and message, a context object, and a suggested fix.
$ chio --session-db ./s2.sqlite mcp governed-sim \
--governed-mustprepay --payment-adapter none --out ./bundle-deny.jsoncrates/kernel/chio-kernel/src/kernel/validation.rs:2698-2713at fe56570The nonzero exit is the visible half. The durable half is that the bundle carries a signed deny receipt:
$ 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"
}
}crates/kernel/chio-kernel/src/kernel/responses/deny_responses.rs:31-90at fe56570Compare the two financial blocks. On the allow, cost_charged is 100 and budget_remaining drops to 900. On the deny, attempted_cost records what was asked for, cost_charged is 0, and the budget is untouched at 1000. A refused call costs the holder nothing and still leaves the attempt on the record with its price attached, which is what makes a denial auditable rather than merely absent.
On-Chain Settlement (Web3 Rail)
The Web3 rail settles tool invocation costs on-chain through smart contracts. A Merkle proof lets a provider release escrow without the operator or an intermediary.
Smart Contracts
The on-chain settlement system uses these contracts:
| Contract | Role |
|---|---|
ChioEscrow | Holds funds in escrow during tool invocations. Supports ERC-20 tokens with permit. |
ChioBondVault | Manages collateral bonds for credit facilities and autonomous agent execution. |
ChioRootRegistry | Stores merkle roots published by the kernel for checkpoint anchoring. |
ChioIdentityRegistry | Maps did:chio identifiers to on-chain addresses for settlement routing. |
ChioPriceResolver | Integrates oracle price feeds for cross-currency settlement and FX verification. |
Settlement Paths
Once funds are locked in ChioEscrow, they can be released through two settlement paths:
- DualSignature (two-party signed): the operator signs a release message authorizing the provider to withdraw. Settlement completes in one transaction after both parties sign the amount.
- MerkleProof(inclusion-proof from the receipt log): the provider submits a merkle proof showing that the invocation receipt was included in a published checkpoint. The provider submits a valid proof against a root in
ChioRootRegistry; the operator does not need to cooperate.
Choose the path at dispatch, not at release
EscrowTerms has no path field (contracts/src/interfaces/IChioEscrow.sol:7-16). The SDK does not. chio-settle refuses to prepare a release whose path does not match the one the dispatch declared (crates/economy/chio-settle/src/evm/prepare.rs:555-559,1129-1133), so the fallback has to be chosen before the escrow is funded.Escrow Lifecycle
The lifecycle flows PendingDispatch -> EscrowLocked -> PartiallySettled / Settled / Reversed / ChargedBack / TimedOut, with Failed and Reorged for a settlement that did not land:
| State | Meaning |
|---|---|
PendingDispatch | Escrow created but funds not yet locked on-chain |
EscrowLocked | Funds locked in the ChioEscrow contract, invocation can proceed |
PartiallySettled | Some funds released to the provider, remainder still held |
Settled | All escrowed funds distributed |
Reversed | Escrow reversed, funds returned to the caller |
ChargedBack | Dispute resolved in caller's favor after settlement |
TimedOut | Escrow expired without settlement, funds returned to caller |
Failed | The settlement transaction failed |
Reorged | A chain reorganization invalidated the settlement |
ERC-20 token support uses the EIP-2612 permit pattern, allowing callers to approve and deposit in a single transaction without a separate approval step.
Payment-Rail Compatibility
Beyond escrow and bond settlement, the chio-settle crate exposes support for established on-chain payment methods, so a settlement can use an existing method instead of the native escrow contracts. Each integration provides preparation and verification functions in the crate's payments module:
- x402 payment requirements (
build_x402_payment_requirements,X402SettlementMode) - EIP-3009
transferWithAuthorizationdigests (prepare_transfer_with_authorization,Eip3009Domain, and anEip3009NonceStore, which the prepare function takes as an argument rather than an option) - Circle nanopayments (
evaluate_circle_nanopayment,CircleNanopaymentPolicy) - ERC-4337 paymaster-sponsorship checks (
prepare_paymaster_compatibility,Erc4337PaymasterPolicy)
Setting Up an Escrow
Settlement uses Rust and SDK APIs
chio-settle crate's prepare / submit / confirm sequence. The economic layer exposes these as Rust and SDK APIs; there is no chio settle <chain> subcommand beyond chio settle status.Create an escrow and settle it through DualSignature as follows. The SDK prepares each call, validates the capital instruction and identity binding, then submits and confirms it:
use chio_settle::evm::{
prepare_web3_escrow_dispatch, submit_call, confirm_transaction,
finalize_escrow_dispatch, EscrowDispatchRequest,
};
use chio_core::web3::trust_profile::Web3SettlementPath;
// `config` (SettlementChainConfig), the signed capital instruction, and the
// operator's SignedWeb3IdentityBinding are assembled upstream.
let request = EscrowDispatchRequest {
dispatch_id: dispatch_id.clone(),
issued_at,
trust_profile_id: profile_id.clone(),
contract_package_id: contract_package_id.clone(),
capability_id: capability_id.clone(),
depositor_address: caller_address.clone(),
beneficiary_address: provider_address.clone(),
capital_instruction: instruction.clone(),
settlement_path: Web3SettlementPath::DualSignature,
oracle_evidence_required_for_fx: false,
note: None,
};
// 1. Prepare the escrow-create call. This validates the instruction and
// identity binding and derives the escrow id via a static contract read.
let prepared = prepare_web3_escrow_dispatch(&config, &request, &operator_binding).await?;
// State: PendingDispatch -> (submit) -> EscrowLocked
// 2. Submit and confirm the create transaction.
let tx_hash = submit_call(&config, &prepared.call).await?;
let tx_receipt = confirm_transaction(&config, &tx_hash).await?;
// 3. Finalize the on-chain escrow id from the emitted event.
let dispatch = finalize_escrow_dispatch(&prepared, &tx_receipt)?.dispatch;After the tool invocation produces a signed ChioReceipt, the operator prepares the dual-signature release and submits it. The release is configured to release the entire escrowed amount:
use chio_settle::evm::{
prepare_dual_sign_release, submit_call, confirm_transaction, DualSignReleaseInput,
};
let receipt = invoke_tool(&request).await?;
// 4. Prepare the dual-signature release for the observed amount.
// DualSignReleaseInput's key field is private and zeroizing, so it is built
// through its constructor rather than as a struct literal.
let release = prepare_dual_sign_release(
&config,
&dispatch,
&receipt,
&DualSignReleaseInput::new(operator_settlement_key_hex.clone(), settlement_amount),
).await?;
// 5. Submit and confirm the release transaction.
let tx_hash = submit_call(&config, &release.call).await?;
confirm_transaction(&config, &tx_hash).await?;
// State: EscrowLocked -> SettledSettling via Merkle Proof
If the operator is unresponsive, the provider settles with an anchor inclusion proof once the receipt has been included in a published checkpoint. Choose the path at dispatch time, not at release time: the contract's EscrowTerms carries no path field, but chio-settle refuses to prepare a release that does not match the dispatch's declared path (crates/economy/chio-settle/src/evm/prepare.rs:555-559,1129-1133). So the dispatch has to have been created with Web3SettlementPath::MerkleProof:
use chio_settle::evm::{
prepare_merkle_release, submit_call, confirm_transaction, EscrowExecutionAmount,
};
use chio_core::web3::anchors::AnchorInclusionProof;
// 1. Build the anchor inclusion proof once the receipt's checkpoint root is
// published to ChioRootRegistry (see chio-anchor).
let anchor_proof: AnchorInclusionProof = build_anchor_inclusion_proof(/* ... */)?;
// 2. Prepare the Merkle release, binding the anchor proof to the dispatch.
let release = prepare_merkle_release(
&config,
&dispatch,
&anchor_proof,
&anchor_content, // SettlementAnchorContentBinding
EscrowExecutionAmount::Full,
)?;
// 3. Submit and confirm the release transaction.
let tx_hash = submit_call(&config, &release.call).await?;
confirm_transaction(&config, &tx_hash).await?;
// State: EscrowLocked -> SettledCheckpoint Anchoring
Checkpoints commit receipt history to on-chain registries. The chio-anchor crate publishes Merkle roots to ChioRootRegistry and verifies returned proofs against those roots.
Checkpoint anchoring can use three independent methods, modeled as AnchorLaneKind::{EvmPrimary, BitcoinOts, SolanaMemo} and verified together as an AnchorProofBundle:
- EVM primary: merkle root published to
ChioRootRegistryon an EVM chain (Ethereum L1 or L2) - Bitcoin OpenTimestamps: OpenTimestamps proof anchored to the Bitcoin blockchain for calendar-independent timestamping
- Solana memo: checkpoint hash written as a Solana memo transaction for fast, low-cost redundancy
chio-anchor also owns a second, independent mechanism: chio.anchor_batch.v1, which Merkle-batches checkpoint IDs and binds the batch to a public witness (Rekor or OpenTimestamps) through a WitnessPolicy. A bounded Chainlink Functions request (ChainlinkFunctionsTarget) provides a fallback-verification path over a receipt batch.
A proof bundle links an individual receipt to a chain anchor:
Receipt (content hash)
-> Merkle inclusion proof (siblings + index)
-> Checkpoint statement (root + timestamp + lane)
-> Chain anchor (tx hash on EVM / BTC / Solana)Verification is offline-capable
Settlement Finality
Settlement finality depends on the chain where the escrow or checkpoint is anchored. Web3FinalityMode declares three modes and binds none of them to a chain (crates/economy/chio-web3/src/trust_profile.rs:26-32); the mapping is deployment configuration, carried per chain as Web3ChainFinalityRule with a chain_id, mode and min_confirmations(:55-59). The chains named below are the usual assignments, not values the enum carries:
| Finality Level | Chain | Meaning |
|---|---|---|
L1Finalized | Ethereum mainnet | Transaction included in a finalized epoch (~12 minutes) |
OptimisticL2 | Optimism, Arbitrum, Base | Soft-confirmed on L2, subject to challenge window for full finality |
SolanaConfirmed | Solana | Confirmed by supermajority of validators (~400ms) |
A dispute window is not an on-chain release gate. Both release paths in ChioEscrow execute immediately: releaseWithSignature settles the moment a valid dual signature lands, and releaseWithProofDetailed settles the moment a valid inclusion proof lands. Neither path carries a timelock; the only gate on either is the expiry check in _ensureLive(contracts/src/ChioEscrow.sol:352-355). Note the name: the five-argument releaseWithProof is a stub that reverts with ProofMetadataRequired(ChioEscrow.sol:158-166); the function that settles takes a ChioMerkle.Proof(:168-197), and partialReleaseWithProof splits the same way at :230. Dispute windows live one layer up, in two places, and neither is keyed on the settlement path.
The chio-settle observer selects a finality dispute window from the settlement amount. The default SettlementPolicyConfig tiers a dispute_window_secs across four bands of minor units:
| Settlement amount (minor units) | dispute_window_secs |
|---|---|
| up to 1,000 | 0 (immediate) |
| up to 100,000 | 3,600 (1 hour) |
| up to 1,000,000 | 14,400 (4 hours) |
| above 1,000,000 | 86,400 (24 hours) |
inspect_finality_for_receipt applies this tiering identically whether the release used DualSignature or MerkleProof. It classifies a SettlementFinalityStatus (AwaitingConfirmations, AwaitingDisputeWindow, Finalized, Reorged) after the release; it does not block the release call.
Separately, a Web3TrustProfile declares a per-path dispute_windows vector. Each entry pairs a settlement_path with challenge_window_secs, recovery_window_secs, and a dispute_policy (OffChainArbitration, TimeoutRefund, or BondSlash). validate_web3_trust_profile rejects a zero challenge or recovery window for any path, including DualSignature, so a compliant profile cannot declare a zero-duration window for the fast path.
Web3SettlementLifecycleState defines states for successful and exceptional flows. The type is in chio-web3, re-exported via chio_core::web3:
pub enum Web3SettlementLifecycleState {
PendingDispatch,
EscrowLocked,
PartiallySettled,
Settled,
Reversed,
ChargedBack,
TimedOut,
Failed,
Reorged,
}Four of the nine are exceptional: Reversed returns funds before settlement, ChargedBack resolves a dispute after it, TimedOut is an escrow that expired without action, and Reorged is a chain reorganization invalidating one.
Reorg handling
Reorged state is rare but important. Reorg handling is bounded, reviewable automation, not unconditional silent resubmission. The chio-settle observer classifies a reorg as SettlementFinalityStatus::Reorged and recommends SettlementRecoveryAction::ResubmitAfterReorg. Execution runs through a cron/log-triggered SettlementWatchdogJob that carries an operator_override_required flag; automation outcomes explicitly include ManualOverrideRequired.Oracle Price Verification
When tool costs are denominated in one currency but settlement occurs in another (for example, costs in USD, settlement in USDC or ETH), the chio-link crate resolves verified exchange rates. Its ChioLinkOracle implements the PriceOracle trait and reads prices from a pluggable OracleBackend that supports both Chainlink (AggregatorV3Interface.latestRoundData) and Pyth (Hermes). The on-chain ChioPriceResolver contract is a separate, contract-level price read used on L2; the receipt-side oracle evidence is produced by chio-link.
The oracle applies several safety mechanisms:
- Staleness checks: prices older than a configured threshold are rejected; the resolver denies conversion when the feed is stale.
- L2 sequencer protection: on L2 chains (Optimism, Arbitrum, Base), it checks the Chainlink sequencer uptime feed and fails closed while the sequencer is down or inside its post-recovery grace period.
- Cross-source divergence: the primary backend read is cross-checked against a fallback with a basis-point circuit breaker, failing closed on divergence. Per-pair
twap_enabledpolicy averages a rolling observation window into a TWAP. - Oracle evidence: cross-currency conversions produce an
OracleConversionEvidencerecord that is stored on the receipt for auditability.
// ChioLinkOracle implements PriceOracle: it resolves the rate from Chainlink
// and/or Pyth behind a divergence circuit breaker and an L2 sequencer check.
let rate = oracle.get_rate(&pair)?;
// The resolved rate converts into OracleConversionEvidence for the receipt.
let evidence = rate.to_conversion_evidence(/* conversion context */);
// evidence fields: schema, base, quote, authority, rate_numerator,
// rate_denominator, source, feed_address, updated_at, max_age_seconds,
// cache_age_seconds, converted_cost_units, original_cost_units,
// original_currency, grant_currency, oracle_public_key, signature.Verify the result
Settlement is verified from the receipt, not from the rail. Three fields answer the question:
metadata.financial.settlement_statusissettledon a completed prepaid call,not_applicableon a refused one, andfailedon an overrun.metadata.financial.payment_referenceis the adapter's authorization id, which is the join key back to the rail's own record.cost_breakdown.payment.preauthorized_unitsagainstrecorded_unitssays whether the hold and the capture agreed. A gap between them is a release.
The captured allow under Run a prepaid call shows all three. For on-chain settlement, chio settle status reads the local lifecycle records.
Failures and recovery
No adapter for a prepaid intent
A MustPrepay intent with no adapter configured is refused before the tool runs, and the bundle is still written. That is the captured deny above. Recovery is configuration: wire an adapter, or change the intent's settlement mode. Do not retry the same call against the same absent rail.
A crash between authorize and capture
This is the window the trait's idempotency contracts exist for. authorize keyed on request.reference means a replayed authorization returns the same hold rather than creating a second one, and settlement_state stays answerable without an authorization_id so recovery can ask the rail what happened before the identifier reached the local journal. An adapter that skips either contract turns a crash into a double charge.
A reorg invalidated a settled transaction
The observer classifies it as SettlementFinalityStatus::Reorged and recommends SettlementRecoveryAction::ResubmitAfterReorg. Resubmission is not automatic: the watchdog job carries operator_override_required and stops at SettlementAutomationOutcome::ManualOverrideRequired rather than resubmitting silently.
The release path does not match the dispatch
Preparing a Merkle release against a dispatch declared as DualSignature, or the reverse, is refused by chio-settle before anything reaches the chain. There is no recovery at release time; the dispatch has to be created with the path you intend to fall back to.
Summary
| Concept | Description |
|---|---|
| Settlement Rails | Seven rails (Manual, Api, Ach, Wire, Ledger, Sandbox, Web3) as CapitalExecutionRailKind variants |
| PaymentAdapter | authorize, capture, release, refund lifecycle for off-chain rails |
| On-Chain Escrow | ChioEscrow contract with DualSignature and MerkleProof settlement paths; the second needs no counterparty signature |
| Checkpoint Anchoring | chio-anchor multi-lane proofs (EVM primary + Bitcoin OTS + Solana memo) plus the chio.anchor_batch.v1 witness mechanism |
| Finality | L1Finalized, OptimisticL2, SolanaConfirmed with chain-specific rules |
| Price Oracle | chio-link (ChioLinkOracle) with Chainlink + Pyth backends, divergence circuit breaker, staleness and L2 sequencer checks |
Next Steps
- Credit & Underwriting · credit facilities, bonds, and underwriting decisions
- Budgets & Metering · cost limits and usage tracking before settlement
- Receipts · the audit trail that settlement proves payment for