In Part 1, I introduced t402 — what it is, why it exists, and what it enables. This post goes deeper. Much deeper. We are going to walk through the complete protocol specification: every data structure, every signature scheme, every verification step. If you want to implement t402, audit it, or simply understand exactly what happens between a 402 Payment Required and a 200 OK, this is the post.

The authoritative reference is the t402 whitepaper (153 pages). What follows is a distillation of its core technical content.

Three-Layer Architecture

t402 is not a monolithic protocol. It is designed as three separable layers, each with distinct responsibilities:

graph TB
    subgraph Representation["Representation Layer"]
        HTTP["HTTP Headers<br/>PAYMENT-REQUIRED / SIGNATURE / RESPONSE"]
        MCP["MCP Transport<br/>JSON-RPC error 402"]
        A2A["A2A Transport<br/>Task lifecycle"]
    end
    subgraph Logic["Logic Layer"]
        EXACT["exact scheme<br/>EIP-3009"]
        UPTO["upto scheme<br/>EIP-2612"]
        PERMIT2["permit2 scheme<br/>Uniswap Permit2"]
    end
    subgraph Types["Types Layer"]
        PR["PaymentRequired"]
        PP["PaymentPayload"]
        SR["SettlementResponse"]
    end
    Representation --> Logic
    Logic --> Types

Layer 1: Types (Transport-Agnostic Data Structures)

The Types layer defines pure data structures with no knowledge of HTTP, WebSocket, or any transport mechanism. These are the canonical representations of payment requests and responses:

  • PaymentRequired — what the server demands
  • PaymentSignature — what the client provides
  • PaymentResponse — what the server returns after settlement

These structures are defined as TypeScript interfaces, JSON Schema, and language-specific types across all SDKs. They can be serialized to JSON, Protobuf, CBOR, or any encoding format. The transport layer has no opinion.

This separation is critical. It means t402 can operate over HTTP headers (the primary transport), but equally over WebSocket frames, gRPC metadata, MQTT payloads, or even QR codes for in-person payments.

Layer 2: Logic (Chain-Specific Payment Schemes)

The Logic layer implements payment scheme handlers — the code that knows how to construct, sign, and verify payments on a specific blockchain. Each scheme is a module that implements a common interface:

interface PaymentScheme {
  scheme: string;
  buildSignature(params: SignParams): Promise<PaymentSignature>;
  verifySignature(sig: PaymentSignature): Promise<VerificationResult>;
  executeOnChain(sig: PaymentSignature): Promise<TransactionReceipt>;
}

Schemes are pluggable. Adding support for a new blockchain family means implementing this interface — the rest of the stack (HTTP headers, middleware, client libraries) works unchanged.

Layer 3: Representation (Transport-Layer Encoding)

The Representation layer handles encoding and decoding for a specific transport. For HTTP, this means:

  • Encoding PaymentRequired as the PAYMENT-REQUIRED header (Base64-encoded JSON)
  • Decoding PAYMENT-SIGNATURE from the client request
  • Encoding PaymentResponse as the PAYMENT-RESPONSE header

For the A2A (Agent-to-Agent) transport, representation uses JSON-RPC message fields. For MCP, it uses tool call parameters.

The key design principle: each layer can evolve independently. A new blockchain (Layer 2) does not require changes to HTTP encoding (Layer 3). A new transport (Layer 3) does not require changes to data structures (Layer 1).

The PaymentRequired Object

When a server returns 402 Payment Required, the PAYMENT-REQUIRED header carries a Base64-encoded JSON object. This is the most important data structure in the protocol. Let us examine every field.

Full Structure

{
  "t402Version": 2,
  "resource": {
    "url": "/api/v2/market-data",
    "description": "Real-time BTC/USD market data with 1s granularity",
    "method": "GET"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:42161",
      "amount": "10000",
      "asset": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
      "payTo": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
      "maxTimeoutSeconds": 300,
      "extra": {
        "facilitatorUrl": "https://facilitator.t402.io"
      },
      "extensions": ["bazaar", "payment-identifier"]
    },
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "10000",
      "asset": "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
      "payTo": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
      "maxTimeoutSeconds": 300,
      "extra": {
        "facilitatorUrl": "https://facilitator.t402.io"
      }
    }
  ]
}

Field-by-Field Breakdown

t402Version (integer, required) The protocol version. Current production version is 2. Version 1 is deprecated and should not be used in new implementations. The version number determines how accepts entries are interpreted and which header names are used.

