AttestSeal Blog

x402 and the New Role of Payment Protocols

Coinbase x402 turned HTTP 402 from a status code into a real wire protocol for machine payments. We extended it with cryptographic trust headers so agents can verify a merchant before paying, in the same packet, with no extra round-trip.

x402 and the New Role of Payment Protocols

For twenty-five years HTTP 402 Payment Required was the status code that nothing returned. The standard reserved it; no implementation actually used it. The reason was practical: HTTP was an open hypertext network where the human at the keyboard handled payment out-of-band, in a different tab, on a different page, with a different protocol entirely.

That assumption broke the moment software started clicking the buttons. An autonomous agent doesn’t pivot to a different tab. It cannot pivot. It needs the resource and the price quote in the same response that says “you cannot have this without paying,” because if those three pieces aren’t co-located the agent has to take a leap of faith between fetching the price and fetching the goods, and a leap of faith is exactly what an agent shouldn’t be taking.

Coinbase published x402 to fix this. A 402 response under x402 carries an Accept-Payment header that names the payment scheme, the amount, the currency, and the destination. The agent reads the header, sends a payment over the named rail, retries the request with proof of payment, and receives the resource. The whole exchange is mechanical. The 402 stops being a placeholder and becomes a real wire protocol.

Stripe shipped a parallel design (Machine Payments Protocol). Skyfire wrapped both with a KYC-bonded identity layer. AP2 emerged. The shape converged: payment becomes part of the request lifecycle, not a separate jurisdiction the agent has to step out into.

This is good. It also exposes a hole that the human-pays world used to paper over: the agent has to decide whether to pay this 402 in the milliseconds between reading the price and authorizing the payment. And there’s nothing in any of these protocols that helps it.

The hole, in concrete terms

Consider an agent that has been asked to buy access to a research paper. It searches, finds a candidate URL, fetches it, gets back:

HTTP/1.1 402 Payment Required
Content-Type: application/json
Accept-Payment: x402; scheme=base-usdc; amount=2.50

{
  "scheme": "x402",
  "amount": "2.50",
  "currency": "USD",
  "destination": "0x...",
  "resource": "the paper PDF after payment"
}

The agent now has to answer one question: should I pay 2.50 to this destination?

The protocol gives the agent: the merchant’s domain, the price, and the wallet address of where the money goes. That’s it. No information about whether this domain is six days old or six years old. No information about whether the SSL chain is valid. No information about whether the domain is currently on Spamhaus DBL. No information about whether anyone in the world has ever heard of this site.

The agent has three options. It can pay anyway. It can refuse anyway. Or it can synchronously call out to a separate trust-scoring API, wait for a response, and decide based on that. The first is reckless. The second turns the agent into a dead-letter office for any merchant the agent doesn’t pre-recognize. The third is what most teams have ended up implementing, but it doubles the latency of every payment, makes the agent’s commerce path depend on a third-party uptime, and bolts a critical trust check onto the side of a protocol that wasn’t designed to compose with it.

We can do better.

Putting the trust into the 402

The fix is to attach a signed trust attestation directly to the 402 response. The 402 response is already an envelope the agent has to open and parse. Adding a few headers to that envelope is free in bandwidth, free in latency, and structurally tied to the payment challenge — meaning an attacker cannot strip the trust signal without also breaking the payment challenge it’s bound to.

The headers AttestSeal defined for this look like:

HTTP/1.1 402 Payment Required
Accept-Payment: x402; scheme=base-usdc; amount=2.50
X-AttestSeal-Issuer: did:web:attestseal.com
X-AttestSeal-Domain: research-archive.example
X-AttestSeal-Score: 78
X-AttestSeal-Recommendation: PROCEED
X-AttestSeal-Confidence: high
X-AttestSeal-Assurance-Basis: earned_proceed
X-AttestSeal-Issued-At: 2026-05-02T14:00:00Z
X-AttestSeal-Expires-At: 2026-05-09T14:00:00Z
X-AttestSeal-Scoring-Model: attestseal-v1.5.1-weights
X-AttestSeal-Signature: M4OqKvuOiu...
X-AttestSeal-Key-Id: did:web:attestseal.com#signing-key-1

