Skip to main content

MPP metered payment sessions

When a buyer agent consumes your agent services call after call, authorizing each checkout separately is the wrong shape: a checkout is discrete, tool calls stream. A metered agent-payment session (MPP session) is the per-call shape: the buyer pre-authorizes a spend envelope once — a spend cap, a per-call ceiling, and a required expiry — and every call the seller agent makes from then on is metered against that envelope and settled as a micropayment over x402. This page explains the model; the endpoint surface lives in the Commerce API reference.

Where MPP sits

Orbit ships three payment primitives for agent-driven commerce, and each answers a different question:
  • One-shot checkout — the commerce checkout spine prices a cart and captures one payment on whichever rail wins. One checkout, one charge.
  • Federated authorization — the agentic commerce model lets a principal issue an AP2-style mandate a buyer agent presents across a federation boundary, with W3C verifiable-credential shapes for cross-network counterparts.
  • Metered sessions — this page. No cart, no checkout: a standing spend envelope that many calls draw down, each settled as its own micropayment.
The use case is a buyer agent renting a seller agent’s capability by the call: rank products, run a search, summarize a document. The buyer pre-authorizes five dollars once; the seller meters two cents per tool call until the envelope is exhausted, expires, or is revoked. Authorization — who may spend how much — stays with the principal. Settlement — actually moving money — stays with the x402 facilitator. The session itself only decides, the same split the mandate plane uses for checkouts.

The session model

Open a session with POST /commerce/mpp-session:
Every amount is in major units of the session currency (dollars, not cents). spendCap and maxPerCall must both be positive, and maxPerCall may not exceed spendCap. expiresAt is a future epoch-ms timestamp and it is required: unlike a checkout mandate, a streaming pre-authorization is always bounded in time. The response is a session snapshot, and storage is yours. Orbit does not persist the session for you — you round-trip the snapshot in the body of every later /meter, /call, /revoke, and /verify request, exactly as you round-trip the cart snapshot on the checkout spine. Round-tripping is safe because the snapshot carries a SHA-256 consent digest over its immutable scope: the id, the agent, the principal, both caps, the currency, the expiry, and the creation time. Every meter decision recomputes that digest before anything else, so a snapshot whose cap or expiry was altered after open fails integrity and is declined; the server re-verifies the consent on every call and never trusts the client’s copy of a cap. Only spent, callCount, status, and the timestamps advance. A session is single-currency: price each call in the session’s currency before metering it. A session holds exactly one of four statuses: Terminal means terminal: once a session leaves active, no later call meters under it. POST /commerce/mpp-session/meter is the dry-run: it evaluates one call against the snapshot — integrity, then status, expiry, amount, per-call ceiling, cumulative cap, in that order — and returns the decision without advancing spend. The decision carries a deterministic meter id (<sessionId>-call-<n>), so a dry-run and the committing call line up one to one in your audit trail.

Settlement flow

POST /commerce/mpp-session/call meters and settles one call. When x402 settlement is not enabled for the deployment, the route answers 503 MPP_SETTLEMENT_NOT_ENABLED and touches nothing — the settlement surface stays dark until it is switched on. When it is on, an authorized call rides the standard x402 two-step:
  1. Challenge. A /call without an X-PAYMENT header answers 402 with the payment requirements for this one call: the scheme, the network, the settlement asset, the amount in the asset’s atomic units, and the payee. The challenge is Cache-Control: no-store.
  2. Verify, then settle. The client retries with a signed X-PAYMENT payload. The facilitator verifies it against the requirements; a missing, malformed, or invalid payment is re-challenged with a fresh 402. Only a verified payment is settled, and only once settlement reports success is the spend committed to the session.
That ordering is the load-bearing part: spent advances only after settlement succeeds. If a payment verifies but settlement fails — a rail outage, a reverted transaction — the call returns with the failed settlement and the session’s headroom is untouched. A facilitator failure never burns cap against money that did not move. On success the response carries the committed session, the meter decision, and the on-chain receipt: the network the payment settled on and the transaction hash. Session bookkeeping runs in major units; the x402 wire amount runs in the settlement asset’s own atomic units — for example USDC’s six-decimal base units on Base. You supply the atomic amount per call; Orbit never assumes an asset’s decimal count. A declined call never reaches settlement. When the meter declines a call — over the per-call ceiling, over the remaining cap, expired, revoked, or tampered — /call answers 200 with authorized: false, the decline reason, and settlement: null. No 402 is shaped for money the session did not authorize.

The money-safety model

Four controls bound every session, all fixed at open and all yours to set:
  • Spend cap. Cumulative spent can never cross spendCap; a call that would cross it declines with exceeds_session_cap, and reaching it flips the session to exhausted.
  • Per-call ceiling. A single call above maxPerCall declines with exceeds_per_call_cap, however much headroom remains.
  • Expiry. expiresAt is mandatory at open, and once the clock passes it every call declines with expired, even while the stored status still reads active.
  • Revocation. POST /commerce/mpp-session/revoke flips the session to revoked immediately and terminally; revoking an already-revoked session is an error, not a silent no-op.
A fifth control underlies all four: the consent digest. Because every meter recomputes the digest over the immutable scope, raising spendCap or pushing expiresAt in a stored snapshot authorizes nothing — the call declines with integrity_failed. Reconcile against your own ledger. spent and callCount advance only on settled calls, and each settlement carries a transaction hash, so the session’s counters match your own record of on-chain receipts line for line; POST /commerce/mpp-session/verify recomputes a snapshot’s digest for your auditors.

Worked example

Open a five-dollar session with a ten-cent per-call ceiling, expiring in an hour (the expiresAt value is epoch-ms). The open response is the snapshot to store: spent 0, callCount 0, status "active", plus the consent digest. Stream three calls — $0.02, $0.03, $0.04 — each through the 402 → X-PAYMENT round trip above. All three settle and commit: spent advances 0.02 → 0.05 → 0.09, callCount reaches 3, and remainingCap reports 4.98, 4.95, then 4.91. Now a $0.25 call. It is inside the remaining cap but above the per-call ceiling, so /call declines it without shaping a settlement:
Spend stays at 0.09. Revoke the session and the snapshot comes back revoked with revokedAt set; every later call declines with reason revoked whatever the headroom.

See also