DEV Community

Amit
Amit

Posted on Originally published at artificialcuriositylabs.ai

MPP vs x402: Two Competing Answers to the Same HTTP 402

Two protocols answer HTTP's thirty-year-old, never-implemented 402 Payment Required status code, and Amazon Bedrock AgentCore Payments 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.

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
Enter fullscreen mode Exit fullscreen mode

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 describe x402's exact and upto schemes and MPP as three parallel options behind one ProcessPayment 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 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:

// 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" });
});
Enter fullscreen mode Exit fullscreen mode
# 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
    }},
)
Enter fullscreen mode Exit fullscreen mode

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 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 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>"
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

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.020.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 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.

Top comments (0)