---
title: "PAYMENT-REQUIRED and x402 V2 headers explained | Settlens"
description: "Field-by-field guide to the x402 V2 headers: Base64 JSON encoding, PaymentRequired, PaymentPayload and SettleResponse, decoded examples and validation pitfalls."
url: https://settlens.com/learn/x402-v2-headers
last_updated: 2026-10-13
published: 2026-10-13
---

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

> **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.

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.

The [x402 V2 HTTP transport](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md) 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](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md). If you are new to the flow, start with [What is x402?](https://settlens.com/learn/what-is-x402).

## The three headers at a glance

_x402 V2 HTTP headers_

| Header | Direction | Carries | Sent with |
| --- | --- | --- | --- |
| `PAYMENT-REQUIRED` | Server to client | `PaymentRequired` | HTTP 402 when no valid payment is attached |
| `PAYMENT-SIGNATURE` | Client to server | `PaymentPayload` | The retried request |
| `PAYMENT-RESPONSE` | Server to client | `SettlementResponse` | The final response: 200 on success, 402 when settlement fails |

_Diagram: Diagram of the three x402 V2 headers between a client and a resource server: 402 with PAYMENT-REQUIRED, the retried request with PAYMENT-SIGNATURE, and the final response with PAYMENT-RESPONSE, each carrying a Base64-encoded JSON object._

_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](https://www.rfc-editor.org/rfc/rfc9110#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)_

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `x402Version` | number | Yes | Protocol version. Must be 2. |
| `error` | string | No | Human-readable reason payment is required. |
| `resource` | object | Yes | A `ResourceInfo` object describing the protected resource. |
| `accepts` | array | Yes | The payment options the server accepts. Each entry is a PaymentRequirements object. |
| `extensions` | object | No | Extension data keyed by extension identifier, each with `info` and `schema`. |

### Payment requirements: each accepts entry

_PaymentRequirements (x402 V2 core specification, section 5.1.2)_

| Field | Type | Meaning |
| --- | --- | --- |
| `scheme` | string | Payment scheme, for example `exact`. |
| `network` | string | [CAIP-2](https://settlens.com/learn/glossary#caip-2) network, `namespace:reference`, for example `eip155:84532`. |
| `amount` | string | Amount in the asset's [atomic units](https://settlens.com/learn/glossary#atomic-units), as an integer string. |
| `asset` | string | Token contract address, or an ISO 4217 currency code for fiat rails. |
| `payTo` | string | Recipient address, or a role constant such as "merchant". |
| `maxTimeoutSeconds` | number | Maximum time allowed for payment completion. |
| `extra` | object, optional | Scheme 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.

**402 response**

```http
HTTP/1.1 402 Payment Required
Content-Type: application/json
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZWFkZXIgaXMgcmVxdWlyZWQiLCJyZXNvdXJjZSI6eyJ1cmwiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbS9wcmVtaXVtLWRhdGEiLCJkZXNjcmlwdGlvbiI6IkFjY2VzcyB0byBwcmVtaXVtIG1hcmtldCBkYXRhIiwibWltZVR5cGUiOiJhcHBsaWNhdGlvbi9qc29uIn0sImFjY2VwdHMiOlt7InNjaGVtZSI6ImV4YWN0IiwibmV0d29yayI6ImVpcDE1NTo4NDUzMiIsImFtb3VudCI6IjEwMDAwIiwiYXNzZXQiOiIweDAzNkNiRDUzODQyYzU0MjY2MzRlNzkyOTU0MWVDMjMxOGYzZENGN2UiLCJwYXlUbyI6IjB4MjA5NjkzQmM2YWZjMEM1MzI4YkEzNkZhRjAzQzUxNEVGMzEyMjg3QyIsIm1heFRpbWVvdXRTZWNvbmRzIjo2MCwiZXh0cmEiOnsibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9fV19

{}
```

**PAYMENT-REQUIRED, decoded**

```json
{
  "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)_

| Field | Required | Meaning |
| --- | --- | --- |
| `x402Version` | Yes | Protocol version. |
| `resource` | No | The `ResourceInfo` of the resource being accessed. |
| `accepted` | Yes | The PaymentRequirements object the client chose from `accepts`. |
| `payload` | Yes | Scheme-specific payment data. |
| `extensions` | No | Extension data echoed from the server. |

For the [`exact` scheme on EVM chains](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md) 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](https://settlens.com/learn/eip-3009) explains each field and how the signature is built. Other transfer methods, such as Permit2, use a different payload shape.

**PAYMENT-SIGNATURE, decoded**

```json
{
  "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)_

| Field | Required | Meaning |
| --- | --- | --- |
| `success` | Yes | Whether settlement succeeded. |
| `errorReason` | No | Why settlement failed. Omitted on success. |
| `payer` | No | The payer's address. |
| `transaction` | Yes | Transaction hash, or an empty string when nothing was broadcast. Must be non-empty with `settlement_pending`. |
| `network` | Yes | CAIP-2 network of the settlement. |
| `amount` | No | Amount actually settled, in atomic units, when applicable. |
| `extensions` | No | Extension data. |

**PAYMENT-RESPONSE with HTTP 200, decoded**

```json
{
  "success": true,
  "transaction": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  "network": "eip155:84532",
  "payer": "0x857b06519E91e3A54538791bDbb0E22373e36b66"
}
```

**PAYMENT-RESPONSE with HTTP 402, decoded**

```json
{
  "success": false,
  "errorReason": "insufficient_funds",
  "transaction": "",
  "network": "eip155:84532",
  "payer": "0x857b06519E91e3A54538791bDbb0E22373e36b66"
}
```

[`settlement_pending`](https://settlens.com/learn/glossary#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](https://settlens.com/learn/x402-error-codes).

## Status codes

_x402 errors mapped to HTTP status codes (V2 HTTP transport)_

| Situation | HTTP status |
| --- | --- |
| Payment required | 402 |
| Invalid payment: malformed payload or requirements | 400 |
| Payment failed: verification or settlement failed | 402 |
| Server error during payment processing | 500 |
| Payment verified and settled | 200 |

## 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_

| Pitfall | SDK schema | Settlens checks that fail or warn |
| --- | --- | --- |
| Decimal amount; `amount: "0.01"` | Accepts | `schema.payment_requirements` fail |
| Zero amount; `amount: "0"` | Accepts | `schema.payment_requirements` fail |
| V1 network name; `network: "base-sepolia"` | Rejects | `schema.official_x402_sdk` fail; `schema.payment_requirements` fail |
| Number as a string; `maxTimeoutSeconds: "60"` | Rejects | `schema.official_x402_sdk` fail; `schema.payment_requirements` fail |
| Role constant as recipient on EVM; `payTo: "merchant"` | Accepts | `schema.payment_requirements` fail |
| Relative resource URL; `resource.url: "/premium-data"` | Accepts | `schema.payment_required` fail |
| Resource URL of another endpoint; `resource.url: ".../other-data"` | Accepts | `binding.resource_url` fail |
| Duplicate payment option; `accepts: [A, A]` | Accepts | `schema.duplicate_requirements` warn |
| One-day payment window; `maxTimeoutSeconds: 86400` | Accepts | `freshness.timeout_window` warn |
| V1 object in a V2 header; `x402Version: 1` | Accepts | `schema.payment_required` fail |
| No EIP-712 domain hints; `extra omitted` | Accepts | None |

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](https://settlens.com/learn/x402-v1-to-v2-migration) maps every renamed field.
- **Missing `extra`.** No scan check fails, yet the [exact EVM specification](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md) 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 id | What it verifies |
| --- | --- |
| `transport.status_402` | A request without any payment header returns HTTP 402 Payment Required. |
| `transport.payment_required_header` | The 402 response carries the PAYMENT-REQUIRED header defined by the x402 V2 HTTP transport. |
| `transport.payment_required_encoding` | The 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_sdk` | The decoded PaymentRequired object passes the parsePaymentRequired schema of the pinned official @x402/core 2.28.0 SDK. |
| `schema.payment_required` | x402Version equals 2, resource is an object whose url is an absolute http(s) URL, and accepts is a non-empty array. |
| `binding.resource_url` | resource.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_requirements` | Every 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_requirements` | No two accepts entries are identical. |
| `freshness.timeout_window` | Every maxTimeoutSeconds value is at most 3600 seconds (one hour). |
| `stability.payment_requirements` | Repeated 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](https://settlens.com/learn/glossary#active-probe) sends one intentionally invalid value:

_Settlens checks behind the authorized active probe_

| Check id | What it verifies |
| --- | --- |
| `security.invalid_signature_fail_closed` | With 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_status` | The 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](https://settlens.com/docs/checks). To run them against your own endpoint, see [Assess](https://settlens.com/product/assess) or the [CLI reference](https://settlens.com/docs/cli). To decode and check a single header in your browser, paste it into the [x402 payment request checker](https://settlens.com/tools/x402-checker).

## Sources

1. [x402 HTTP transport, version 2](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md) — x402 Foundation
2. [x402 protocol specification, version 2](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md) — x402 Foundation
3. [x402 scheme: exact on EVM](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md) — x402 Foundation
4. [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110) — IETF
