Agentic AI Foundation Logo
Illustrated robots at service counters depicting MCP server payment flow with gateway, planner, identity gate, and budget authority steps.

402 payment required: what enterprise MCP servers owe the agents that pay them

Marco Gonzalez & Prakash Rao BethapudiSeptember 22, 2026

Two protocol releases seven months apart made a similar move, for related reasons, and the two are usually discussed as if unrelated.

x402 v2 arrived in December 2025, with its HTTP binding putting payment data in HTTP headers. The server states its terms in PAYMENT-REQUIRED on a 402 response, the client returns a signed authorization in PAYMENT-SIGNATURE, and the server reports settlement in PAYMENT-RESPONSE, all three base64 encoded JSON. The core specification is deliberately transport agnostic; those header names come from a separate HTTP binding that sits beside it (x402, 2025; x402 Foundation, 2026a).

MCP 2026-07-28 arrived in July and made a similar move for tool calls. Sessions are gone, along with Mcp-Session-Id and the initialize handshake. The existing MCP-Protocol-Version header identifies the version in use (Model Context Protocol, 2025). The July release adds Mcp-Method and, for tool calls, Mcp-Name to make routing metadata explicit. The release says plainly what this is for: your gateway, rate limiter, or WAF can route and meter on those headers instead of parsing JSON bodies (Model Context Protocol, 2026).

With the HTTP payment binding, intermediaries can inspect routing and payment metadata without parsing JSON-RPC. Headers identify the tool, carry the offered terms, and report the settlement outcome. A trusted enforcement point must still validate that routing headers match the request body; headers do not replace that check or independently prove payment.

That is useful if you operate MCP servers at enterprise scale, and it is also a question. Once the price is visible to your infrastructure, your infrastructure has to decide something about it. This article is about what, and about which component should be deciding.

The exchange, exactly

Here is the whole thing, traced against a working server. This is real output, trimmed for width, from a small reference server we wrote against the v2 specifications to check that what follows actually behaves as described. It is not published. An illustrative excerpt of the resource server payment checks appears at the end of this article, and the official x402 repository provides runnable implementations of both bindings. The excerpt does not reproduce the complete server or the enterprise authorization services discussed below.

Pass one, no payment attached:

POST /mcp
> MCP-Protocol-Version: 2026-07-28
> Mcp-Method: tools/call
> Mcp-Name: text.wordfreq
< HTTP 402 Payment Required
< PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJwYXltZW50IHJlcXVpcmVkIi...
< content-length: 0
  PAYMENT-REQUIRED decoded:
  {
    "x402Version": 2,
    "error": "payment required",
    "resource": {
      "url": "https://tools.example/mcp#text.wordfreq",
      "description": "Word frequency: one call",
      "mimeType": "application/json"
    },
    "accepts": [ {
      "scheme": "exact",
      "network": "eip155:84532",
      "amount": "1100",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "payTo": "0x1111111111111111111111111111111111111111",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USDC", "version": "2" }
    } ]
  }

Four details there are easy to get wrong if your mental model of x402 came from v1 examples. This example leaves the body empty; the HTTP binding carries the terms in the header and leaves response-body content to the implementation. The price field is amount, not v1's maxAmountRequired. And the network is a CAIP-2 identifier, eip155:84532 rather than the string base-sepolia. The resource is now a top-level object of its own rather than a field inside each entry.

Pass two, the same call with a signed authorization:

POST /mcp
> MCP-Protocol-Version: 2026-07-28
> Mcp-Method: tools/call
> Mcp-Name: text.wordfreq
> PAYMENT-SIGNATURE: eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2UiOnsidXJsIjoiaHR0cHM6...
  PAYMENT-SIGNATURE decoded:
  {
    "x402Version": 2,
    "resource": { "url": "https://tools.example/mcp#text.wordfreq", ... },
    "accepted": {
      "scheme": "exact", "network": "eip155:84532", "amount": "1100",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "payTo": "0x1111111111111111111111111111111111111111",
      "maxTimeoutSeconds": 60, "extra": { "name": "USDC", "version": "2" }
    },
    "payload": {
      "signature": "0x107de5941f9feaada623a832e3085e420fe86a89...",
      "authorization": {
        "from": "0xfc9B2F246cFDD54E9853bF315F79BBb0497d4683",
        "to": "0x1111111111111111111111111111111111111111",
        "value": "1100",
        "validAfter": "0",
        "validBefore": "1789306605",
        "nonce": "0x500c0bb222c04671096fd37564c5daf7be2cd31beb8d12ff..."
      }
    }
  }
