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

# x402 Protocol

> Pay-per-use USDC micropayments for AI agents — no subscription required

LimitGuard implements the x402 V2 micropayment protocol for AI agent pay-per-use access. No subscription or API key required — agents pay per call in USDC.

## Overview

x402 extends HTTP with a payment layer. The flow is simple:

<Steps>
  <Step title="Request without payment">
    Agent makes a normal API request.
  </Step>

  <Step title="Receive HTTP 402">
    Server returns payment requirements — chain, amount, recipient, contract address.
  </Step>

  <Step title="Build payment signature">
    Agent constructs an EIP-3009 `TransferWithAuthorization` signature.
  </Step>

  <Step title="Retry with X-PAYMENT header">
    Agent retries the same request with the base64-encoded payment header.
  </Step>

  <Step title="Receive response">
    Server verifies signature, processes the request, and returns the result.
  </Step>
</Steps>

## Supported Networks

| Network        | CAIP-2 Chain ID                           | USDC Contract                                  |
| -------------- | ----------------------------------------- | ---------------------------------------------- |
| Base Mainnet   | `eip155:8453`                             | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`   |
| Base Sepolia   | `eip155:84532`                            | `0x036CbD53842c5426634e7929541eC2318f3dCF7e`   |
| Solana Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |
| Solana Devnet  | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` |

## Step 1: Discover Payment Requirements

Make a request without payment to get the requirements:

```bash theme={null}
curl -X POST https://api.limitguard.ai/v1/entity/check \
  -H "Content-Type: application/json" \
  -d '{"entity_name": "Acme Corp BV", "country": "NL"}'
```

```json HTTP 402 Response theme={null}
{
  "x402Version": "2",
  "error": "Payment Required",
  "accepts": [
    {
      "chainId": "eip155:8453",
      "currency": "USDC",
      "contractAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "50000",
      "decimals": 6,
      "recipient": "0xFacilitatorAddress",
      "description": "LimitGuard API call (0.05 USDC)"
    },
    {
      "chainId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "currency": "USDC",
      "contractAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "amount": "50000",
      "decimals": 6,
      "recipient": "SolanaFacilitatorAddress",
      "description": "LimitGuard API call (0.05 USDC)"
    }
  ]
}
```

## Step 2: Build EVM Payment (Base / EIP-3009)

<CodeGroup>
  ```python Python theme={null}
  import base64, json, secrets, time
  from eth_account import Account
  from eth_account.messages import encode_typed_data

  def build_x402_payment(
      chain_id: str,          # e.g. "eip155:8453"
      amount: int,            # in USDC 6-decimal units
      sender_key: str,        # private key (hex)
      recipient: str,         # facilitator address
      usdc_address: str,      # USDC contract address
  ) -> str:
      sender = Account.from_key(sender_key).address
      chain_num = int(chain_id.split(":")[1])  # 8453

      nonce = "0x" + secrets.token_hex(32)
      now = int(time.time())

      # EIP-712 domain
      domain = {
          "name": "USD Coin",
          "version": "2",
          "chainId": chain_num,
          "verifyingContract": usdc_address,
      }

      # EIP-3009 TransferWithAuthorization
      message = {
          "from": sender,
          "to": recipient,
          "value": amount,
          "validAfter": now - 10,
          "validBefore": now + 300,
          "nonce": bytes.fromhex(nonce[2:]),
      }

      types = {
          "EIP712Domain": [
              {"name": "name", "type": "string"},
              {"name": "version", "type": "string"},
              {"name": "chainId", "type": "uint256"},
              {"name": "verifyingContract", "type": "address"},
          ],
          "TransferWithAuthorization": [
              {"name": "from", "type": "address"},
              {"name": "to", "type": "address"},
              {"name": "value", "type": "uint256"},
              {"name": "validAfter", "type": "uint256"},
              {"name": "validBefore", "type": "uint256"},
              {"name": "nonce", "type": "bytes32"},
          ],
      }

      signable = encode_typed_data(domain, types, "TransferWithAuthorization", message)
      signed = Account.sign_message(signable, private_key=sender_key)

      payload = {
          "chainId": chain_id,
          "amount": str(amount),
          "sender": sender,
          "recipient": recipient,
          "nonce": nonce,
          "signature": signed.signature.hex(),
          "validAfter": now - 10,
          "validBefore": now + 300,
      }

      return base64.b64encode(json.dumps(payload).encode()).decode()
  ```

  ```javascript JavaScript theme={null}
  import { ethers } from "ethers";

  async function buildX402Payment(
    chainId,      // "eip155:8453"
    amount,       // 50000 (USDC 6-decimal)
    signer,       // ethers.Wallet
    recipient,    // facilitator address
    usdcAddress   // USDC contract
  ) {
    const chainNum = parseInt(chainId.split(":")[1]);
    const nonce = "0x" + [...crypto.getRandomValues(new Uint8Array(32))]
      .map((b) => b.toString(16).padStart(2, "0")).join("");
    const now = Math.floor(Date.now() / 1000);

    const domain = {
      name: "USD Coin",
      version: "2",
      chainId: chainNum,
      verifyingContract: usdcAddress,
    };

    const types = {
      TransferWithAuthorization: [
        { name: "from", type: "address" },
        { name: "to", type: "address" },
        { name: "value", type: "uint256" },
        { name: "validAfter", type: "uint256" },
        { name: "validBefore", type: "uint256" },
        { name: "nonce", type: "bytes32" },
      ],
    };

    const message = {
      from: signer.address,
      to: recipient,
      value: amount,
      validAfter: now - 10,
      validBefore: now + 300,
      nonce: nonce,
    };

    const signature = await signer.signTypedData(domain, types, message);

    const payload = {
      chainId,
      amount: String(amount),
      sender: signer.address,
      recipient,
      nonce,
      signature,
      validAfter: now - 10,
      validBefore: now + 300,
    };

    return btoa(JSON.stringify(payload));
  }
  ```
