Migrating from x402 V1 to V2

x402 V2 keeps the same handshake but changes almost every name on the wire. This guide lists the differences from the specifications, then the steps for resource servers, clients and facilitators, and how to confirm an endpoint really speaks V2.

In short

x402 V2 moves the payment terms from the 402 response body into a Base64 PAYMENT-REQUIRED header, renames X-PAYMENT to PAYMENT-SIGNATURE and X-PAYMENT-RESPONSE to PAYMENT-RESPONSE, replaces maxAmountRequired with amount and identifies networks in CAIP-2 form, such as eip155:84532.

On this page

The x402 V2 core specification dates protocol version 2 to December 9, 2025. The payment still happens in one HTTP round trip: the server answers 402 with its terms, the client signs and retries, a facilitator verifies and settles. What changed is where each message travels and what its fields are called, so a V1 client and a V2 server do not understand each other. If the flow itself is new to you, read What is x402? first.

Checked on Oct 13, 2026

The differences below come from the V1 and V2 core specifications and their HTTP transports in the x402-foundation/x402 repository. SDK and facilitator support for V1 changes faster than the specifications: check your vendor's documentation for dates.

What changed on the wire

Wire-level differences between x402 V1 and V2 (HTTP transport)
AspectV1V2
Payment termsJSON body of the 402 responsePAYMENT-REQUIRED header, Base64 JSON
Client payment headerX-PAYMENTPAYMENT-SIGNATURE
Settlement headerX-PAYMENT-RESPONSEPAYMENT-RESPONSE
Network identifiersNames such as base-sepoliaCAIP-2, such as eip155:84532
Amount fieldmaxAmountRequiredamount
Resource descriptionresource URL inside each requirementOne top-level resource object
Chosen option in the payloadScheme and network onlyThe full requirement, in accepted
Extensionsextensions object

Beyond the headers

Other differences between the V1 and V2 core specifications
AreaV1V2
error in the 402 termsRequiredOptional
Resource metadataDescription required, MIME type and output schema optional, in each requirementOne ResourceInfo object: URL required; description, MIME type, service name, tags and icon optional
Reserved extra keysNoneassetTransferMethod and paymentFlow
Facilitator GET /supportedkinds onlykinds, extensions and signers, all required
SettleResponsepayer requiredpayer optional, amount and extensions added

The error codes changed too. The V1 code for a wrong amount, invalid_exact_evm_payload_authorization_value, meant "amount insufficient"; V2 replaces it with invalid_exact_evm_payload_authorization_value_mismatch, "amount not an exact match". V2 also adds settlement_pending, the only code the specification marks non-terminal. The full list, with fixes, is in the x402 error codes reference.

The same offer in V1 and V2

Both examples come from the HTTP transport specifications and describe the same payment: 10000 atomic units of the token at 0x036C…CF7e on Base Sepolia, to 0x2096…287C, within 60 seconds. In V1 the client reads it from the response body:

JSONV1: body of the 402 response
{
  "x402Version": 1,
  "error": "Payment required to access this resource",
  "accepts": [
    {
      "scheme": "exact",
      "network": "base-sepolia",
      "maxAmountRequired": "10000",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
      "resource": "https://api.example.com/premium-data",
      "description": "Access to premium market data",
      "mimeType": "application/json",
      "outputSchema": null,
      "maxTimeoutSeconds": 60,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    }
  ]
}

In V2 the body is left to the server, and the client Base64-decodes the PAYMENT-REQUIRED header instead:

JSONV2: PAYMENT-REQUIRED header, decoded
{
  "x402Version": 2,
  "error": "PAYMENT-SIGNATURE header is required",
  "resource": {
    "url": "https://api.example.com/premium-data",
    "description": "Access to premium market data",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "amount": "10000",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
      "maxTimeoutSeconds": 60,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    }
  ]
}

Every field is explained on the x402 V2 headers page.

Migration steps for resource servers

  1. Send the terms in the PAYMENT-REQUIRED header. Answer 402 with a Base64-encoded PaymentRequired object, x402Version 2, in PAYMENT-REQUIRED. The V2 transport treats the response body as a server implementation concern, so clients must not need it.
  2. Move the resource out of each requirement. Replace the per-requirement resource, description and mimeType with one top-level resource object, and set its url to the exact URL the client requested.
  3. Rename the amount and convert the networks. maxAmountRequired becomes amount, still an integer string in atomic units. Network names become CAIP-2 identifiers (table below).
  4. Read PAYMENT-SIGNATURE instead of X-PAYMENT. The payload now carries the chosen requirement in accepted. Match it against your own accepts entries and send your own requirement, with x402Version 2, to /verify and /settle.
  5. Return PAYMENT-RESPONSE instead of X-PAYMENT-RESPONSE. HTTP 200 with the settlement result on success, HTTP 402 with success: false when settlement fails.
  6. Declare and enforce the payment flow. When an option settles before the handler runs (upfront or escrow), V2 requires extra.paymentFlow in it. Servers must reject transfer method and payment flow combinations they do not support.
