pay.sh docs
Building with payPayment channels

Streaming sessions

MPP sessions — open one channel, stream many metered deliveries authorized by cumulative off-chain vouchers, and settle once when the session idle-closes.

An MPP session opens one payment channel and meters many deliveries against it. Each delivery is authorized by an off-chain voucher carrying a running cumulative total; nothing touches the chain per delivery. When the session goes idle the operator settles the final amount on-chain and refunds the rest.

When to use it

Reach for a session when you're metering a stream rather than a single call:

  • a token-by-token SSE response (the canonical case),
  • a burst of many cheap calls a client makes in one sitting,
  • anything where you want one channel reused across deliveries — and optionally split the proceeds across multiple recipients.

For a single call whose cost you only learn afterward, usage charges (upto) are simpler — one authorization, one settlement.

How it flows

The buyer opens the channel with a deposit (the cap). For each delivery they sign a cumulative voucher — if the first authorizes $0.0001 and the next $0.0002, the operator may claim up to $0.0002 total, not the sum. The gateway verifies each voucher and serves off-chain. After the configured idle delay (close_delay_ms), the operator settles the final voucher and distributes — refunding the unused deposit. A buyer can topUp to extend a channel, and can always force-close (after a grace period) to recover funds if the server goes away.

With the pay CLI

A session needs two things in the spec: a top-level session: block configuring the channel, and one or more endpoints whose metering accepts mpp-session. The channel cap and idle-close delay live on the block; the per-delivery price is the endpoint's metering dimension.

# Channel-wide config: deposit ceiling + idle-close delay.
session:
  cap_usdc: 1.0
  close_delay_ms: 2000 # settle + close after 2s of no deliveries
  modes: [pull] # or [push]
  pull_voucher_strategy: client_voucher
  # Optional: split the settled total across recipients (percentage-only).
  splits:
    - recipient: platform
      percent: 30

endpoints:
  - method: GET
    path: 'api/v1/stream'
    description: 'Metered token stream over an MPP session channel.'
    metering:
      schemes: [mpp-session]
      dimensions:
        - direction: usage
          unit: requests
          scale: 1
          tiers:
            - price_usd: 0.0001 # per-delivery price
pay --sandbox server start provider.yml

On-chain settle-at-close needs an operator signer + RPC. With the CLI the gateway owns the metering side channel and the settlement transaction for you; on --sandbox the operator is provisioned, and in production you set operator.signer + operator.fee_payer: true. Without a signer the close is a state-flip and no on-chain settlement signature is produced.

On the wire

Sessions ride MPP's 402 handshake:

  • WWW-Authenticate: Payment (402) — the session challenge: per-unit amount, channelProgram, feePayer, minVoucherDelta, gracePeriodSeconds, and optional distributionSplits.
  • Authorization: Payment (retry) — a discriminated credential with action: "open" | "voucher" | "topUp" | "close". A voucher carries the 48-byte on-chain structure [channelId | cumulativeAmount | expiresAt] plus the buyer's Ed25519 signature.
  • Payment-ReceiptacceptedCumulative (highest voucher accepted), spent, and on close refunded + the settlement txHash.

Full details: the MPP Solana session method spec.

Next

On this page