x402 V2 headers: PAYMENT‑REQUIRED, PAYMENT‑SIGNATURE and PAYMENT‑RESPONSE

x402 V2 carries every protocol message in three HTTP headers. This reference covers their encoding, every field, decoded examples from the specification and the mistakes that make a payment request unsafe to pay.

In short

x402 V2 uses three HTTP headers, each a Base64-encoded JSON object: PAYMENT-REQUIRED (sent with 402, the PaymentRequired terms), PAYMENT-SIGNATURE (the client's PaymentPayload on the retry) and PAYMENT-RESPONSE (the settlement result). All protocol data travels in headers, not in the body.

On this page

The x402 V2 HTTP transport moves all protocol data into headers: the response body is left to the server. Each header value is a Base64-encoded JSON object. The object types themselves are defined in the V2 core specification. If you are new to the flow, start with What is x402?.

The three headers at a glance

x402 V2 HTTP headers
HeaderDirectionCarriesSent with
PAYMENT-REQUIREDServer to clientPaymentRequiredHTTP 402 when no valid payment is attached
PAYMENT-SIGNATUREClient to serverPaymentPayloadThe retried request
PAYMENT-RESPONSEServer to clientSettlementResponseThe final response: 200 on success, 402 when settlement fails
Each header carries one Base64-encoded JSON object. The facilitator calls happen behind the resource server and use plain JSON bodies.

Encoding: Base64 JSON

To read a header, Base64-decode the value and parse the result as UTF-8 JSON. To write one, serialize the object and Base64-encode it. A few practical points:

  • Header names are case-insensitive in HTTP (RFC 9110, section 5.1). Some servers emit payment-required in lower case (the Settlens demo target does), so clients must match names case-insensitively.
  • The specification examples use standard Base64 with padding. Settlens' decoder accepts the standard and URL-safe alphabets and refuses values longer than 131,072 characters.
  • Do not rely on the response body. The transport says bodies are a server implementation concern; a V2 client reads the terms from the header.
  • Large values can hit proxy and server header-size limits, so keep descriptions and extensions short.

PAYMENT-REQUIRED

Sent with HTTP 402 when a request carries no valid payment. It tells the client what the resource is and every way it can be paid.

PaymentRequired fields

PaymentRequired (x402 V2 core specification, section 5.1)
FieldTypeRequiredMeaning
x402VersionnumberYesProtocol version. Must be 2.
errorstringNoHuman-readable reason payment is required.
resourceobjectYesA ResourceInfo object describing the protected resource.
acceptsarrayYesThe payment options the server accepts. Each entry is a PaymentRequirements object.
extensionsobjectNoExtension data keyed by extension identifier, each with info and schema.

Payment requirements: each accepts entry

PaymentRequirements (x402 V2 core specification, section 5.1.2)
FieldTypeMeaning
schemestringPayment scheme, for example exact.
networkstringCAIP-2 network, namespace:reference, for example eip155:84532.
amountstringAmount in the asset's atomic units, as an integer string.
assetstringToken contract address, or an ISO 4217 currency code for fiat rails.
payTostringRecipient address, or a role constant such as "merchant".
maxTimeoutSecondsnumberMaximum time allowed for payment completion.
extraobject, optionalScheme data. assetTransferMethod and paymentFlow are reserved protocol keys; for EIP-3009 tokens, name and version give the token's EIP-712 domain.

ResourceInfo

url is required: it identifies the protected resource the payment is for. The optional fields are description, mimeType, serviceName (printable ASCII, at most 32 characters), tags (at most 5, each at most 32 characters) and iconUrl (absolute http or https URL, at most 2048 characters).

Decoded example

The response below is the example from the HTTP transport specification. The header value was produced at build time by encoding the decoded object underneath; it matches the specification byte for byte.

HTTP402 response
HTTP/1.1 402 Payment Required
Content-Type: application/json
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZWFkZXIgaXMgcmVxdWlyZWQiLCJyZXNvdXJjZSI6eyJ1cmwiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbS9wcmVtaXVtLWRhdGEiLCJkZXNjcmlwdGlvbiI6IkFjY2VzcyB0byBwcmVtaXVtIG1hcmtldCBkYXRhIiwibWltZVR5cGUiOiJhcHBsaWNhdGlvbi9qc29uIn0sImFjY2VwdHMiOlt7InNjaGVtZSI6ImV4YWN0IiwibmV0d29yayI6ImVpcDE1NTo4NDUzMiIsImFtb3VudCI6IjEwMDAwIiwiYXNzZXQiOiIweDAzNkNiRDUzODQyYzU0MjY2MzRlNzkyOTU0MWVDMjMxOGYzZENGN2UiLCJwYXlUbyI6IjB4MjA5NjkzQmM2YWZjMEM1MzI4YkEzNkZhRjAzQzUxNEVGMzEyMjg3QyIsIm1heFRpbWVvdXRTZWNvbmRzIjo2MCwiZXh0cmEiOnsibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9fV19

{}
JSONPAYMENT-REQUIRED, 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"
      }
    }
  ]
}

