Payments with MPP
Pay for sessions and streams with the open Machine Payments Protocol on Tenzro Network 1: challenges, credentials, signed vouchers and receipts.
The Machine Payments Protocol (MPP) is an open protocol for machine-to-machine payments over HTTP. It uses the same 402 Payment Required handshake as x402, carried in the IETF Payment HTTP authentication scheme, and adds sessions: a payer funds a session once, then pays for each unit of work with a signed voucher. That makes it the natural choice for streamed inference billed per token, long-running jobs and anything metered. Tenzro nodes accept MPP on every paid route.
Prerequisites
- A wallet with TNZO or stablecoins: a passkey wallet from the console or a delegated agent wallet from Build an AI payment agent.
curlandjq.- Background: MPP.
1. See what the node accepts
curl -s https://rpc.tenzro.xyz \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tenzro_paymentGatewayInfo","params":[]}' | jq .The result lists the payment protocols and assets the node's gateway supports.
2. Receive a challenge
Ask for a paid resource with no credential. The node answers 402 with an MPP challenge in the WWW-Authenticate: Payment header and as a JSON body:
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment id="ch_c41e09", realm="/v1/chat/completions", method="mpp", intent="charge", request="eyJjaGFsbGVuZ2VfaWQiOi...", expires="2026-10-02T12:35:00Z"
Payment-Required: true{
"challenge_id": "ch_c41e09",
"protocol": "mpp",
"resource": "/v1/chat/completions",
"amount": 250000,
"asset": "USDC",
"recipient": "0x<provider-wallet>",
"chain": "<caip2>",
"expires_at": "2026-10-02T12:35:00Z",
"extra": {}
}If you run the resource server yourself on a Tenzro node, you can issue challenges directly. Pass an app_id to add your registered developer margin on top of the network cost; settlement routes the margin to your app wallet.
curl -s https://rpc.tenzro.xyz \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tenzro_createPaymentChallenge","params":{"protocol":"mpp","resource":"/v1/weather","amount":"250000","asset":"USDC","recipient":"0x<your-wallet>"}}'3. Answer with a credential
The credential names the challenge, the payer and the amount, and carries a hybrid classical and ML-DSA-65 signature from the payer's account. Your wallet signs inside its hardware. Send it base64url-encoded in the IETF Payment scheme and retry the request:
CRED=$(base64 -w0 < credential.json | tr '+/' '-_' | tr -d '=')
curl -s https://rpc.tenzro.xyz/v1/chat/completions \
-H 'content-type: application/json' \
-H "Authorization: Payment $CRED" \
-d '{"model":"qwen3.6-35b-a3b","stream":true,"messages":[{"role":"user","content":"Explain rental escrow."}]}'The credential JSON has the same fields as for x402 (credential_id, challenge_id, protocol: "mpp", payer_did, payer_address, amount, asset and the signatures). The node checks the payer's identity and delegation scope before it verifies the payment, so an agent can never pay outside the limits its controller set.
4. Pay per unit with session vouchers
For a stream, the payer opens a session with a deposit and then signs vouchers as work arrives. Each voucher states the cumulative amount spent so far and a nonce:
{
"session_id": "5c0f4c1e-...",
"cumulative_amount": 1200,
"nonce": 48,
"signature": ["...classical..."],
"public_key": ["..."],
"pq_signature": ["...ML-DSA-65..."],
"pq_public_key": ["..."]
}The provider accepts a voucher only if:
- both signatures verify over the session id, cumulative amount and nonce;
- the session is open;
- the nonce is higher than the last one seen, so vouchers cannot be replayed;
- the cumulative amount never goes down;
- the cumulative amount does not exceed the deposit.
When the stream ends, the session closes and only the final cumulative amount settles on the ledger. Many vouchers become one settlement, and the unused deposit goes back to the payer.
With the @tenzro/ai SDK all of this is automatic: streamText with payment: { protocol: "mpp", maxPrice } opens a session per stream, signs vouchers as tokens arrive and closes the session at the end. Pass mppSessionId to keep one session across several calls.
5. Inspect sessions and receipts
tenzro payment sessions --rpc https://rpc.tenzro.xyz
tenzro payment receipt <receipt-id> --rpc https://rpc.tenzro.xyzA receipt records the protocol, challenge and credential ids, amount, asset, chain, settlement transaction, time, and the chain of principals behind the payer.
6. Settle in the asset you want
A provider chooses what it is paid in. keep_inbound keeps whatever asset the payer used, for example USDC; tnzo converts to TNZO. The change is signed by the provider's own identity key, so the node operator cannot change it on the provider's behalf. To check which networks and stablecoins a charge can settle on:
curl -s https://rpc.tenzro.xyz \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tenzro_settlementNetworks","params":{}}' | jq '.networks[] | {caip2, name, native_stablecoins, x402}'Next steps
- One-shot payments: Payments with x402.
- Stablecoins end to end: Pay for inference in stablecoins.
- Protocol reference: MPP.