The agent’s flow is now:

  1. Read the 402.
  2. Verify the X-AttestSeal-Signature against the cached DID document at attestseal.com/.well-known/did.json. This is local; no network call.
  3. Confirm X-AttestSeal-Domain matches the host of the URL that returned the 402. (This prevents an attacker from copying a legitimate attestation for one domain into a 402 from another.)
  4. Confirm the attestation hasn’t expired.
  5. Apply policy based on X-AttestSeal-Recommendation and X-AttestSeal-Assurance-Basis.

In the happy path, the entire trust check costs one Ed25519 signature verification (a few hundred microseconds on a modern CPU) and zero network round-trips. The agent goes from “payment requested” to “payment decision” without ever consulting AttestSeal’s API.

If the headers are absent, the signature is invalid, or the attestation is expired, the agent falls back to a direct API call to AttestSeal. Same outcome, plus one round-trip. The protocol degrades gracefully; it never fails closed at the cost of a partner’s bad implementation.

Why this is interesting beyond AttestSeal

The deeper move here is that the 402 stops being just a payment gate and becomes a trust-and-payment gate. The merchant is signaling not just “here’s the price” but “here’s the price and here’s a verifiable claim about who I am, signed by an independent attestation authority.” A protocol-aware agent can choose to honor only 402s that carry a recognized issuer’s headers and refuse the rest, the same way a browser today refuses to load a page over HTTPS if the cert chain doesn’t validate.

That’s a significant shift in what the 402 response means. It’s no longer a request for money; it’s a payment-and-credential exchange. The ergonomics for merchants are good: get a free Cloudflare Worker, point it at your origin, your 402s now carry your AttestSeal attestation automatically. The ergonomics for agents are good: one signature verify per 402, no separate trust API to depend on. The ergonomics for AttestSeal are good: we become protocol infrastructure rather than an external API in the critical path.

And the ergonomics for the network as a whole are good, because trust gets composable. An agent can layer policies: “I will pay any 402 with X-AttestSeal-Recommendation=PROCEED and X-AttestSeal-Assurance-Basis=well_known_tranco_anchor up to $5; any with assurance_basis=registered_proceed up to $1; any with assurance_basis=kyc_verified up to $50; nothing else without confirming with the user.” That policy is local, transparent, and the agent’s developer can audit it. No part of it depends on AttestSeal answering an API call at the moment of payment.

What this is not

A few things this proposal explicitly does not try to do.

It does not replace the payment protocol. Accept-Payment, the x402 challenge body, the AP2 manifest URL — those all stay exactly where they are. The trust headers sit alongside.

It does not replace KYC. A merchant whose attestation says assurance_basis=well_known_tranco_anchor is, with very high probability, the real public entity at that domain. That is not the same as saying the merchant has been business-license-verified, identity-verified, or bank-verified. Agents that need stronger guarantees should require assurance_basis=kyc_verified, which AttestSeal issues only after a full identity-verification flow with the operator.

It does not assume one trust authority. The DID-based architecture means another trust attester can stand up did:web:competitor.example tomorrow and ship X-Competitor-* headers using exactly the same mechanism. Agents authenticate per-issuer; multiple issuers can co-exist. We expect (and welcome) that, because trust attestation is the kind of market that benefits from open competition.

How to participate

If you are building an x402-aware server: drop the attestseal-x402 Python package into your middleware stack. It auto-stamps your 402 responses with the right headers based on your domain. There’s a Cloudflare Worker version for edge deployment. Both are MIT-licensed.

If you are building an x402-aware client: the same package ships a verifier. Configure your allow-list of issuer DIDs and your transaction-limit policy per assurance basis, and the verifier handles the rest.

If you are an agent runtime maintainer: consider whether you want trust verification to be a default. The cost is one Ed25519 verify per payment. The benefit is your agents stop sending money to phishing kits. We’re happy to help integrate.

If you have feedback on the spec itself: it’s at github.com/AttestSeal/attestseal/blob/main/spec/X-ATTESTSEAL-HEADERS.md. Open an issue, file a PR, or comment on the Coinbase x402 GitHub Discussion where we proposed it.

The protocol is young. The patterns aren’t set. This is the moment to argue about the shape of the headers, the right cache TTL, the canonical signing form, and whether the optional fields should become required. Two months from now the cement will be poured. Until then, pull requests welcome.


Read next: Public Legitimacy Is Not Merchant Trust, How to Read an AttestSeal Attestation, or the X-AttestSeal-* Header Specification.