resource (object, required) Describes what the client is paying for.

  • resource.url (string, required) — The URL path of the protected resource. This is the canonical identifier used during signature verification to prevent cross-resource replay attacks.
  • resource.description (string, optional) — Human-readable description. Intended for display in wallet UIs and AI agent decision-making.
  • resource.method (string, optional) — The HTTP method. Defaults to GET. Important for distinguishing GET /data (read, might cost ₮0.01) from POST /data (write, might cost ₮0.10).

accepts (array, required) An array of payment options the server will accept. The client chooses one. Each entry is an accept object:

  • scheme (string, required) — The payment scheme identifier. One of: "exact", "upto", "permit2", "permit2-proxy". Determines which signature construction and on-chain settlement mechanism is used.

  • network (string, required) — A CAIP-2 network identifier. See the Network Identifiers section below.

  • amount (string, required) — The payment amount in the token’s smallest unit, expressed as a decimal string. For USDT with 6 decimals: "10000" = 0.01 USDT. String type avoids floating-point precision issues.

  • asset (string, required) — The token contract address (EVM), mint address (Solana), jetton master (TON), or equivalent identifier for the payment asset.

  • payTo (string, required) — The recipient address. This address is cryptographically bound into the client’s signature — the most important security property in the protocol.

  • maxTimeoutSeconds (integer, optional) — Maximum time window the server will accept between signature creation and submission. Default: 300 (5 minutes). Shorter windows reduce replay risk; longer windows accommodate slow networks.

  • extra (object, optional) — Scheme-specific and implementation-specific metadata. Common fields:

    • extra.facilitatorUrl — URL of the facilitator service that will settle this payment
    • extra.chainId — Explicit chain ID (redundant with network but useful for quick EVM lookups)
    • extra.tokenDecimals — Token decimals (avoids an on-chain lookup)
  • extensions (array of strings, optional) — Active protocol extensions. Each string is an extension identifier that modifies the behavior of the payment flow.

Payment Schemes Deep Dive

The scheme field determines everything: how the signature is constructed, what smart contract function is called, and what security guarantees are provided. Let us examine each scheme in detail.

exact Scheme: EIP-3009 transferWithAuthorization

The exact scheme is the primary production scheme for EVM networks. It uses EIP-3009 transferWithAuthorization, which allows a token holder to authorize a transfer via an off-chain signature that anyone can submit on-chain.

Why EIP-3009?

EIP-3009 was designed specifically for this use case: delegated transfers. Unlike EIP-20 approve + transferFrom (which requires two on-chain transactions), transferWithAuthorization executes in a single transaction using a pre-signed authorization. The signer never touches the blockchain. The submitter (facilitator) pays gas and earns a fee.

Signature Construction

The client constructs an EIP-712 typed data signature over the following domain and message:

Domain Separator:

{
  "name": "Tether USD",
  "version": "1",
  "chainId": 42161,
  "verifyingContract": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"
}

The domain separator is token-contract-specific. Each USDT deployment has its own name, version, and address. The chainId prevents cross-chain replay — a signature valid on Arbitrum (chain 42161) cannot be replayed on Base (chain 8453).

Type Hash:

TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)

This type hash defines the structure of the signed message. Every field listed here is cryptographically committed to in the signature — changing any one of them invalidates the entire authorization.

Message:

{
  "from": "0xClientWalletAddress...",
  "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
  "value": "10000",
  "validAfter": 1735200000,
  "validBefore": 1735200300,
  "nonce": "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b"
}

Let us examine each field:

  • from: The client’s wallet address. This will be ecrecover‘d from the signature on-chain.
  • to: The payTo address from the PaymentRequired object. This is the field that prevents facilitator fund redirection — it is locked into the signature.
  • value: The exact payment amount. Must match the amount in the accept object.
  • validAfter: Unix timestamp. The authorization is invalid before this time. Typically set to current time minus a small buffer.
  • validBefore: Unix timestamp. The authorization expires after this time. Set to validAfter + maxTimeoutSeconds.
  • nonce: A random 32-byte value. EIP-3009 nonces are not sequential — they are random, enabling concurrent authorizations. The contract tracks used nonces to prevent replay.

The client signs this with eth_signTypedData_v4, producing a 65-byte (v, r, s) signature. At this point, no on-chain interaction has occurred — the client has merely produced a cryptographic proof of intent.

On-Chain Execution

