t402 Protocol Deep Dive: How HTTP 402 Actually Works
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
PaymentRequiredas thePAYMENT-REQUIREDheader (Base64-encoded JSON) - Decoding
PAYMENT-SIGNATUREfrom the client request - Encoding
PaymentResponseas thePAYMENT-RESPONSEheader
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 toGET. Important for distinguishingGET /data(read, might cost ₮0.01) fromPOST /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 withnetworkbut 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 beecrecover‘d from the signature on-chain. -
to: ThepayToaddress from thePaymentRequiredobject. This is the field that prevents facilitator fund redirection — it is locked into the signature. -
value: The exact payment amount. Must match theamountin 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 tovalidAfter+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:
- Reconstructs the EIP-712 hash from the parameters
- Calls
ecrecover(hash, v, r, s)to recover the signer - Verifies
recovered == from - Checks
block.timestamp > validAfter && block.timestamp < validBefore - Checks the nonce has not been used
- Marks the nonce as used
- Transfers
valuetokens fromfromtoto
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:
-
spenderis the facilitator contract, not the merchant. The facilitator is authorized to transfer up tovaluetokens. -
nonceis 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
- Client signs
permitfor max amount (e.g., ₮1.00) - Server delivers resource, measures usage (e.g., 45 KB at ₮0.001/KB = ₮0.045)
- Facilitator calls
permit()to set allowance, thentransferFrom()for actual amount (₮0.045) - 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:
- Verifies the signature
- Checks nonce and deadline
- 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:
-
EIP-712 Typed Signature: The client signs a hash
H = keccak256(domainSeparator, messageHash)wheremessageHashincludes thetofield. The signature(v, r, s)is bound to this exact hash. -
ecrecover Verification: On-chain, the smart contract computes
H'from the submitted parameters (includingto') and callsecrecover(H', v, r, s). Ifto' != to, thenH' != H, soecrecoverreturns an address different fromfrom. Therequire(recovered == from)check fails. -
Atomic Execution: The
transferWithAuthorizationfunction executes atomically — either all checks pass and the transfer occurs toto, 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:
-
On-chain: The second
transferWithAuthorizationcall reverts because the nonce was already consumed by the first. Funds cannot be transferred twice. -
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
toaddress 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
chainIdin 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 theX-prefix, following an older HTTP convention that has since been deprecated by RFC 6648) - Single accept: V1 supported only one payment option per
PaymentRequired— noacceptsarray, just a flat structure withscheme,network,amountat the top level - No extensions: The
extensionsfield did not exist. Protocol behavior was fixed. - No CAIP-2: Networks were identified by chain ID integers, limiting support to EVM chains only.
- No
resourcemetadata: V1 had noresourcefield — 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
- t402: The Internet Finally Gets a Payment Protocol — Part 1: Why t402 exists
- t402 Protocol Deep Dive: How HTTP 402 Actually Works — You are here
- Building with t402: From Zero to Paid API in 10 Minutes — Hands-on developer tutorial with the SDK and sandbox
- t402 and the AI Economy: Machine-to-Machine Payments — MCP integration, A2A transport, ERC-8004 agent identity
- t402 Multi-Chain Architecture: 52 Networks, One Protocol — Chain mechanisms, ERC-4337 gasless, USDT0 cross-chain bridging
References
- t402 Whitepaper — Full 153-page technical specification
- t402 Documentation — Developer guides and API reference
- EIP-712: Typed Structured Data Hashing and Signing
- EIP-3009: Transfer With Authorization
- EIP-2612: Permit
- Uniswap Permit2
- CAIP-2: Blockchain ID Specification
- GitHub: t402-io/t402
t402 is open source under the Apache 2.0 license. The protocol specification and whitepaper are available under the MIT license.