ChallengeEngine and state machine as the other transports. The difference is the wire format: JSON-RPC task lifecycle with x402 metadata keys instead of HTTP headers.
Flow Overview
Discovery
The client fetches
/.well-known/agent.json to learn about available skills, pricing plans, and the seller’s wallet address.Challenge
The server creates a PENDING record in the challenge store and returns an
X402Challenge with the payment amount, destination wallet, chain ID, and expiration.Payment
The client pays on-chain USDC on Base — a standard ERC-20 transfer. No custom contracts or off-chain signatures required.
Proof
The client submits a
PaymentProof containing the transaction hash via a second message/send with x402 payment metadata.Verification
The server verifies the payment on-chain: correct recipient, correct amount, not expired, not double-spent. On success, the challenge transitions from PENDING to PAID.
Grant
The server calls
fetchResourceCredentials to issue a credential (JWT, API key, etc.), transitions PAID to DELIVERED, and returns an AccessGrant with the token and resource endpoint URL.Sequence Diagram
A2A Protocol Details
The A2A flow usesKey0Executor, which implements the AgentExecutor interface from @a2a-js/sdk. The payment negotiation happens across two message/send round-trips, using task metadata to carry x402 payment state.
Phase 1 — AccessRequest to Challenge
The client sends amessage/send JSON-RPC request with an AccessRequest in the message parts:
engine.requestAccess() and publishes a Task with:
- State:
input-required— indicating the client needs to take action (pay) - Metadata:
x402.payment.statusset to"payment-required"andx402.payment.requiredcontaining the fullPaymentRequirementsobject - Parts: a text description of the challenge plus the
X402Challengeas a data part
Phase 2 — Payment to Grant
After paying on-chain, the client sends a secondmessage/send with the payment payload in task metadata:
- Extracts
challengeIdfrompayload.accepted.extra.challengeId - Publishes working Task with
x402.payment.status: "payment-submitted" - Calls
settlePayment()to verify and settle on-chain - Publishes working Task with
x402.payment.status: "payment-verified" - Calls
engine.processHttpPayment()to transition PENDING to PAID to DELIVERED - Publishes final Task:
- State:
completed - Metadata:
x402.payment.status: "payment-completed"andx402.payment.receiptswith the settlement receipt - Parts: confirmation text and the
AccessGrantdata part - Artifacts: the
AccessGrantas a data artifact
- State:
x402 Metadata Keys
All payment state is communicated through task metadata using these keys:| Key | Value | Direction |
|---|---|---|
x402.payment.status | payment-required / payment-submitted / payment-verified / payment-completed / payment-failed | Server to Client |
x402.payment.required | PaymentRequirements object (plans, wallet, chain) | Server to Client |
x402.payment.payload | X402PaymentPayload object (signature, authorization) | Client to Server |
x402.payment.receipts | Array of X402SettleResponse (txHash, status) | Server to Client |
x402.payment.error | Error code string (e.g. CHALLENGE_EXPIRED) | Server to Client |
A2A Headers
Native A2A clients send theX-A2A-Extensions header, which tells the x402 HTTP middleware to pass the request through to the A2A SDK and Key0Executor. Without this header, the same {basePath}/jsonrpc endpoint is handled by the x402 middleware directly.
Next Steps
x402 HTTP Flow
The simpler REST-based payment flow using HTTP 402 status codes.
State Machine
Full state diagram covering PENDING, PAID, DELIVERED, EXPIRED, and REFUNDED states.
Settlement Strategies
Gas wallet vs. facilitator settlement and EIP-3009 authorization.
Refunds
Automatic refund handling for failed deliveries.