The facilitator takes that off-chain signature and submits it to the token contract on behalf of the client, paying gas so the client never needs to hold ETH:

function transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    uint8 v,
    bytes32 r,
    bytes32 s
) external;

The contract:

  1. Reconstructs the EIP-712 hash from the parameters
  2. Calls ecrecover(hash, v, r, s) to recover the signer
  3. Verifies recovered == from
  4. Checks block.timestamp > validAfter && block.timestamp < validBefore
  5. Checks the nonce has not been used
  6. Marks the nonce as used
  7. Transfers value tokens from from to to

If any check fails, the entire transaction reverts. The facilitator wasted gas but could not steal funds.

upto Scheme (Draft): EIP-2612 Permit

The upto scheme is currently in draft status and targets usage-based billing scenarios where the final amount is not known at request time.

Consider an API that charges per kilobyte of data returned, or a compute service that charges per second of execution. The client does not know the final cost when the request begins.

How It Works

Instead of authorizing a transfer to a specific recipient, the client signs an EIP-2612 permit that grants an allowance up to a maximum amount:

{
  "owner": "0xClientWallet...",
  "spender": "0xFacilitatorContract...",
  "value": "1000000",
  "nonce": 42,
  "deadline": 1735200300
}

The type hash follows the EIP-2612 standard, which is simpler than EIP-3009 because it grants an allowance rather than authorizing a direct transfer:

Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)

Key differences from exact:

  • spender is the facilitator contract, not the merchant. The facilitator is authorized to transfer up to value tokens.
  • nonce is sequential (EIP-2612 uses the contract’s nonce counter), not random.
  • The actual transfer amount is determined after resource delivery, based on measured usage.

Settlement Flow

  1. Client signs permit for max amount (e.g., ₮1.00)
  2. Server delivers resource, measures usage (e.g., 45 KB at ₮0.001/KB = ₮0.045)
  3. Facilitator calls permit() to set allowance, then transferFrom() for actual amount (₮0.045)
  4. Client is charged only for what was consumed

Security Trade-off

The upto scheme grants the facilitator more power than exact — the facilitator determines the final amount within the authorized range. This is why it requires a trusted facilitator relationship and is marked as draft. The protocol is exploring commit-reveal mechanisms to constrain facilitator discretion while preserving usage-based flexibility.

permit2 Scheme: Uniswap Permit2 SignatureTransfer

The permit2 scheme uses Uniswap’s Permit2 contract — a universal signature-based transfer system that works with any ERC-20 token, even those without native EIP-2612 or EIP-3009 support.

Why Permit2?

Not all USDT deployments support EIP-3009. Legacy USDT on Ethereum mainnet, for example, predates EIP-3009. Permit2 solves this: the user approves the Permit2 contract once (a standard ERC-20 approve), and then all future transfers can be authorized via off-chain signatures to the Permit2 contract.

Signature Structure

{
  "permitted": {
    "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
    "amount": "10000"
  },
  "spender": "0xFacilitatorContract...",
  "nonce": "98237498237",
  "deadline": 1735200300
}

The Permit2 SignatureTransfer type hash includes a nested TokenPermissions struct, binding the specific token and amount into the signature:

PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)
TokenPermissions(address token,uint256 amount)

The facilitator calls Permit2.permitTransferFrom() which:

  1. Verifies the signature
  2. Checks nonce and deadline
  3. Transfers tokens from the signer to the specified recipient

Nonce Scheme

Permit2 uses a bitmap-based nonce scheme (not sequential, not random). The nonce is a uint256 where the upper 248 bits select a “word” and the lower 8 bits select a bit within that word. This allows up to 256 concurrent pending authorizations per word, with practically unlimited words.

permit2-proxy Scheme: Witness-Based Facilitator Binding

The permit2-proxy scheme extends permit2 with a witness — additional typed data that is hashed into the signature, binding the payment to a specific facilitator and resource.

The Problem It Solves

With basic permit2, the spender is the facilitator contract. But what if there are multiple facilitators? The client might sign a payment intended for Facilitator A, but Facilitator B could intercept and submit it. The witness mechanism prevents this.

Witness Structure

{
  "permitted": {
    "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
    "amount": "10000"
  },
  "spender": "0xPermit2ProxyContract...",
  "nonce": "98237498237",
  "deadline": 1735200300,
  "witness": {
    "facilitator": "0xSpecificFacilitatorAddress...",
    "resourceHash": "0x...",
    "payTo": "0xMerchantWallet..."
  }
}