< HTTP 200 OK
< PAYMENT-RESPONSE: eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6IjB4U0lNVUxBVEVE...

The accepted object is the client echoing back which of the offered terms it chose, which is how v2 makes the selection explicit rather than implied.

What is real here and what is not. The schemas are the v2 schemas. The signature is a genuine EIP-712 signature over a genuine EIP-3009 TransferWithAuthorization struct, verified by recovering the signer independently. Settlement is simulated: the server returns a placeholder transaction id prefixed 0xSIMULATED and never calls a facilitator. In this EIP-3009 exact EVM flow the payer signs the authorization and the resource server verifies it and initiates settlement, with a facilitator broadcasting transferWithAuthorization on chain (x402 Foundation, 2026d). Nothing in this article depends on that last hop, but you should know which part we stubbed.

The same call, in the MCP binding

x402 v2 also defines a binding for MCP specifically, and it does not use HTTP status codes for payment signaling (x402 Foundation, 2026b). The server returns an ordinary tool result with isError: true, carrying the PaymentRequired object in structuredContent and the same object JSON encoded in content[0].text for clients that cannot read structured content:

tools/call  (no payment)
< HTTP 200, JSON-RPC result:
  {
    "isError": true,
    "structuredContent": {
      "x402Version": 2,
      "error": "payment required",
      "resource": { "url": "https://tools.example/mcp#text.wordfreq", ... },
      "accepts": [ { "scheme": "exact", "network": "eip155:84532",
                     "amount": "1100", ... } ]
    },
    "content": [ { "type": "text", "text": "{\"x402Version\":2,\"error\":..." } ]
  }

The client then resends the call with the authorization in _meta["x402/payment"], and settlement comes back in _meta["x402/payment-response"]:

tools/call  (authorization in _meta["x402/payment"])
< HTTP 200, JSON-RPC result:
  {
    "isError": false,
    "content": [ { "type": "text", "text": "[{\"word\":\"the\",\"count\":3}, ...]" } ],
    "_meta": { "x402/payment-response": {
      "success": true, "network": "eip155:84532", "amount": "1100",
      "payer": "0xfc9B...", "transaction": "0xSIMULATED0ccbf123ec3b32f5..."
    } }
  }

Note what this is not. It is not a JSON-RPC error member. The call succeeded at the protocol level and the tool reported a payment condition in its result, which is the distinction that keeps existing MCP clients working.

So which binding should a server speak? This is a transport choice with real trade-offs rather than a correctness question.

HTTP bindingMCP binding
Works overStreamable HTTPany MCP transport, stdio included
Visible to a proxy that reads only headersyesno, it is inside the result
Survives clients that discard non-2xx responsesnoyes
Needs the client to read response headersyesno

The argument in the rest of this article is about where authorization decisions sit, and it holds either way. But if the reason you are pricing tools is so that shared infrastructure can meter and enforce, the HTTP binding is what makes that possible without a body parser in the path. The reference server implements both against the same price, which is the honest way to compare them.

What actually changes when a tool costs money

Before the 402, one component made one decision: the agent chose which tool to call. After it, four decisions exist, and they have different owners.

DecisionWho should make itWhat it looks like when it goes wrong
What work to dothe agent and its modela bad plan, cheaply discarded
Who is askingthe identity provider, checked at the gatewayan unauthenticated or out of scope caller reaching a priced tool
What it coststhe server, from the arguments it receiveda caller that pays less than the work it obtained
Whether this spend is permitteda budget authority the agent cannot influencespending limits that move when the agent is talked into moving them

The fourth row is the one worth dwelling on, and the rest of this article is mostly about it.

The threat model this argument assumes

Security claims mean nothing without saying what they are claims against.

Assumed hostile. The agent process and everything reaching it: its planner, its prompts, its tool outputs, the documents it reads. Assume it can be induced to attempt any request syntactically available to it, including requests that misstate prices, name the wrong payee, or replay an earlier authorization.

