Skip to main content
Unfamiliar with terms like EIP-3009, x402, AccessGrant, or ChallengeEngine? Read Core Concepts first.
Key0 uses a three-phase payment flow to gate API access behind on-chain USDC payments on Base. Every transaction follows the same pattern regardless of transport.
1

Discover Plans

The client calls GET /discover to browse available plans and pricing. This returns all plan IDs, USDC amounts, and the seller’s wallet address. No payment or challenge is created.
2

Request Access and Pay

The client sends POST /x402/access with a planId. The server creates a PENDING challenge and returns a 402 with the payment amount, destination wallet, and chain ID. The client signs an EIP-3009 authorization off-chain and retries with a payment-signature header. The server settles on-chain.
3

Access the Resource

The server returns an AccessGrant containing a JWT and the resource endpoint. The client uses the JWT as a Bearer token to call the protected API.

Sequence Diagram

Phase 1 — Discovery

The client calls GET /discover to retrieve the full plan catalog. The response contains one entry per configured plan with its planId, USDC amount, seller wallet, and chain ID. No challenge record is created.
POST /x402/access without a planId returns HTTP 400 with a pointer to GET /discover — it is not the discovery endpoint.

Phase 2 — Challenge

The client sends POST /x402/access with a resourceId and planId. The server looks up the matching plan, creates a PENDING challenge record in the challenge store, and returns a 402 containing:
  • amount — the USDC amount to pay (in base units)
  • destination — the seller’s wallet address
  • chainId8453 (Base mainnet) or 84532 (Base Sepolia)
  • challengeId — a unique identifier for this payment session

Phase 3 — Settlement and Grant

The client signs an EIP-3009 authorization off-chain and retries POST /x402/access with the same planId + requestId plus a payment-signature header. The server then:
  1. Verifies the ERC-20 Transfer event on Base matches the expected amount, destination, and chain.
  2. Transitions the challenge from PENDING to PAID (atomic, prevents double-spend).
  3. Calls the seller’s fetchResourceCredentials callback to issue a credential (JWT, API key, or any token).
  4. Transitions from PAID to DELIVERED and returns an AccessGrant with the token and resource URL.
All state transitions go through ChallengeEngine, which enforces the state machine invariants and logs every transition for auditability.

Pay-Per-Call Variant

For paid routes, the flow differs slightly: no JWT is issued. Instead, Key0 returns the API response directly.

Standalone (proxy mode)

Add proxyTo (or fetchResource) to SellerConfig. Clients call the paid route directly, receive a 402 challenge, then retry the same route with payment-signature.

Embedded (inline settlement)

Use key0.payPerRequest(routeId) middleware on each route. No proxyTo needed — Key0 settles on-chain and calls next(), so your route handler runs as normal. Both variants share the same PENDING → PAID → DELIVERED lifecycle with automatic refunds if delivery fails after on-chain settlement. See Payment Flow for the full state machine.

Two Endpoints, One Engine

Both entry points share the same ChallengeEngine instance, so payment logic, state management, and security invariants are identical regardless of how the client connects.
EndpointUse Case
POST /x402/accessUnified: x402 HTTP flow (default), A2A JSON-RPC (with X-A2A-Extensions header), or standalone per-request proxy
POST /mcpMCP Streamable HTTP (opt-in via mcp: true)
GET /api/*Embedded per-request — each route has key0.payPerRequest() middleware

Next Steps

Paying for Access

The buyer’s perspective: sign EIP-3009, submit payment, and use the access token.

Payment Flow Details

Full walkthrough of every state transition and error path.

State Machine

The complete PENDING / PAID / DELIVERED / EXPIRED / REFUNDED state diagram.

Settlement Strategies

Direct transfer vs. EIP-3009 authorization and facilitator settlement.