The type hash now includes the witness, which cryptographically binds the payment to a specific facilitator and merchant — not just the token amount:

PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,T402Witness witness)
T402Witness(address facilitator,bytes32 resourceHash,address payTo)

Notice that payTo is the merchant’s own wallet address — not the facilitator’s. This is what makes the protocol non-custodial: funds flow directly from client to merchant, with the facilitator merely relaying the pre-signed authorization.

The witness fields are hashed into the EIP-712 signature. On-chain, the Permit2 contract verifies that the provided witness data matches what was signed. If a different facilitator tries to submit, the witness check fails.

This is the most secure scheme for multi-facilitator environments and is recommended for production deployments where the facilitator is not self-hosted.

CAIP-2 Network Identifiers

t402 uses CAIP-2 (Chain Agnostic Improvement Proposal) identifiers to specify blockchain networks. This is a universal addressing scheme that works across all blockchain families.

Format: namespace:reference

Network CAIP-2 Identifier Notes
Ethereum Mainnet eip155:1 EIP-155 chain ID
Arbitrum One eip155:42161 Primary production network
Base eip155:8453 Coinbase L2
Optimism eip155:10 OP Stack L2
Polygon eip155:137 Polygon PoS
BNB Smart Chain eip155:56 Legacy USDT
Avalanche C-Chain eip155:43114 Legacy USDT
Solana Mainnet solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp Genesis hash
TON Mainnet ton:mainnet The Open Network
TRON Mainnet tron:0x2b6653dc Genesis block hash
NEAR Mainnet near:mainnet  
Aptos Mainnet aptos:1  
Polkadot Asset Hub polkadot:68d56f15f85d3136970ec16946040bc1 Parachain genesis
Cosmos Noble cosmos:noble-1 Native USDC issuance
Stellar Mainnet stellar:pubnet  
Bitcoin Mainnet bip122:000000000019d6689c085ae165831e93 Genesis block hash

The CAIP-2 identifier is used throughout the protocol — in PaymentRequired, in PaymentSignature, and in routing logic to select the appropriate payment scheme handler.

For EVM networks, the reference is the decimal chain ID from EIP-155. For non-EVM networks, the reference is typically a genesis hash or well-known identifier. The full mapping is maintained in the t402 chain registry.

Facilitator Verification Pipeline

When the resource server receives a PAYMENT-SIGNATURE header, it forwards the signed authorization to a Facilitator for verification and settlement. The Facilitator executes a precise 7-step pipeline. Every step must pass; any failure aborts the entire flow.

graph TD
    A[Receive Payment Signature] --> B{Schema Valid?}
    B -->|No| REJECT[Reject: Invalid Format]
    B -->|Yes| C{Signature Valid?}
    C -->|No| REJECT2[Reject: Bad Signature]
    C -->|Yes| D{Sufficient Balance?}
    D -->|No| REJECT3[Reject: Insufficient Funds]
    D -->|Yes| E{Amount >= Required?}
    E -->|No| REJECT4[Reject: Underpayment]
    E -->|Yes| F{Time Window Valid?}
    F -->|No| REJECT5[Reject: Expired]
    F -->|Yes| G[Simulate Transaction]
    G --> H{Simulation OK?}
    H -->|No| REJECT6[Reject: Would Revert]
    H -->|Yes| I[Broadcast to Blockchain]
    I --> J[Return Settlement Receipt]

Step 1: Schema Validation

Parse the PaymentSignature JSON and validate against the expected schema for the declared scheme. Check that all required fields are present, types are correct, and values are within expected ranges.

REJECT if: malformed JSON, missing fields, invalid types

Step 2: Signature Recovery

For EVM schemes, reconstruct the EIP-712 typed data hash and call ecrecover(hash, v, r, s) to recover the signer address. Verify that the recovered address matches the declared from field.

REJECT if: ecrecover returns zero address, or recovered != declared from

This step catches tampered signatures, wrong chain IDs, wrong contract addresses, and any parameter modification. The EIP-712 hash covers every field — changing any single byte produces a different hash, which recovers a different address.

Step 3: Balance Verification

Query the token contract’s balanceOf(from) to verify the signer has sufficient balance.

REJECT if: balance < amount

This is checked off-chain first to avoid wasting gas on a transaction that would revert. The on-chain state may change between this check and execution (a known TOCTOU issue), which is why Step 6 includes simulation.