Assumed honest but fallible. The gateway, the server, and the policy they enforce. They can be misconfigured. They are not assumed to be running attacker supplied code.

Out of scope. A compromised gateway binary or host, a malicious operator with deploy access, key extraction from an HSM, chain level attacks, and denial of service. Each defeats parts of what follows.

What the separations buy, stated precisely. Every claim below holds when three conditions are met: each check derives its inputs from its own primary source rather than from a value a previous stage passed along, every request path reaches the check with no bypass route, and a failure at any check stops the request. Those are design obligations for the operator, not guarantees the protocols provide. Where a test demonstrates a property under this model we name it. A passing test demonstrates behavior for the cases it exercises; it is not a proof.

Who is asking: what the July release changed

MCP 2026-07-28 hardened authorization in ways that matter more for paid tools than for free ones (Model Context Protocol, 2026). Authorization servers should return the iss parameter per RFC 9207 and clients must validate it before redeeming a code. Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents. Client credentials are bound to the issuer that minted them, with no reuse across authorization servers.

Read that list again with a price attached to every tool call. Issuer confusion is no longer only an authorization risk; it is also a spending risk. A credential that works against the wrong authorization server is a credential that can authorize spending against the wrong budget.

This example uses standalone agentgateway’s simplified MCP configuration, following the 1.5 documentation. Authentication, MCP authorization, and backend credential forwarding have distinct policy fields. The illustrative endpoints must be replaced with the deployment’s issuer, public resource URL, and backend. This is a configuration example, not a Kubernetes AgentgatewayPolicy manifest (agentgateway, n.d.-a, n.d.-b).

