ChallengeEngine and state machine as the x402 HTTP flow. The difference is the wire format: A2A task lifecycle with x402 metadata keys instead of HTTP headers. A2A-native clients reach the Key0Executor via POST /x402/access with the X-A2A-Extensions header (Express only).
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 on POST /x402/access. This tells the Express integration to bypass the standard x402 HTTP flow and route the request to the A2A JSON-RPC handler and Key0Executor.
Next Steps
Express Integration
A2A is only available on Express — see setup details and the
X-A2A-Extensions header.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.