Step 4: Amount Matching

Verify that the signed amount matches or exceeds the amount specified in the original PaymentRequired object.

REJECT if: signed amount < required amount

For exact scheme, the amount should match precisely. For upto scheme, the signed amount is the maximum, and the actual charge will be determined later.

Step 5: Time Window Validation

Check that the current time falls within the authorization’s validity window.

REJECT if: now < validAfter OR now > validBefore
REJECT if: (validBefore - validAfter) > maxTimeoutSeconds

The second check prevents clients from creating excessively long-lived authorizations that increase replay risk.

Step 6: Transaction Simulation

Before broadcasting, simulate the transaction using eth_call (or equivalent) against the current blockchain state. This catches issues that off-chain checks might miss: insufficient allowance for Permit2, paused token contracts, blacklisted addresses, etc.

REJECT if: simulation reverts

Step 7: On-Chain Settlement

Broadcast the transaction. Wait for inclusion in a block. Verify the transaction succeeded (status = 1) and emitted the expected transfer event.

REJECT if: transaction reverts, or confirmation timeout exceeded

Upon successful settlement, the Facilitator returns the transaction hash and block number to the resource server, which includes them in the PAYMENT-RESPONSE header.

The total pipeline typically completes in 1-3 seconds on L2 networks (Arbitrum, Base, Optimism) and 12-30 seconds on Ethereum mainnet.

The entire verification pipeline (Steps 1-6) executes in under 100ms on modern hardware. The bottleneck is the blockchain confirmation in Step 7, which ranges from ~250ms on Arbitrum to ~15 seconds on Ethereum L1. This means the protocol’s latency is dominated by the settlement chain the client chooses — the t402 logic itself adds negligible overhead.

Formal Security Model

The t402 whitepaper defines a formal security model with proofs for several critical properties. Here we present the key theorems and their intuitive explanations.

graph LR
    C[Client] -->|"Low trust<br/>(signed auth)"| S[Resource Server]
    S -->|"Low trust<br/>(cannot redirect)"| F[Facilitator]
    F -->|"High trust<br/>(consensus)"| B[Blockchain]
    S -->|"Zero trust<br/>(on-chain proof)"| C
    C -->|"Low trust<br/>(only executes signed)"| F

Theorem 2.1: Facilitator Cannot Redirect Funds

Statement: Given a valid EIP-712 signature (v, r, s) over a TransferWithAuthorization message containing to = payTo, no entity (including the Facilitator) can execute a transfer to any address to' != payTo using this signature.

Proof sketch:

