x402 vs MPP: what we learned building both
We run small pay-per-call services that accept both x402 and MPP. We settle them in one handler, because both credentials are an EIP-3009 transferWithAuthorization signature on USDC. The headers, the nonce rule, and the binding differ. The signature does not. This post covers what the two protocols have in common, what bit us, and what our traffic says about them.
What x402 and MPP are
Both build on HTTP 402. The client requests a resource, the server answers 402 with a challenge, the client sends a payment credential, and the server settles it and then returns the resource. The specs are the x402 spec and the MPP spec.
x402 puts the challenge in PAYMENT-REQUIRED as base64 JSON, the credential comes back in PAYMENT-SIGNATURE, and success is PAYMENT-RESPONSE.
MPP puts the challenge in WWW-Authenticate: Payment, the credential is Authorization: Payment plus a base64url payload, and success is Payment-Receipt.
A WorkOS comparison says x402 is stateless per request, and that MPP adds sessions: pre-authorized spend, streamed payments, and batch settlement. It also says MPP has been submitted to the IETF, that MPP carries a Stripe and Tempo dependency, and that MPP can express an x402-style one-shot payment.
A TRM Labs analysis, as reported, found only roughly 0.6 to 7.5 percent of screened x402 volume plausibly agentic. Artemis, as reported, estimated about half of x402 transactions were artificial. Coverage of BlackRock's machine-native economy paper says the paper discussed x402 and stablecoins. Fortune reported that Cloudflare announced tooling to charge agents in USDC.
What we built
One handler settles both. Both credentials are an EIP-3009 transferWithAuthorization signature on USDC.
The server tells them apart by the Authorization header. If the first token is Payment, the request is MPP. Otherwise the server looks at the x402 headers.
Each route is served under /x402/<name> and /mpp/<name>. Both paths accept either credential.
The idempotency key we use is:
protocol:eip3009:network:asset:payer:nonce
In our implementation MPP is Base only. x402 also runs on Polygon and Solana. Settlement for Base x402 goes through a facilitator (Coinbase CDP). MPP, and x402 on the other networks, we settle ourselves by sending the transferWithAuthorization transaction.
This is the comparison from our spec.
| Field | x402 | MPP |
|---|---|---|
| Challenge header | PAYMENT-REQUIRED (base64 JSON) | WWW-Authenticate: Payment |
| Credential header | PAYMENT-SIGNATURE | Authorization: Payment plus base64url |
| Success header | PAYMENT-RESPONSE | Payment-Receipt |
| EIP-3009 nonce | Client-chosen random 32 bytes | Must be keccak256 of the challenge id and the realm, binding the payment to one challenge |
| Challenge integrity | None. The server recomputes the price every time | HMAC over the challenge fields |
| Body binding | None | Content-Digest of the request body, bound into the HMAC, so a credential cannot be replayed against a different query |
x402 leaves the nonce as 32 random bytes chosen by the client. It does not bind the challenge. It does not bind the body. MPP requires the nonce to be keccak256 of the challenge id and the realm, so the payment is bound to one challenge. Its HMAC covers the challenge fields. The same HMAC covers a Content-Digest of the request body, so a credential cannot be replayed against a different query.
Things that bit us
A facilitator timeout during settle leaves the outcome ambiguous. The authorization nonce was never used on chain, nothing was charged, and a retry works. The server must not deliver until settlement is confirmed. Ambiguous means not delivered.
An overflowing validAfter value passed a naive check but could never settle. That is a free-scrape attack. Numbers must be strict canonical decimals.
The price must be recomputed server side on every request.
An empty or failed result must not be charged.
What the traffic says
In our traffic, about 96 of every 100 payment challenges our servers issued were on /x402/ paths and about 4 were on /mpp/ paths, about 22 to 1. Settled payments split the same way. In absolute terms, MPP payments were a handful.
Per challenge issued, the share that ended in a paid call was about the same for both, roughly 0.3 percent. MPP's gap is mostly in how many callers arrive, not in whether they pay once they do.
About 94 of 100 payment attempts were on Base. Polygon and Solana together were a few percent.
Why MPP lags
We do not know why fewer callers show up for MPP. These are guesses.
- Discovery catalogs and scanners mostly index x402.
- Client libraries and wallets ship x402 first.
- The first-party tooling most agents use already speaks x402.
- MPP's headline feature is sessions. Sessions matter for streaming or per-token billing, which a pay-per-call service does not need. WorkOS describes them as pre-authorized spend, streamed payments, and batch settlement.
- The Stripe and Tempo coupling. WorkOS says MPP carries that dependency.
What we would do as a seller
For a pay-per-call service we would ship x402 first. We would keep the MPP one-shot path in the same handler. The credential is the same transferWithAuthorization on USDC. Both paths already accept either credential. One charge per call does not need a session.
We would list the /x402/ routes where the x402 discovery catalog and the scanners look. We would list the /mpp/ routes wherever MPP is indexed.
Base x402 would keep going through the facilitator (Coinbase CDP). MPP, and x402 on Polygon and Solana, we would keep settling ourselves by sending the transaction.
If we later billed per token, or charged during a stream, we would look at MPP sessions then. That is new work.
Caveats
The sample is small and includes our own test purchases and automated checkers. Path is not protocol: both /x402/<name> and /mpp/<name> accept either credential, so a path hit is not the protocol the client chose. The x402 paths are listed in the discovery catalog and by scanners, the /mpp/ paths are listed in fewer places, and we have not measured that bias, so we call it likely. Path share partly measures discoverability. We built MPP's one-shot charge intent, the EVM charge flow, not its sessions. The TRM Labs, Artemis, WorkOS, BlackRock, and Cloudflare claims below are theirs.
Sources
- x402 spec
- MPP spec
- WorkOS, on stateless x402, MPP sessions, the IETF submission, the Stripe and Tempo dependency, and one-shot payments
- TRM Labs analysis, as reported, on screened x402 volume that looked plausibly agentic
- Artemis estimate, as reported, that about half of x402 transactions were artificial
- Coverage of BlackRock's machine-native economy paper, which discussed x402 and stablecoins
- Cloudflare's announcement of tooling to charge agents in USDC