Read it as: pay 10000 atomic units of the token at 0x036C…CF7e on Base Sepolia (eip155:84532), whose EIP-712 domain name is USDC, to 0x2096…287C, within 60 seconds, for https://api.example.com/premium-data. With 6 decimals, as USDC has, that is 0.01.

Extensions

Servers advertise optional features in extensions, and clients echo them in their payload. A client must include at least the info it received: it may append information but cannot delete or overwrite it.

PAYMENT-SIGNATURE

Sent by the client on the retried request. It carries the payment authorization for exactly one of the advertised options.

PaymentPayload fields

PaymentPayload (x402 V2 core specification, section 5.2)
FieldRequiredMeaning
x402VersionYesProtocol version.
resourceNoThe ResourceInfo of the resource being accessed.
acceptedYesThe PaymentRequirements object the client chose from accepts.
payloadYesScheme-specific payment data.
extensionsNoExtension data echoed from the server.

For the exact scheme on EVM chains with the default EIP-3009 transfer method, payload holds a 65-byte EIP-712 signature and the authorization it signs: from, to, value, validAfter, validBefore and a 32-byte nonce. The EIP-3009 guide explains each field and how the signature is built. Other transfer methods, such as Permit2, use a different payload shape.

JSONPAYMENT-SIGNATURE, decoded
{
  "x402Version": 2,
  "resource": {
    "url": "https://api.example.com/premium-data",
    "description": "Access to premium market data",
    "mimeType": "application/json"
  },
  "accepted": {
    "scheme": "exact",
    "network": "eip155:84532",
    "amount": "10000",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
    "maxTimeoutSeconds": 60,
    "extra": {
      "name": "USDC",
      "version": "2"
    }
  },
  "payload": {
    "signature": "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c",
    "authorization": {
      "from": "0x857b06519E91e3A54538791bDbb0E22373e36b66",
      "to": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
      "value": "10000",
      "validAfter": "1740672089",
      "validBefore": "1740672154",
      "nonce": "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480"
    }
  }
}

Verify against your own terms

accepted is a copy written by the client. A resource server should check it against one of its own accepts entries and send its own requirement to the facilitator, so a client cannot lower the amount or change the recipient by editing the copy.

PAYMENT-RESPONSE

Sent by the server with the final response, after settlement. It carries a settlement response.

SettleResponse (x402 V2 core specification, section 5.3)
FieldRequiredMeaning
successYesWhether settlement succeeded.
errorReasonNoWhy settlement failed. Omitted on success.
payerNoThe payer's address.
transactionYesTransaction hash, or an empty string when nothing was broadcast. Must be non-empty with settlement_pending.
networkYesCAIP-2 network of the settlement.
amountNoAmount actually settled, in atomic units, when applicable.
extensionsNoExtension data.
JSONPAYMENT-RESPONSE with HTTP 200, decoded
{
  "success": true,
  "transaction": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  "network": "eip155:84532",
  "payer": "0x857b06519E91e3A54538791bDbb0E22373e36b66"
}
JSONPAYMENT-RESPONSE with HTTP 402, decoded
{
  "success": false,
  "errorReason": "insufficient_funds",
  "transaction": "",
  "network": "eip155:84532",
  "payer": "0x857b06519E91e3A54538791bDbb0E22373e36b66"
}

settlement_pending is the one non-terminal error: the transaction was broadcast but its confirmation could not be established. The caller should reconcile on chain with the returned hash before deciding whether to retry. Every value of errorReason is listed in the x402 error codes reference.

Status codes

x402 errors mapped to HTTP status codes (V2 HTTP transport)
SituationHTTP status
Payment required402
Invalid payment: malformed payload or requirements400
Payment failed: verification or settlement failed402
Server error during payment processing500
Payment verified and settled200

Validation pitfalls

The official schema in @x402/core 2.28.0 checks types and required fields, and is permissive about values. The table below was computed when this page was built: each row changes one field of the example above (the V1 row replaces the whole object), then runs the pinned SDK schema and Settlens' offline checks against it, with https://api.example.com/premium-data as the requested URL.