The security chain has three links:

  1. EIP-712 Typed Signature: The client signs a hash H = keccak256(domainSeparator, messageHash) where messageHash includes the to field. The signature (v, r, s) is bound to this exact hash.

  2. ecrecover Verification: On-chain, the smart contract computes H' from the submitted parameters (including to') and calls ecrecover(H', v, r, s). If to' != to, then H' != H, so ecrecover returns an address different from from. The require(recovered == from) check fails.

  3. Atomic Execution: The transferWithAuthorization function executes atomically — either all checks pass and the transfer occurs to to, or the entire transaction reverts. There is no intermediate state where funds are accessible.

Implication: The Facilitator’s only role is to broadcast. It cannot modify any parameter of the transfer — not the recipient, not the amount, not the sender. This is enforced by mathematics (elliptic curve cryptography), not by trust, reputation, or legal agreement.

In practice, this means a compromised facilitator can deny service (refuse to settle) but cannot steal funds. The worst case is a failed payment, not a lost payment.

Replay Attack Prevention

t402 prevents replay attacks through two complementary mechanisms:

Nonce Uniqueness: Each EIP-3009 authorization includes a random 32-byte nonce. The token contract maintains a mapping mapping(address => mapping(bytes32 => bool)) of used nonces. Once a nonce is consumed, any attempt to replay the same signature reverts with authorization already used.

Time Windows: The validAfter / validBefore window bounds the authorization’s lifetime. Even if, hypothetically, a nonce were not tracked (it is), the authorization would expire. Combined with nonces, this creates defense in depth.

For Permit2 schemes, the bitmap nonce provides equivalent uniqueness guarantees with more efficient gas usage for concurrent authorizations.

Double-Spend Prevention

A client might sign two authorizations for the same funds and submit them to two different servers simultaneously.

t402 handles this at two levels:

  1. On-chain: The second transferWithAuthorization call reverts because the nonce was already consumed by the first. Funds cannot be transferred twice.

  2. Facilitator simulation: Step 6 of the verification pipeline simulates the transaction. If another authorization has already consumed the balance, the simulation reverts, and the Facilitator rejects the payment before wasting gas.

The combination ensures that at most one of the two competing payments succeeds, and the server whose payment fails receives an error before delivering the resource.

Man-in-the-Middle Prevention

An attacker who intercepts the PAYMENT-SIGNATURE header in transit obtains a signed authorization. Can they exploit it?

  • Redirect to attacker: Impossible (Theorem 2.1). The to address is locked in the signature.
  • Replay against another server: The nonce is consumed on-chain by the legitimate submission. Replay fails.
  • Modify the amount: Any parameter change invalidates the signature (ecrecover returns wrong address).
  • Use on a different chain: The chainId in the EIP-712 domain separator prevents cross-chain replay.

The only remaining attack vector is a denial of service — the attacker submits the signature before the legitimate facilitator, consuming the nonce and causing the legitimate payment to fail. This is mitigated by HTTPS (preventing interception) and by the short time windows (maxTimeoutSeconds).

HTTP Header Encoding

t402 defines three custom HTTP headers. All three use the same encoding: Base64-encoded UTF-8 JSON.

PAYMENT-REQUIRED (Server to Client)

Sent with HTTP 402 responses. Contains the PaymentRequired object.

PAYMENT-REQUIRED: eyJ0NDAyVmVyc2lvbiI6MiwicmVzb3VyY2UiOnsidXJsIjoiL2FwaS92Mi9tYXJrZXQtZGF0YSJ9LCJhY2NlcHRzIjpbeyJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6NDIxNjEiLCJhbW91bnQiOiIxMDAwMCJ9XX0=

Decoded:

{
  "t402Version": 2,
  "resource": { "url": "/api/v2/market-data" },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:42161",
      "amount": "10000"
    }
  ]
}

PAYMENT-SIGNATURE (Client to Server)

Sent with the retry request after the client signs. Contains the PaymentSignature object:

{
  "accepted": {
    "scheme": "exact",
    "network": "eip155:42161",
    "amount": "10000",
    "asset": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
    "payTo": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
  },
  "signature": {
    "from": "0xClientWallet...",
    "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "value": "10000",
    "validAfter": 1735200000,
    "validBefore": 1735200300,
    "nonce": "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b",
    "v": 28,
    "r": "0x...",
    "s": "0x..."
  }
}

The accepted field echoes the accept object the client chose. The signature field contains the EIP-712 signature and all parameters needed for on-chain submission.

PAYMENT-RESPONSE (Server to Client)

Sent with the 200 OK response after successful settlement:

{
  "success": true,
  "transactionHash": "0x4a5b6c7d8e9f...",
  "blockNumber": 284729384,
  "network": "eip155:42161",
  "settledAmount": "10000",
  "facilitator": "https://facilitator.t402.io"
}

On settlement failure:

{
  "success": false,
  "error": "insufficient_balance",
  "message": "Sender balance 5000 is less than required 10000"
}

The server returns 402 again (not 200) when settlement fails, allowing the client to retry with a different payment option.

Protocol Versioning: V1 vs V2

V1 (Deprecated)

Protocol V1 was the initial implementation. Key differences from V2:

  • Header names: V1 used X-PAYMENT-REQUIRED, X-PAYMENT-SIGNATURE, X-PAYMENT-RESPONSE (with the X- prefix, following an older HTTP convention that has since been deprecated by RFC 6648)
  • Single accept: V1 supported only one payment option per PaymentRequired — no accepts array, just a flat structure with scheme, network, amount at the top level
  • No extensions: The extensions field did not exist. Protocol behavior was fixed.
  • No CAIP-2: Networks were identified by chain ID integers, limiting support to EVM chains only.
  • No resource metadata: V1 had no resource field — the URL was inferred from the HTTP request.

V2 (Current)

V2 is a breaking change from V1. Key improvements:

  • Multiple accepts: Servers offer multiple payment options; clients choose.
  • CAIP-2 networks: Universal chain identification supporting non-EVM networks.
  • Extension system: Modular protocol extensions.
  • Resource metadata: Explicit resource identification for signature binding.
  • Clean header names: No X- prefix.

Migration: V2 servers should check the t402Version field. If absent, treat the request as V1. The facilitator API remains backward-compatible for V1 signatures, but new features (extensions, multi-chain) require V2.

