> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# MPP metered payment sessions

> The concept model behind Devotel Orbit metered agent-payment sessions: a pre-authorized spend envelope a buyer agent opens once, a seller agent meters per call, and x402 settles on-chain. Caps, required expiry, revocation, and settlement ordering explained.

# 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](https://docs.orbit.devotel.io/api-reference/commerce.md).

## 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](https://docs.orbit.devotel.io/concepts/commerce-checkout-spine.md) prices a cart and captures one
  payment on whichever rail wins. One checkout, one charge.
* **Federated authorization** — the [agentic commerce
  model](https://docs.orbit.devotel.io/concepts/agentic-commerce-model.md) 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`:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/commerce/mpp-session \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "mpp_abc123",
    "agentId": "agent_buyer_7",
    "principalId": "principal_42",
    "spendCap": 5,
    "maxPerCall": 0.1,
    "currency": "USD",
    "expiresAt": 1796000000000
  }'
```

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:

| Status | Meaning |
| - | - |
| `active` | Open; calls meter against the remaining headroom. |
| `exhausted` | `spent` reached `spendCap`. Terminal. |
| `expired` | The clock passed `expiresAt`. Terminal, and applied by the clock even when the status field has not flipped yet. |
| `revoked` | The principal withdrew consent. Terminal. |

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:

```json theme={null}
{
  "data": {
    "session": { "id": "mpp_abc123", "status": "active", "spent": 0.09, "callCount": 3 },
    "meter": {
      "authorized": false,
      "reason": "exceeds_per_call_cap",
      "amount": 0.25,
      "remainingCap": 4.91
    },
    "settlement": null
  }
}
```

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

* [Agentic commerce authorization and checkout
  model](https://docs.orbit.devotel.io/concepts/agentic-commerce-model.md) — the mandate plane MPP sessions
  complement.
* [Commerce checkout spine](https://docs.orbit.devotel.io/concepts/commerce-checkout-spine.md) — the
  one-cart checkout a metered session deliberately is not.
* [A2A federation model](https://docs.orbit.devotel.io/concepts/a2a-federation-model.md) — the federation
  boundary buyer and seller agents meet at.
* [Commerce API reference](https://docs.orbit.devotel.io/api-reference/commerce.md) — the endpoint surface
  for open, meter, call, revoke, and verify.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.