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
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" });
});
# 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 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>"
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 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
402round 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)