Extensions

t402 is designed to be extended. Extensions modify or augment the base protocol behavior. They are declared in the extensions array of each accept object.

Bazaar

The Bazaar extension enables resource discovery. Servers expose a /.well-known/t402-bazaar.json endpoint listing all paid resources, their prices, and descriptions:

{
  "name": "Market Data API",
  "description": "Real-time cryptocurrency market data",
  "resources": [
    {
      "url": "/api/v2/market-data",
      "method": "GET",
      "description": "BTC/USD real-time feed",
      "price": "0.01 USDT",
      "network": "eip155:42161"
    },
    {
      "url": "/api/v2/historical",
      "method": "GET",
      "description": "Historical OHLCV data",
      "price": "0.05 USDT",
      "network": "eip155:42161"
    }
  ]
}

This is critical for AI agents. An MCP-enabled agent can discover paid APIs, evaluate pricing, and decide whether to pay — all programmatically, without human intervention.

Payment Identifier

The Payment Identifier extension adds a unique identifier to each payment flow, enabling correlation between the initial 402 response, the PAYMENT-SIGNATURE submission, and the final settlement. Useful for logging, debugging, and audit trails.

{
  "extensions": ["payment-identifier"],
  "extra": {
    "paymentId": "pay_2025_abc123def456"
  }
}

Sign-In-With-X

The Sign-In-With-X extension repurposes the t402 signature flow for authentication. Instead of transferring tokens, the client signs a message proving wallet ownership. The server can use this to establish a session — essentially combining EIP-4361 (Sign-In with Ethereum) with the t402 request flow.

This enables a unified flow: pay and authenticate in a single HTTP round-trip.

ERC-8004

The ERC-8004 extension integrates with ERC-8004 (Native Token Paymaster) for ERC-4337 account abstraction. It allows smart contract wallets (not just EOAs) to authorize t402 payments through UserOperations, enabling:

  • Multi-sig wallets authorizing payments
  • Social recovery wallets
  • Spending policy enforcement (daily limits, whitelisted recipients)
  • Gasless client-side experience via paymasters

This is particularly relevant for AI agents, which may operate through smart contract wallets with programmatic spending policies rather than raw private keys.

Putting It All Together

Let us trace a complete payment flow with all the pieces we have covered.

1. Client requests a resource:

GET /api/v2/market-data HTTP/1.1
Host: data.example.com

2. Server returns 402 with PaymentRequired:

HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: <base64-encoded PaymentRequired>

The decoded object offers two options: Arbitrum (exact scheme) and Base (exact scheme).

3. Client selects Arbitrum, constructs EIP-712 typed data:

Domain: USDT on Arbitrum. Message: TransferWithAuthorization with to = payTo, random nonce, 5-minute window. Signs with eth_signTypedData_v4.

4. Client resubmits with signature:

GET /api/v2/market-data HTTP/1.1
Host: data.example.com
PAYMENT-SIGNATURE: <base64-encoded PaymentSignature>

5. Server forwards to Facilitator, which runs the 7-step pipeline:

Schema validation, signature recovery, balance check, amount match, time window, simulation, settlement. Transaction confirmed on Arbitrum in ~1 second.

6. Server returns resource with settlement proof:

HTTP/1.1 200 OK
PAYMENT-RESPONSE: <base64-encoded PaymentResponse with txHash>
Content-Type: application/json

{"btc_usd": 108234.56, "timestamp": 1735200002}

Total time: under 3 seconds. Cost to client: 0.01 USDT. Gas paid by facilitator. No account creation, no API keys, no OAuth, no webhooks. Just HTTP and cryptographic signatures.


Next in Series

  1. t402: The Internet Finally Gets a Payment Protocol — Part 1: Why t402 exists
  2. t402 Protocol Deep Dive: How HTTP 402 Actually Works — You are here
  3. Building with t402: From Zero to Paid API in 10 Minutes — Hands-on developer tutorial with the SDK and sandbox
  4. t402 and the AI Economy: Machine-to-Machine Payments — MCP integration, A2A transport, ERC-8004 agent identity
  5. t402 Multi-Chain Architecture: 52 Networks, One Protocol — Chain mechanisms, ERC-4337 gasless, USDT0 cross-chain bridging

References


t402 is open source under the Apache 2.0 license. The protocol specification and whitepaper are available under the MIT license.