---
title: "MPP vs x402: Two Competing Answers to the Same HTTP 402"
description: "AgentCore Payments speaks both protocols natively. I tested x402's metered settlement and built a spec-compliant MPP challenge to compare what each actually buys: simplicity and on-chain audit trails versus multi-rail reach and high-frequency billing."
canonical_url: "https://artificialcuriositylabs.ai/posts/mpp-vs-x402-agent-payments/"
md_url: "https://artificialcuriositylabs.ai/posts/mpp-vs-x402-agent-payments.md"
published_at: "2026-08-19T07:00:00.000Z"
tags:
  - "agents"
  - "agentcore"
  - "bedrock"
  - "payments"
  - "protocols"
---

Two protocols answer HTTP's thirty-year-old, never-implemented `402 Payment Required` status code, and [Amazon Bedrock AgentCore Payments](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments.html) speaks both. x402 (Coinbase) and MPP (Stripe and Tempo) are direct competitors for the same job: an agent hits a paywalled resource and pays for it inline, no API key, no billing account set up in advance. They disagree about what "pay" is allowed to mean.

```mermaid
sequenceDiagram
    participant A as Agent
    participant M as Merchant
    participant AC as AgentCore Payments
    A->>M: GET /resource
    M-->>A: 402 Payment Required + challenge
    A->>AC: ProcessPayment(challenge)
    AC->>AC: check session budget, sign with wallet
    AC-->>A: signed proof / credential
    A->>M: retry + proof (x402: PAYMENT-SIGNATURE, MPP: Authorization)
    M-->>A: 200 OK
```

Both protocols follow that identical outer loop. Where they diverge is what each one chooses to standardize.

## Same shape, different commitments

| | x402 | MPP |
|---|---|---|
| Settlement | On-chain only — stablecoins, primarily USDC on Base | Multi-rail — Tempo stablecoins, Stripe cards/ACH/BNPL via Shared Payment Tokens, Bitcoin via Lightning |
| Retry header | `PAYMENT-SIGNATURE` (v2) or `X-PAYMENT` (v1) | `Authorization: Payment <base64url-token>` |
| Pricing model | `exact` (fixed price) and `upto` (metered, settle-for-less-than-authorized) | Charge intent, plus a session primitive for high-frequency metered billing |
| Facilitator | Required — a third party verifies and settles the on-chain transfer | Framed as a formal HTTP auth scheme; some methods avoid a separate facilitator dependency |
| Transport | HTTP only | HTTP, plus an MCP transport binding — MCP tool servers can charge per tool call directly |
| Backing | Coinbase, fully open and permissionless | Stripe + Tempo, with Visa and Lightspark extending it to cards and Lightning |

[AgentCore's release notes](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/release-notes.html) describe x402's `exact` and `upto` schemes and MPP as three parallel options behind one [`ProcessPayment`](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-process-payment.html) API — you pick `paymentType` (`CRYPTO_X402` or `MPP`) per call, and the wallet, budget check, and signing path are identical underneath.

## x402's `upto` scheme: pay for what you actually used

`upto` lets a merchant advertise a *ceiling* instead of a fixed price. The buyer's wallet authorizes a [Permit2](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-process-payment.html) allowance up to that ceiling, and the merchant settles for whatever was actually consumed — no larger than the ceiling, no second round trip. That's what makes per-token LLM inference or metered compute billable in a single request/response instead of a price negotiation.

I proved this by extending a local x402 merchant with `UptoEvmScheme` and a `Settlement-Overrides` response header carrying the real charge:

```javascript
// merchant route: authorize up to a ceiling, settle for less
app.get("/metered-recap", (_req, res) => {
  res.setHeader(
    "Settlement-Overrides",
    JSON.stringify({ amount: "1000" }),  // actual metered charge
  );
  res.json({ status: "paid", settledUnits: "1000" });
});
```

```python
# buyer: authorize a ceiling, let the merchant settle lower
payment = client.process_payment(
    ...,
    paymentType="CRYPTO_X402",
    paymentInput={"cryptoX402": {
        "version": "2",
        "payload": accept,
        "permit2AllowanceLimit": accept["amount"],  # the ceiling
    }},
)
```

`ProcessPayment` authorized a ceiling of `15000` base units on Base Sepolia testnet; the merchant declared an actual charge of `2000`. Checking the wallet's on-chain balance immediately before and after confirmed it moved by exactly `2000`, not `15000` — real settlement tracked real usage, not the authorization.

The gotcha: the `ProcessPayment` X-Ray span's `payments.spend_amount` attribute reads the *ceiling*, not the settled amount, because AgentCore signs the authorization before the merchant declares what it actually consumed. A spend dashboard built on that [span attribute](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-observability.html) alone will overstate real spend for `upto` transactions — reconcile against actual settlement, not the span, to get the true number.

## MPP: one interface, four rails behind it

MPP standardizes the challenge-response *interface* and lets the merchant advertise whichever payment methods it accepts — `evm`, `tempo`, `solana`, and through Stripe, card and fiat — inside one `WWW-Authenticate: Payment` header, per the [mpp-specs](https://github.com/tempoxyz/mpp-specs) IETF draft co-authored by Tempo and Stripe.

No live MPP-compatible merchant exists anywhere yet to round-trip against — not from AWS, not in Coinbase's Bazaar. To prove AgentCore's side of the handshake, I built the smallest thing that could: a synthetic, spec-compliant `evm`-method challenge.

```
WWW-Authenticate: Payment id="<unique-id>", realm="<realm>",
  method="evm", intent="charge",
  request="<base64url JSON: amount, currency, recipient, methodDetails.chainId>"
```

```python
payment = client.process_payment(
    ...,
    paymentType="MPP",
    paymentInput={"mpp": {
        "version": "1",
        "wwwAuthenticateHeaders": [header_value],  # forwarded verbatim
    }},
)
credential = payment["paymentOutput"]["mpp"]["paymentCredential"]
# -> "Payment <base64url-token>", ready to attach as Authorization
```

Two checks confirmed AgentCore genuinely parsed the challenge rather than rubber-stamping it: the session's available budget dropped by exactly the amount declared in the request (`0.02` → `0.019` USD for a `$0.001` charge), and the returned credential was a well-formed, 1176-character token with the documented `Payment ` prefix. That's genuine credential generation, matching the [`ProcessPayment` response schema](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-process-payment.html) exactly. What it doesn't prove is settlement — there's nothing real to settle against yet.

## When to reach for which

- **x402** when buyers already hold stablecoins and you want the simplest integration, a full on-chain audit trail, and sub-cent transaction costs — paywalled content, one-off compliance lookups, real-time market data.
- **MPP** when your buyer population doesn't hold crypto, or you need card/ACH reach alongside stablecoins, or you're billing something metered and high-frequency where MPP's session primitive fits better than repeated `402` round trips.

AgentCore Payments doesn't make you choose between them. It abstracts the protocol behind one API, so the decision moves from "which protocol do I integrate" to "which protocol does this merchant accept" — which is the right place for that decision to live.

## The open thread

Full settlement against a live MPP merchant hasn't been tested anywhere yet — no public merchant exists that accepts MPP payments through the complete cycle. I've proved that AgentCore can generate valid credentials and that budget tracking works, but settling a real charge against a real merchant and confirming the merchant received payment remains untested. This is a genuine gap, not a missing step in the documentation. Once live MPP merchants exist in production, the full picture — credential generation, settlement success, reconciliation — will be available to verify.