mcp:
  port: 3000
  policies:
    mcpAuthentication:
      mode: strict
      issuer: https://id.example/realms/agents
      audiences: [https://tools.example/mcp]
      jwks:
        url: https://id.example/realms/agents/protocol/openid-connect/certs
      resourceMetadata:
        resource: https://tools.example/mcp
    mcpAuthorization:
      rules:
      - 'has(jwt.dept) && jwt.dept == "eng"'
  targets:
  - name: paid-tools
    mcp:
      host: https://resource.internal.example/mcp
    policies:
      backendAuth:
        passthrough: {}

We set mode: strict explicitly to make the authentication requirement visible. Strict is the documented default for this standalone MCP authentication policy. Optional mode permits requests without a token and is unsuitable where every paid-tool caller must be authenticated (agentgateway, n.d.-a).

The backend independently validates the forwarded credential. The target’s backendAuth.passthrough policy re-adds the validated JWT to the backend request. We do not need preserveToken: true for this purpose; it leaves the token available to subsequent policies more broadly. Gateway and backend must both validate the issuer and the intended resource audience. Forwarding does not make a token valid for a different resource (agentgateway, n.d.-c).

The mcpAuthorization rule requires a signed dept claim equal to eng. has(jwt.dept) makes the missing-claim case explicit. This gates access to the target’s tools; it does not itself allocate or authorize a monetary budget. Spending limits remain the responsibility of the separate authorization service described below (agentgateway, n.d.-b).

One limitation to design around: CEL in this position can compare claims and compute hashes, but it does not provide RFC 8785 JSON canonicalization. If your design needs an RFC 8785-canonicalized request digest, the canonicalization step needs to happen in a component that supports it.

What it costs: never a number the caller supplied

The server computes the price from the arguments it received. That sounds obvious until you notice how often the alternative ships, usually as a client SDK that caches the first quote and an execution path that trusts it.

The reference server quotes 1000 base units plus 100 per kilobyte, rounded up, and recomputes the price from the execution request’s arguments. A five-byte input costs 1100 units. Reusing its authorization for a 40,000-byte input, which costs 5000 units, is refused with AMOUNT_MISMATCH. Both the signed authorization value and the selected payment amount must equal the server’s computed price; matching overpayments are refused too (x402 Foundation, n.d.-b).

For a single vendor that is the whole story. Selection across competing vendors is harder: if the agent reports which vendor was cheaper, the agent's report is an input to the decision that trusts it. One alternative is to have the gateway record terms from 402 responses as they pass back through it and select on what the gateway observed. That works, and it costs you a stateful gateway and a witnessing window. Whether the trade is worth it depends on what a wrong selection costs you.

Whether it is permitted: deriving the same answer twice

Where the spend decision is consequential, the pattern that has held up for us is to evaluate it twice, from two different primary sources.

An authorization service canonicalizes the proposed action per RFC 8785, hashes it, evaluates policy, reserves budget, and issues a short lived signed receipt. Every field is inside the signature:

typescript
interface ReceiptPayload {
  v: 1;
  receiptId: string;
  decision: 'permit' | 'deny';
  canonicalDigest: string;   // of the authorized action, not the action itself
  policyVersion: string;
  reservationRef: string;
  issuer: string;
  audience: string;
  issuedAt: string;
  expiresAt: string;
}

An enforcement point on the egress path then re-derives the canonical action from the bytes it is actually about to send, recomputes the digest, and compares. It imports no digest, no decision, and no terms from the caller.

The property, within the threat model above: an actor who can modify the request after authorization, but who cannot reach the enforcement point's policy copy or bypass it, cannot get the modified request through even holding a genuine and correctly signed receipt. Refusal comes from divergence between two independent derivations, not from recognizing the tampering as such.

What it does not do deserves equal clarity. It does not help if the policy is wrong in both places, since both evaluate the same policy. It does not help if the enforcement point can be bypassed, which is a deployment property and a common way this pattern fails in practice. And it does not survive a compromised enforcement point, which is out of scope above.

The receipt carries a digest rather than the action, which costs something: when enforcement refuses, it can say that something diverged but not which field. That is the intended trade. A receipt carrying the action would disclose what was approved to anyone who intercepted one, and would give the enforcement path a second source of truth to be confused by.

Approval and signing belong in different components

A gate evaluates the identity assertion against a bound claim constraint and, on a permitting evaluation, issues an authorized signing request carrying a ceiling. A separate signing service holds the key and evaluates its own constraints. If the gate approves a ceiling of 500,000 and the signer's own limit is 100,000, a request to sign 400,000 is refused by the signer. The gate's ceiling acts only as an additional upper bound, never as a grant.

In our implementation the gate module contains no signing key, no signing primitive, and no import that could produce one, and a test asserts this by scanning the module's own source and exercising its exports. We want to be careful about what that establishes. It is a regression guard: it catches the commit where someone adds a signing dependency to the wrong module. It does not establish that the deployed process is incapable of signing, which depends on the full dependency tree, the runtime, and the host, none of which a source scan sees. Treat it as a tested invariant with a stated scope, not an impossibility proof.

The same reasoning covers the payee, constrained in three places with no shared code path: at the gate, at the signer, and at the resource. The value of the repetition is that one misconfiguration does not silently redirect payment. The third is the one the code below demonstrates: an authorization naming a different to is refused regardless of what the client was told.

What the server owes: five obligations

  1. Validate the price against the work requested. Recompute it from the current arguments, as this example does, or validate an earlier quote and its binding to the resource, arguments, terms, and validity window. Never trust a client-supplied price without those checks.
  2. Make discovery free. tools/list should not cost anything, or agents cannot shop before they buy.
  3. Reject replay explicitly. Track authorization use atomically across every serving instance, retain records for the authorization validity period, and define how retries recover after settlement or delivery failure.
  4. Enforce the payee locally. Your own address, from your own configuration, regardless of what the authorization names.
  5. Say which binding you speak. HTTP or MCP, and which clients you have actually tested against.

And one shared obligation for gateway and server operators: check that Mcp-Method and Mcp-Name agree with the body behind them. Metering on headers is only sound if the body cannot contradict them, and a server that accepts a request whose header advertises one tool while the body calls another has quietly invalidated everyone's dashboards. Refuse those before pricing happens.

Reachability is not the same as authority

We have argued for putting the budget authority somewhere the agent cannot reach, and network isolation is the easiest way to picture that. But isolation is one control, not the definition of an enforceable limit. A service the agent can reach, over an API the agent can call, can still enforce a budget, provided the agent's credential authorizes it to request spending and not to change what it is allowed to spend.

The questions that actually decide whether a limit is a control are narrower than reachability:

  • Can the agent's credential modify the limit, or only consume against it?
  • Can the agent forge or replay an approval, or obtain one it was not issued?
  • Can the agent reach the priced resource on a path where enforcement does not run?

Within the stated threat model, those answers help establish an enforceable limit whether or not the agent can open a socket to the service. They also require correct policy evaluation and atomic budget accounting. Network isolation can reduce access to vulnerable paths, but it does not replace those authorization controls.

x402 v2 gives you a way to state a price and carry an authorization. MCP 2026-07-28 exposes routing metadata in headers; the x402 HTTP binding exposes payment metadata alongside it. Neither gives you a decision about whose budget is being spent and under what limit. That decision is yours to place, and where you place it is the part that will still matter after both specifications have moved on again.

The part worth copying

The resource server payment checks below illustrate one part of the design. They do not implement organizational identity, budget reservation, receipt and action binding, or independent signer limits. Those controls remain separate.

This abbreviated JavaScript class method uses getAddress, isHexString, and verifyTypedData from ethers. It assumes the standard EIP-3009 TRANSFER_WITH_AUTHORIZATION_TYPES definition, validated local configuration for the chain, asset, EIP-712 domain, payee, and maxTimeoutSeconds (60 here), and an initialized this.seenNonces set. It is an explanatory excerpt, not a standalone server or the verbatim implementation covered by the local test suite.

javascript
verifyPayment(payment, expectedAmount) {
  const refuse = (code) => ({ ok: false, code });
  const uint = (value) => {
    if (typeof value !== 'string' || !/^[0-9]+$/.test(value)) {
      throw new TypeError('Expected an unsigned decimal string');
    }
    const n = BigInt(value);
    if (n >= (1n << 256n)) throw new RangeError('uint256 overflow');
    return n;
  };
javascript
  try {
    if (payment?.x402Version !== 2) {
      return refuse('UNSUPPORTED_VERSION');
    }
    const accepted = payment.accepted;
    const auth = payment.payload?.authorization;
    const signature = payment.payload?.signature;
    if (!accepted || !auth || !isHexString(signature, 65)) {
      return refuse('MALFORMED');
    }
    if (accepted.scheme !== 'exact') return refuse('UNSUPPORTED_SCHEME');
    if (accepted.network !== this.config.network) {
      return refuse('WRONG_NETWORK');
    }
    if (getAddress(accepted.asset) !== getAddress(this.config.asset)) {
      return refuse('WRONG_ASSET');
    }
javascript
    const from = getAddress(auth.from);
    const payee = getAddress(this.config.payTo);
    if (getAddress(auth.to) !== payee ||
        getAddress(accepted.payTo) !== payee) {
      return refuse('WRONG_PAYEE');
    }
javascript
    const expected = uint(String(expectedAmount));
    const authorized = uint(auth.value);
    const selected = uint(accepted.amount);
    if (authorized !== expected || selected !== expected) {
      return refuse('AMOUNT_MISMATCH');
    }
    if (!isHexString(auth.nonce, 32)) return refuse('MALFORMED');
javascript
    const nonceKey = [this.config.chainId,
      getAddress(this.config.asset), from,
      auth.nonce.toLowerCase()].join(':');
    if (this.seenNonces.has(nonceKey)) return refuse('REPLAY');
javascript
    const now = BigInt(Math.floor(Date.now() / 1000));
    const validAfter = uint(auth.validAfter);
    const validBefore = uint(auth.validBefore);
    if (validAfter > now) return refuse('NOT_YET_VALID');
    if (validBefore <= now) return refuse('EXPIRED');
    if (validBefore < now + 6n) {
      return refuse('INSUFFICIENT_SETTLEMENT_TIME');
    }
javascript
    // Additional local policy, assuming aligned clocks.
    const timeout = this.config.maxTimeoutSeconds; // 60 in this example
    if (accepted.maxTimeoutSeconds !== timeout) {
      return refuse('TIMEOUT_MISMATCH');
    }
    if (validBefore > now + uint(String(timeout))) {
      return refuse('TIMEOUT_TOO_LONG');
    }
javascript
    let recovered;
    try {
      recovered = verifyTypedData(
        { name: this.config.assetName, version: this.config.assetVersion,
          chainId: this.config.chainId,
          verifyingContract: this.config.asset },
        TRANSFER_WITH_AUTHORIZATION_TYPES,
        { from: auth.from, to: auth.to, value: auth.value,
          validAfter: auth.validAfter, validBefore: auth.validBefore,
          nonce: auth.nonce },
        signature,
      );
    } catch {
      return refuse('BAD_SIGNATURE');
    }
    if (getAddress(recovered) !== from) return refuse('BAD_SIGNATURE');
typescript
    this.seenNonces.add(nonceKey);
    return { ok: true, payer: from, amount: String(auth.value) };
  } catch {
    return refuse('MALFORMED');
  }
}

Timeout validation has two layers. The reference EIP-3009 client sets validBefore to its current time plus maxTimeoutSeconds; validAfter is zero. The reference verifier rejects a future validAfter and requires at least six seconds before expiry. It does not impose a maximum validBefore-minus-validAfter interval, which would be inappropriate for a zero start timestamp (x402 Foundation, n.d.-a, n.d.-b).

The excerpt adds an explicit local policy: with aligned clocks, the remaining lifetime may not exceed the server’s configured 60 seconds, and the echoed timeout must match that configuration. This cap is an operator choice, not a universal x402 requirement or proof of when an offer was issued. Deployments needing clock-skew tolerance or offer-anchored expiry must define those rules and bind the deadline to trusted offer data.

Read it as a list of the ways a paid call can be wrong, in the order that costs least to discover. Version, then shape, then scheme and network, then the asset, then the payee from local configuration rather than from the payment, then the amount against a quote recomputed from the arguments in hand, then nonce shape, then replay, then the validity window, and only then signature recovery. The cheaper validation checks run before signature recovery, so malformed payments are rejected before that cryptographic work.

Two checks express the resource server obligations. The payee comes from local configuration and is compared with both payment fields. The server computes expectedAmount from the current request arguments, so execution does not depend on trusting the client's earlier quote.

The in-memory set only demonstrates replay rejection within one running process. It loses records on restart, is not shared across instances, and has no expiry cleanup. A deployed service needs a shared atomic record keyed by chain, asset, authorizer, and nonce, retained through the authorization validity period. It must track pending and completed settlement and define idempotent retry behavior so a failed delivery does not cause a second charge or prevent recovery of a paid result.

Signature recovery establishes who signed these terms; it does not establish that funds are available or that the authorization has not already been used on chain. Verification and settlement must check the relevant chain state before the service treats the payment as successful. The excerpt is limited to standard EOA signatures and does not cover smart-contract wallet validation.

For runnable code, the official x402 repository carries reference implementations and SDKs for both bindings, and the specifications linked below are short enough to read end to end (x402 Foundation, 2026c). If you are building a paid MCP server, read the MCP binding before you reach for HTTP status codes, and decide between them deliberately rather than by which one your framework makes easier.

References

agentgateway. (n.d.-a). MCP authentication.
https://agentgateway.dev/docs/standalone/latest/documentation/configuration/security/mcp-authn/

agentgateway. (n.d.-b). MCP authorization.
https://agentgateway.dev/docs/standalone/latest/documentation/configuration/security/mcp-authz/

agentgateway. (n.d.-c). Static keys and passthrough.
https://agentgateway.dev/docs/standalone/latest/documentation/configuration/security/backend-authn/key/

Model Context Protocol. (2025). Transports.
https://modelcontextprotocol.io/specification/2025-06-18/basic/transports

Model Context Protocol. (2026, July 28). The 2026-07-28 specification.
https://blog.modelcontextprotocol.io/posts/2026-07-28/

x402. (2025, December 11). Introducing x402 v2: Evolving the standard for internet-native payments.
https://x402.org/x402-v2-launch/

x402 Foundation. (n.d.-a). EIP-3009 client [Computer software].
https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/client/eip3009.ts

x402 Foundation. (n.d.-b). EIP-3009 facilitator [Computer software].
https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/facilitator/eip3009.ts

x402 Foundation. (2026a). HTTP transport binding for x402 v2 [Specification].
https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md

x402 Foundation. (2026b). MCP transport binding for x402 v2 [Specification].
https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/mcp.md

x402 Foundation. (2026c). x402: A payments protocol for the internet [Computer software].
https://github.com/x402-foundation/x402

x402 Foundation. (2026d). x402 specification v2 [Specification].
https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md

Share

Authors

subscription section bg
Subscribe

Subscribe to the AAIF Briefing

Weekly signal on standards, governance, and the people building the future. No fluff. Just what matters.

About AAIF