Skip to main content
The primary x402 HTTP endpoint. It handles four distinct cases depending on the request body and headers:
  1. No planId — Returns HTTP 400 directing the client to GET /discover.
  2. ChallengeplanId in the body, no PAYMENT-SIGNATURE header. Returns HTTP 402 with payment requirements and a challengeId.
  3. Settlement (subscription)planId in the body and a PAYMENT-SIGNATURE header. Settles the payment on-chain and returns HTTP 200 with an AccessGrant (JWT).
  4. Settlement (route-based standalone) — Same as (3) but with a routeId and resource in the body. Key0 proxies to the backend and returns a ResourceResponse (backend data, no token).

Case 1: No planId

Sending a body without planId returns HTTP 400 with a pointer to the discovery endpoint.

Case 2: Challenge

Send a planId in the body without a PAYMENT-SIGNATURE header to purchase a subscription. For standalone route calls, send routeId plus a resource field without a PAYMENT-SIGNATURE header. The server creates a PENDING challenge record and returns payment requirements.
Subscription plan:
Route-based standalone call:

Case 3: Settlement

Resend the same request with a PAYMENT-SIGNATURE header containing a base64url-encoded X402PaymentPayload. The server settles the payment on-chain and returns either an AccessGrant (subscription) or a ResourceResponse (route-based standalone).
The PAYMENT-SIGNATURE header is a base64url-encoded JSON object with the following structure:
The accepted field echoes back the payment requirements from the 402 response. The payload.signature is the EIP-3009 transferWithAuthorization signature. The payload.authorization contains the EIP-3009 parameters.
If planId or routeId is missing from the request body but present in the PAYMENT-SIGNATURE payload’s accepted.extra, the server extracts it automatically. This supports standard x402 clients that replay the exact same request with only the header added.

Settlement Strategies

Key0 supports two settlement strategies, configured via SellerConfig:

Case 4: Route-Based Standalone Response

When settlement succeeds for a route-based call (with proxyTo or fetchResource configured), the response is a ResourceResponse containing the backend’s data — no JWT is issued:
If the backend returns a non-2xx status, the challenge remains PAID (awaiting refund) and the response includes the backend’s error body.

Pre-Settlement Check

Before settling a payment, the server checks for existing challenge records:
  • Already DELIVERED: Returns the cached AccessGrant immediately (HTTP 200). No on-chain transaction.
  • EXPIRED or CANCELLED: Returns an error (HTTP 410). The client must start a new flow.
  • PENDING or PAID: Proceeds with settlement.
This prevents burning USDC on duplicate settlements.

Error Responses


A2A-Native Clients (Express only)

When using the Express integration, the same POST /x402/access endpoint also handles A2A JSON-RPC requests. A2A-native clients (e.g. the Google ADK A2AClient) signal their intent by including the X-A2A-Extensions header in the request:
When this header is detected, the Express middleware skips the x402 HTTP flow and delegates the request directly to the A2A JSON-RPC handler (Key0Executor). The response conforms to the A2A protocol (task lifecycle with AccessRequest and payment metadata).
Header-based A2A routing is Express only. The Hono and Fastify integrations serve the standard x402 HTTP flow on POST /x402/access only. A2A-native agents should use the Express integration or deploy the Key0 standalone Docker image.

x402 HTTP Flow

End-to-end walkthrough of the x402 payment protocol over HTTP.

A2A Flow

How A2A-native agents discover and pay for access via Key0Executor.

Data Models

TypeScript types for X402PaymentPayload, X402SettleResponse, and more.

ChallengeEngine

The state machine that processes challenges created by this endpoint.