</CodeGroup>

## Step 3: Make Paid Request

<CodeGroup>
  ```python Python theme={null}
  import httpx

  x_payment = build_x402_payment(
      chain_id="eip155:8453",
      amount=50_000,           # $0.05 for /v1/entity/check (fresh)
      sender_key="0xYOUR_PRIVATE_KEY",
      recipient="0xFacilitatorFromThe402Response",
      usdc_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  )

  response = httpx.post(
      "https://api.limitguard.ai/v1/entity/check",
      headers={"X-PAYMENT": x_payment, "Content-Type": "application/json"},
      json={"entity_name": "Acme Corp BV", "country": "NL"},
  )
  print(response.json())
  ```

  ```bash curl theme={null}
  curl -X POST https://api.limitguard.ai/v1/entity/check \
    -H "X-PAYMENT: <base64-encoded-payment>" \
    -H "Content-Type: application/json" \
    -d '{"entity_name": "Acme Corp BV", "country": "NL"}'
  ```
</CodeGroup>

## Response Headers

On successful payment verification, these headers are included:

| Header               | Value   | Meaning                          |
| -------------------- | ------- | -------------------------------- |
| `X-Payment-Verified` | `true`  | Payment accepted                 |
| `X-Payment-Amount`   | `50000` | Amount verified (6-decimal USDC) |
| `X-Payment-Fallback` | `true`  | Circuit breaker triggered (rare) |

## Replay Prevention

Each nonce can only be used once. Nonces are tracked in Redis with a 5-minute TTL. Reusing a nonce returns:

```json theme={null}
HTTP 402
{
  "error": "Nonce already used (replay detected)"
}
```

## Quality Tiers with x402

Control cost via the `X-Response-Quality` header. The amount in `X-PAYMENT` must match or exceed the price for the selected tier:

| Tier       | /v1/entity/check | /v1/risk/score  | /v1/kyb/check      |
| ---------- | ---------------- | --------------- | ------------------ |
| `cached`   | \$0.01 (10,000)  | \$0.005 (5,000) | \$0.25 (250,000)   |
| `fresh`    | \$0.05 (50,000)  | \$0.01 (10,000) | \$1.25 (1,250,000) |
| `enhanced` | \$0.15 (150,000) | \$0.05 (50,000) | \$2.50 (2,500,000) |

<Note>
  If the payment amount is insufficient for the requested tier, you'll receive HTTP 402 with the correct amount.
</Note>

## Circuit Breaker Fallback

If the payment facilitator is unavailable (circuit breaker open), LimitGuard falls back to a verified wallet cache. Wallets that have previously completed verified payments are cached. The response includes `X-Payment-Fallback: true` when this path is used.

## Discovery

AI agents can discover LimitGuard's x402 payment requirements automatically:

```bash theme={null}
curl https://api.limitguard.ai/.well-known/x402.json
```

Returns a Bazaar-compatible service listing with all endpoints, pricing, and accepted payment methods.

## V1 Backward Compatibility

The older `PAYMENT-SIGNATURE` header (V1 format) is still accepted for backward compatibility. V2 (`X-PAYMENT`) is recommended for all new integrations.
