Skip to content

Operate sessions

Collect payments, retain state and recover safely.

On this page

Establish readiness

Choose Python or Rust below. These fragments run inside your async provider host, using its sdk instance and retained state. Rust snippets use ? inside a function returning IntegrationResult; import ProviderLifecycle to access the runtime methods.

Check await sdk.start() and per-session readiness before admitting paid work. Readiness depends on confirmed funding, fresh chain/pause/deadline evidence, authenticated Control ownership, reconciled accounting and no retained close intent. A registered identity or reachable endpoint is insufficient.

readiness = await sdk.start()
print(readiness.ready, readiness.failures)
page = await sdk.sessions.list(limit=100)
for session in page.sessions:
    print(session.session_id.hex(), session.readiness)

Collect USDC

The configured collector captures eligible accumulated usage and can batch due sessions. Explicit operations are also available. Each collection stays within the voucher ceiling, deposit and collection deadline; successful capture leaves the session open.

result = await sdk.sessions.settle(session_id)
print(result.state, result.operation_ref)
# Batch targets retain their original order.
batch = await sdk.sessions.settleBatch((first_session_id, second_session_id))
for outcome in batch.sessions:
    print(outcome.session_id.hex(), outcome.target_covered, outcome.failure)

Inspect each batch outcome. The outer transaction succeeding does not prove every item succeeded, and a no-op does not create a positive settlement reference. Retain original operation references during uncertainty. Do not split or resubmit an unknown batch as new economic operations.

Open collection ends 24 hours after qualifying on-chain activity. Increasing confirmed capture and positive confirmed top-up refresh activity. A timely requestClose starts a full one-hour closing collection window. Requests, vouchers, meters and no-op captures do not extend the clock. Capture must execute before the deadline; submission before it is insufficient.

Configure freshness, settlement intervals and collection margins for your infrastructure, including time to stop jobs, obtain final counts and confirm collection. Pauses and outages do not stop the clocks. After expiry, late usage evidence creates no new payment right.

Account for the session

Four amounts have different jobs: the deposit funds the session, the signed ceiling authorizes collection, reported usage determines SDK accounting, and confirmed capture records what has actually been paid.

ILLUSTRATIVE · ONE RUN · NO CACHE

1,000 input + 500 output tokens

1 USDC / MTok input · 2 USDC / MTok output

Deposit
1.000000USDC
Funds available in escrow
Signed ceiling
0.010000USDC
Authorized maximum
Reported usage
0.002000USDC
1,000 × 1 + 500 × 2, per million
Confirmed capture
0.002000USDC
Paid only after chain confirmation
After a timely confirmed close: 0.998000 USDC returns from escrow. Network fees are separate.

The example uses illustrative prices of 1 USDC per million input tokens and 2 USDC per million output tokens, with no cached input. It assumes one run, no previous capture and a timely successful close. Network fees are separate; the refund is an escrow amount, not the wallet's net balance after gas.

Exact accounting · amounts in six-decimal USDC base units
run_numerator = (input_tokens - cache_tokens) * input_rate
              + cache_tokens * cache_rate
              + output_tokens * output_rate

session_usage = floor(sum(run_numerator) / 1_000_000)
required_coverage = ceil((usage_numerator + reserved_numerator
                         + candidate_numerator) / 1_000_000)

Use exact integers. Apply the floor once to the accumulated session numerator, across all accepted model/price versions. A per-run receipt amount is informative and non-additive. Cache counts are a subset of input counts and require an accepted cache price; with no cache and zero cache counts, use input and output only.

Unused authorization is not a charge. A capture changes confirmed settlement; it does not add usage or restore consumed reservation. The contract bounds capture by the signed ceiling, deposit and eligibility, but does not verify token counts.

Retain and recover authoritative state

One runtime owns a consistent payment view for its sessions. Coordinate your workers around it. Socket affinity is not payment authority, and the SDK supplies no distributed database, replication layer or engine recovery service.

evidence = await sdk.sessions.checkpoint(session_id)
# Persist the complete evidence in your durable storage.
# After restart, load that original evidence before recovery.
view = await restored_sdk.sessions.recover(evidence)

Retain all required signed run proofs, historical prices, reservations, accounting and operation evidence. Chain state cannot reconstruct private token history. Missing or contradictory history fails recovery; never replace it with zero usage. Recovery installs validated state, not Control ownership or execution readiness.

A new authenticated attachment fences the old generation. Old-generation reports may finalize their original runs but cannot authorize new work. Keep unresolved reservations until definitive provider reports establish the outcome.

Close sessions and shut down

  1. 01Open

    Funded sessions can admit covered work. Eligible capture keeps the session open.

  2. 02Closing

    A wallet requestClose starts a one-hour collection window. Stop new grants.

  3. 03Closed

    An eligible close or exit transaction completes the transition and returns unused escrow.

  4. 04Cooperative path

    A ready cooperative close can go directly from Open to Closed, without the wallet's waiting period.

Open-session expiry is 24 hours after qualifying confirmed activity. Passing a deadline does not send a refund; an eligible transaction must confirm.

Cooperative close stops new grants and accounts for final work before confirmed capture/refund. Preserve close intent across retries and restarts. A timed-out close must not reopen admission. After collection expiry, only a permitted refund-only close at the already settled amount is possible.

import time

report = await sdk.shutdown(time.monotonic() + 30.0)
print(report.unresolved_session_ids)
print(report.unresolved_runs)
print(report.pending_operations)

Shutdown stops grants and waits boundedly for definitive reports. It does not stop your server, prove engine termination or close escrow. Retain its unresolved report and stop your own listeners/jobs through your host lifecycle. Rust uses an absolute tokio::time::Instant; retain ShutdownResult from standalone::serve or its error.

Apply intentional updates

Write changes.json containing only your intended updates. Existing models accept partial models.upsert edits; new models need complete declarations. models.remove removes named models. Review this illustrative change before use:

JSON · changes.json illustration
{"models":{"upsert":[{"model":"YOUR_PROVIDER/MODEL","rates":{"output":"1500000"}}]}}
Shell
mppi provider update --testnet --config mppi.toml \
  --changes changes.json --rpc-env ARC_RPC_URL_TESTNET

Update only the requested configuration sections. Recipient changes use the separate proof process and confirm before a changed catalogue publishes. Completed configuration must be loaded through your application's deployment lifecycle. Existing sessions retain their operator/recipient; admitted runs retain their accepted rates.

Observe without changing accounting

Use lifecycle hooks for logs and metrics, correlated by session/run IDs, meter sequence and operation reference. Delivery is best effort through a bounded queue. Callbacks can be repeated, dropped or fail; callback counts are not payment totals or a durable event stream.

Keep callbacks short and hand off to your logging system. Inspect sdk.hook_stats for local delivered/dropped/failed/queued counters. Neither those counters nor log lines establish canonical settlement.

Practice the failure paths

Condition / Expected operating response

Stale or stalled chain evidence
Stop new grants; preserve the last accounting view; restore fresh canonical observation.
Contract pause or approaching cutoff
Stop admission/renewal as required and terminate actual jobs within coverage; keep clocks running.
Lost Control attachment
Fence the old generation, terminate its jobs and retain reservations until final reports.
Unknown wallet submission
Keep the original journal and identity; reconcile, do not create replacement intent.
Restart with incomplete history
Remain not ready; restore complete evidence rather than infer an unused session.
Engine cannot establish final counts
Keep the run unresolved; EOF or cancellation does not free its reservation.
Operate sessions | Agent providers | MPPI Docs