Malformed PAYMENT-REQUIRED variants: @x402/core 2.28.0 schema versus Settlens protocol checks
PitfallSDK schemaSettlens checks that fail or warn
Decimal amount
amount: "0.01"
Acceptsschema.payment_requirements fail
Zero amount
amount: "0"
Acceptsschema.payment_requirements fail
V1 network name
network: "base-sepolia"
Rejectsschema.official_x402_sdk fail
schema.payment_requirements fail
Number as a string
maxTimeoutSeconds: "60"
Rejectsschema.official_x402_sdk fail
schema.payment_requirements fail
Role constant as recipient on EVM
payTo: "merchant"
Acceptsschema.payment_requirements fail
Relative resource URL
resource.url: "/premium-data"
Acceptsschema.payment_required fail
Resource URL of another endpoint
resource.url: ".../other-data"
Acceptsbinding.resource_url fail
Duplicate payment option
accepts: [A, A]
Acceptsschema.duplicate_requirements warn
One-day payment window
maxTimeoutSeconds: 86400
Acceptsfreshness.timeout_window warn
V1 object in a V2 header
x402Version: 1
Acceptsschema.payment_required fail
No EIP-712 domain hints
extra omitted
AcceptsNone

Three rows deserve a comment:

  • Role constants. The core specification allows payTo to be a role constant such as "merchant". On eip155 networks Settlens requires a 20-byte address, because the to field of an EIP-3009 authorization is an address.
  • V1 objects. The SDK schema accepts both protocol versions, so a V1 object passes it. A V2 header must carry x402Version: 2, which Settlens checks separately. The V1 to V2 migration guide maps every renamed field.
  • Missing extra. No scan check fails, yet the exact EVM specification makes extra.name and extra.version required for EIP-3009: a client cannot build the signature without the token's EIP-712 name and version, and Settlens' offline verify-payment refuses to verify without them.

How Settlens checks each header

PAYMENT-REQUIRED

A passive scan sends the request without any payment header and runs these checks on the answer. The same content checks run offline when an agent passes the header to the MCP tool evaluate_payment_requirements.

Settlens checks that read PAYMENT-REQUIRED
Check idWhat it verifies
transport.status_402A request without any payment header returns HTTP 402 Payment Required.
transport.payment_required_headerThe 402 response carries the PAYMENT-REQUIRED header defined by the x402 V2 HTTP transport.
transport.payment_required_encodingThe PAYMENT-REQUIRED value is at most 131072 characters, uses only Base64 or Base64URL characters and decodes to valid JSON nested at most 64 levels deep.
schema.official_x402_sdkThe decoded PaymentRequired object passes the parsePaymentRequired schema of the pinned official @x402/core 2.28.0 SDK.
schema.payment_requiredx402Version equals 2, resource is an object whose url is an absolute http(s) URL, and accepts is a non-empty array.
binding.resource_urlresource.url equals the URL that was requested, including path and query (the fragment is ignored). In evaluate_payment_requirements this check is a warning when no resource URL is supplied, because the binding cannot be confirmed.
schema.payment_requirementsEvery accepts entry has a non-empty scheme, a CAIP-2 network, a positive integer atomic amount, a non-empty asset and payTo, and a positive maxTimeoutSeconds. On eip155 networks payTo must be a 20-byte hex address.
schema.duplicate_requirementsNo two accepts entries are identical.
freshness.timeout_windowEvery maxTimeoutSeconds value is at most 3600 seconds (one hour).
stability.payment_requirementsRepeated identical unauthenticated requests (2 by default) return the same resource, accepts and extensions, ignoring the known ephemeral fields recentBlockhash and lastValidBlockHeight. Skipped when only one sample is available.

PAYMENT-SIGNATURE

Passive scans never send this header, and Settlens refuses a user-supplied one. With the endpoint owner's authorization, the active probe sends one intentionally invalid value:

Settlens checks behind the authorized active probe
Check idWhat it verifies
security.invalid_signature_fail_closedWith explicit authorization, Settlens sends one intentionally invalid PAYMENT-SIGNATURE (for example an all-zero EVM signature) and expects the resource not to answer with a 2xx status.
transport.invalid_payment_statusThe response to the intentionally invalid payment is HTTP 400 or 402.
security.facilitator_rejects_invalid_signature/verify returns isValid=false for the intentionally invalid, all-zero signature. Settlens never calls /settle.

Separately, a captured authorization can be verified offline: the CLI's verify-payment reads a JSON file with the requirement and the payload (the same paymentRequirements and paymentPayload pair a server sends to /verify also works), and the MCP tool verify_payment_signature accepts the raw header value.

PAYMENT-RESPONSE

Settlens does not inspect PAYMENT-RESPONSE. It never completes a payment and never calls /settle, so an endpoint that behaves correctly never sends one to a scan.

Every check id, with its severity and remediation, is listed in the check reference. To run them against your own endpoint, see Assess or the CLI reference. To decode and check a single header in your browser, paste it into the x402 payment request checker.

Sources

  1. x402 HTTP transport, version 2x402 Foundation · github.com
  2. x402 protocol specification, version 2x402 Foundation · github.com
  3. x402 scheme: exact on EVMx402 Foundation · github.com
  4. RFC 9110: HTTP SemanticsIETF · rfc-editor.org