V1 network names and their CAIP-2 identifiers
V1 nameV2 (CAIP-2)Network
base-sepoliaeip155:84532Base Sepolia
baseeip155:8453Base mainnet
avalanche-fujieip155:43113Avalanche Fuji
avalancheeip155:43114Avalanche mainnet

Migration steps for clients and agents

  • Read the header, not the body. Match PAYMENT-REQUIRED case-insensitively, Base64-decode it and parse the JSON.
  • Check the binding. resource.url should equal the URL you requested.
  • Echo the whole requirement. Put the chosen accepts entry in accepted, the signed data in payload, and send it in PAYMENT-SIGNATURE. Echo any extensions you received: you may add information but not remove it.
  • Skip what you do not recognize. Clients must not build a payment for a paymentFlow they do not recognize, and should prefer authorization when a resource also offers upfront or escrow.
  • Update your spending policy. Allowlists keyed on V1 network names must switch to CAIP-2, and amount caps now compare against amount. The agent spending policy guide covers the full set of limits.
  • Read the outcome from PAYMENT-RESPONSE. Treat settlement_pending as unresolved: reconcile on chain with the returned hash before paying again.

Migration steps for facilitators

  • List V2 kinds in GET /supported: x402Version 2 with CAIP-2 networks. A facilitator can list V1 and V2 kinds side by side, since each kind carries its own x402Version.
  • Add the now-required extensions array and signers map, which pairs CAIP-2 patterns such as eip155:* with your public signer addresses.
  • Keep /verify read-only: the V2 specification states it must not commit payment state or write on chain.
  • Return settlement_pending, with a non-empty transaction and network, when a broadcast transaction cannot be confirmed in time. The facilitator security guide explains why.

Verify the migration

Settlens tests x402 V2 only, which makes it a direct way to confirm a migration from the outside. A passive scan sends no payment. Against an endpoint that still answers the V1 way, with the terms in the body, the header check fails. The bundled demo's /missing-header route answers 402 without the header, and a V1 server gives the same result:

Passive scan of a V1-style endpoint (demo target)
settlens scan http://127.0.0.1:4402/missing-header --no-write
Settlens x402 assessment fa65d786
Targets 1 · Active 0 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/missing-header
4 pass · 1 fail · 0 warn · 1 skip
  FAIL high     PAYMENT-REQUIRED header is present
       The response does not include PAYMENT-REQUIRED.
  SKIP medium   Payment requirements remain stable
       Only one successful sample was available.
 
Total: 4 pass · 1 fail · 0 warn · 1 skip

Once the server sends a valid V2 header, every passive check passes:

Passive scan of a V2 endpoint (demo target)
settlens scan http://127.0.0.1:4402/secure --no-write
Settlens x402 assessment 11984f96
Targets 1 · Active 0 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/secure
13 pass · 0 fail · 0 warn · 0 skip
 
Total: 13 pass · 0 fail · 0 warn · 0 skip

Two more signals help during the transition:

  • A V1 object Base64-encoded into a V2 header is rejected too. Passed to the MCP tool evaluate_payment_requirements when this page was built, the V1 example above got the decision do_not_pay, with schema.payment_required, policy.network and policy.acceptable_option failing.
  • A facilitator scan (settlens facilitator) fails facilitator.supported_schema while /supported still has the V1 shape, without extensions and signers, and facilitator.v2_capability when it lists no x402Version 2 kind.

To check one header by hand, paste it into the x402 payment request checker.

To gate every deploy on these checks, see CI gating; the check reference lists each check with its fix.

Compatibility during the transition

The official @x402/core schemas parse both versions, keyed on x402Version, but the two wire formats do not mix: a V1 client looks for the terms in a response body that V2 leaves to the server, and a V2 client finds no PAYMENT-REQUIRED header on a V1 response. Facilitators differ in which versions they still accept, and /supported is where each one says so, kind by kind. The specifications set no end-of-life date for V1; this page does not guess one.

Frequently asked questions

Is x402 V2 backward compatible with V1?

Not on the wire. V2 moves the payment terms into the PAYMENT-REQUIRED header and renames the client and settlement headers, so each side must speak the same version. A server or facilitator can support both by branching on x402Version.

What replaces maxAmountRequired in x402 V2?

The amount field. It is still an integer string in the asset's atomic units. For the exact scheme it is the exact amount to pay; for the upto scheme it is the maximum authorized at verification time.

Does Settlens test x402 V1 endpoints?

No. Settlens checks x402 V2 only. A V1 endpoint fails the PAYMENT-REQUIRED header check, which is how a scan confirms that a migration has not shipped yet.

Sources

  1. x402 protocol specification, version 2x402 Foundation · github.com
  2. x402 protocol specification, version 1x402 Foundation · github.com
  3. x402 HTTP transport, version 2x402 Foundation · github.com
  4. x402 HTTP transport, version 1x402 Foundation · github.com
  5. CAIP-2: Blockchain ID SpecificationChain Agnostic Standards Alliance · github.com