> ## Documentation Index
> Fetch the complete documentation index at: https://docs.key0.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# How It Works

> Key0's two-phase payment flow: challenge, pay, access.

<Note>
  Unfamiliar with terms like EIP-3009, x402, AccessGrant, or ChallengeEngine? Read [Core Concepts](/introduction/core-concepts) first.
</Note>

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.

<Steps>
  <Step title="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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

## Sequence Diagram

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server
    participant Base as Base (L2)

    Note over Client,Server: Phase 1 -- Discovery
    Client->>Server: GET /discover
    Server->>Client: 200 + plan catalog (planIds, amounts, wallet)

    Note over Client,Server: Phase 2 -- Challenge
    Client->>Server: POST /x402/access { planId }
    Server-->>Server: Create PENDING challenge
    Server->>Client: 402 + PaymentRequirements + challengeId

    Note over Client,Base: Phase 3 -- Settlement
    Client->>Server: POST /x402/access { planId } + payment-signature
    Server->>Base: Execute transferWithAuthorization (EIP-3009)
    Base-->>Server: txHash confirmed
    Server-->>Server: PENDING → PAID → DELIVERED
    Server-->>Server: Issue credentials (fetchResourceCredentials)
    Server->>Client: AccessGrant (JWT, resourceUrl)

    Note over Client,Server: Access protected resource
    Client->>Server: GET /api/resource (Bearer JWT)
    Server->>Client: Protected data
```

## 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.

<Note>
  `POST /x402/access` without a `planId` returns HTTP **400** with a pointer to `GET /discover` — it is not the discovery endpoint.
</Note>

## 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
* **chainId** -- `8453` (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`.

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Key0
    participant Backend

    Note over Client,Key0: Phase 1 — Challenge
    Client->>Key0: GET /api/weather/london
    Key0-->>Key0: Create PENDING challenge
    Key0->>Client: 402 + PaymentRequirements

    Note over Client,Backend: Phase 2 — Settlement & Proxy
    Client->>Key0: GET /api/weather/london + payment-signature
    Key0->>Key0: Settle on-chain (PENDING → PAID)
    Key0->>Backend: Forward request (injects x-key0-tx-hash, x-key0-plan-id)
    Backend->>Key0: Response data
    Key0-->>Key0: PAID → DELIVERED
    Key0->>Client: ResourceResponse (backend data, no token)
```

### 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.

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant App

    Note over Client,App: No payment header → 402
    Client->>App: GET /api/weather/london
    App->>Client: 402 + PaymentRequirements

    Note over Client,App: With PAYMENT-SIGNATURE header
    Client->>App: GET /api/weather/london + payment-signature
    App->>App: Settle on-chain, PAID → DELIVERED
    App->>Client: 200 { city, temp } (your handler's response)
```

Both variants share the same `PENDING → PAID → DELIVERED` lifecycle with automatic refunds if delivery fails after on-chain settlement. See [Payment Flow](/architecture/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.

| Endpoint            | Use Case                                                                                                          |
| ------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `POST /x402/access` | Unified: x402 HTTP flow (default), A2A JSON-RPC (with `X-A2A-Extensions` header), or standalone per-request proxy |
| `POST /mcp`         | MCP Streamable HTTP (opt-in via `mcp: true`)                                                                      |
| `GET /api/*`        | Embedded per-request — each route has `key0.payPerRequest()` middleware                                           |

## Next Steps

<CardGroup cols={2}>
  <Card title="Paying for Access" icon="credit-card" href="/guides/paying-for-access">
    The buyer's perspective: sign EIP-3009, submit payment, and use the access token.
  </Card>

  <Card title="Payment Flow Details" icon="diagram-project" href="/architecture/payment-flow">
    Full walkthrough of every state transition and error path.
  </Card>

  <Card title="State Machine" icon="arrows-spin" href="/architecture/state-machine">
    The complete PENDING / PAID / DELIVERED / EXPIRED / REFUNDED state diagram.
  </Card>

  <Card title="Settlement Strategies" icon="link" href="/architecture/settlement-strategies">
    Direct transfer vs. EIP-3009 authorization and facilitator settlement.
  </Card>
</CardGroup>
