<!--
Parallel Documentation — this page, as markdown.
Index of every page: https://docs.parallel.best/llms.txt
The whole documentation in one file: https://docs.parallel.best/llms-full.txt
-->

# Payment schemas

The three x402 headers each carry a base64-encoded JSON payload. These are the shapes the middleware and the facilitator exchange.

## `402` response — `payment-required`

Returned when no valid payment is present. The `payment-required` header holds a base64-encoded `PaymentRequired` object:

```ts
interface PaymentRequired {
  x402Version: 2;
  error?: string;            // present on re-challenge, e.g. "INVALID_SIGNATURE"
  message?: string;          // the facilitator's text for `error`, when it sent one
  resource: {
    url: string;
    description?: string;
    mimeType: "application/json";
  };
  accepts: Array<{
    scheme: "exact" | "upto";  // "upto": `amount` is a cap, see metered payments below
    network: string;         // EIP-155, e.g. "eip155:8453"
    asset: string;           // token address
    amount: string;          // in the token's smallest unit (18 decimals for USDp)
    payTo: string;
    maxTimeoutSeconds: number;  // the settlement window, 300 by default, 3,600 at most on "upto"
    extra: { decimals: number };  // the payer must scale the amount with THESE decimals
  }>;
}
```

## Request header — `payment-signature`

The agent attaches a `payment-signature` (or `X-PAYMENT`) header with a base64-encoded signed payload:

```ts
const signedPayload = {
  scheme: "exact",
  network: "base",
  method: "transferWithAuthorization",
  payload: {
    signature: "0x...",
    authorization: {
      from: "0xAgentAddress",
      to: "0xMerchantAddress",
      value: "10000000000000000", // 0.01 USDp in wei
      validAfter: "0",
      validBefore: "1234567890",  // Unix timestamp
      nonce: "0x...",
    },
  },
};

const header = Buffer.from(JSON.stringify(signedPayload)).toString("base64");
```

Agent-side signing helpers (EIP-3009 authorizations, EIP-712 signing) live in the `@parallel-protocol/payment-core` package.

### Metered payments (`upto`)

On a metered invoice, the agent signs a `ReceiveWithAuthorization` of the cap to Parallel's escrow contract, on the USDp domain, instead of a transfer to the merchant. The payload has `scheme: "upto"` and the method `uptoWithAuthorization`, carries the merchant in `uptoParams.payTo`, and puts the payer's random salt in `authorization.nonce`: the signed nonce is derived from the salt and the payment's terms, so the signature only holds for those exact values. `@parallel-protocol/payment-core` exports `deriveUptoNonce` for this. The escrow address comes from Parallel's chain catalog (`@parallel-protocol/chains`), never from the invoice.

## `200` response — `payment-response`

On success, the middleware sets a `payment-response` header with a base64-encoded settlement confirmation:

```ts
interface PaymentConfirmation {
  success: true;
  txHash: string;
  route: string;        // the settlement route, "A" to "L", or "U" for a metered payment
  chain: string;        // "base"
  gasSponsored: boolean;
  networkId: string;    // "eip155:8453"
  amountSettled?: string;  // metered routes: the amount charged, in the token's smallest unit
  alreadySettled?: true;  // answered from an earlier settle's recorded outcome, nothing broadcast this time
}
```

`route` is one of the [settlement routes](/agents/x402/verify-vs-settle#settlement-routes). The middleware sets `access-control-expose-headers` automatically so browser clients can read `payment-response`.

## `402` after a failed settlement

When the settlement fails after your handler answered, the response is discarded and replaced by a `402` with a plain JSON body. It carries no `payment-required` header, so an x402 client does not take it for a new invoice:

```ts
interface SettlementFailureBody {
  error: "PAYMENT_SETTLEMENT_FAILED";
  reason: string;   // why, e.g. "INSUFFICIENT_BALANCE" or "AMOUNT_MISMATCH" (see the error codes)
  message: string;  // one line, cut at 300 characters
}
```

The facilitator's `Retry-After` header is copied when it sent one. The reasons and what the agent should do with each are listed in [settlement failures](/agents/x402/error-codes#settlement-failures).

