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

# x402 + Session Keys

> Automated x402 payments using SNIP-9 session keys

## The Killer Combo

x402 payments are USDC transfers. Session keys on CHIPI wallets authorize transfers without requiring the owner key each time.

| Mode                | User Experience                                                 |
| ------------------- | --------------------------------------------------------------- |
| **Without session** | Each payment requires signing (wallet popup / biometrics / PIN) |
| **With session**    | Payments happen automatically — zero interaction per request    |

This makes x402 + sessions ideal for:

* AI agent autonomous API consumption
* Streaming data feeds (pay-per-query)
* Backend automation (server-to-server payments)
* Mobile apps with "subscribe for X hours" UX

## React Implementation

```typescript theme={null}
import { useChipiSession, useX402Payment } from "@chipi-stack/nextjs";

function AutoPayContent() {
  // Step 1: Set up session
  const {
    session,
    hasActiveSession,
    createSession,
    registerSession,
  } = useChipiSession({
    wallet,
    encryptKey: pin,
    getBearerToken: getToken,
  });

  // Step 2: Pass session to x402
  const { x402Fetch, isPaying, paymentCount } = useX402Payment({
    wallet,
    encryptKey: pin,
    getBearerToken: getToken,
    session,  // Session key signs payments automatically
    maxPaymentAmount: "0.50",
  });

  // Step 3: Set up session (one-time)
  const setupSession = async () => {
    await createSession();
    await registerSession({
      allowedEntrypoints: ["transfer"],  // Only USDC transfers
    });
  };

  // Step 4: Use — payments are fully automatic
  const fetchData = async () => {
    // No popup, no PIN, no interaction
    const response = await x402Fetch("https://api.example.com/ai-query?q=...");
    return response.json();
  };

  return (
    <div>
      {!hasActiveSession ? (
        <button onClick={setupSession}>Enable Auto-Pay (6 hours)</button>
      ) : (
        <div>
          <button onClick={fetchData} disabled={isPaying}>
            Query AI API
          </button>
          <p>{paymentCount} queries paid</p>
        </div>
      )}
    </div>
  );
}
```

## How It Works Under the Hood

1. **Session creation**: Generate a temporary keypair, encrypt private key with user's PIN
2. **Session registration**: Register the session public key on the wallet contract (one owner signature)
3. **Session constraint**: `allowedEntrypoints: ["transfer"]` restricts the session to only USDC transfer calls
4. **x402 payment**: When a 402 is received, the hook uses `executeTransactionWithSession()` instead of `executeTransaction()` — the session key signs automatically

```
User sets up session (one-time PIN entry)
         |
         v
[Session key registered on wallet contract]
         |
         v
x402Fetch("https://api.example.com/data")
         |
         v
Server returns 402 → parse requirement → sign with session key → retry
         |                                     ^
         |                                     |
         +-- No user interaction! Session key signs automatically
```

## Node.js Backend Automation

For server-side automation where you want to consume paid APIs:

```typescript theme={null}
import { X402Client, DirectSigner, createAccount } from "@chipi-stack/core";
import { ChipiServerSDK } from "@chipi-stack/backend";

const sdk = new ChipiServerSDK({
  apiPublicKey: process.env.CHIPI_PUBLIC_KEY!,
  apiSecretKey: process.env.CHIPI_SECRET_KEY!,
});

// Create session for automated payments
const session = sdk.sessions.createSessionKey({
  encryptKey: process.env.ENCRYPT_KEY!,
  durationSeconds: 3600, // 1 hour
});

// Register session with transfer-only permissions
await sdk.sessions.addSessionKeyToContract({
  encryptKey: process.env.ENCRYPT_KEY!,
  wallet: myWallet,
  sessionConfig: {
    sessionPublicKey: session.publicKey,
    validUntil: session.validUntil,
    maxCalls: 100,
    allowedEntrypoints: ["transfer"],
  },
}, bearerToken);

// Now use session for automated x402 payments
const signer = new DirectSigner(decryptedSessionKey);
const account = await createAccount({ signer, provider });
const client = new X402Client(account, { maxPaymentAmount: "0.10" });

// Automated — no human interaction needed
for (const query of queries) {
  const response = await client.fetch(`https://api.example.com/ai?q=${query}`);
  const result = await response.json();
  // Process result...
}
```

## On-Chain Spending Limits

Session keys alone don't enforce dollar limits — `maxCalls` caps the number of transactions, not the amount. For real budget control, use **Spending Policies**:

```typescript theme={null}
// After registering the session, set on-chain limits
await setSpendingPolicy({
  token: USDC_ADDRESS,
  maxPerCall: 100_000n,        // Max $0.10 per x402 payment (6 decimals)
  maxPerWindow: 50_000_000n,   // Max $50 per day
  windowSeconds: 86400,        // 24h rolling window
});
```

The contract enforces this automatically — if a payment exceeds the per-call limit or the rolling window total, the transaction reverts on-chain. No middleware or backend enforcement needed.

<Info>
  Spending policies require **CHIPI v33** wallets. See the full guide: [Spending Policies](/sdk/guides/spending-policies).
</Info>

### Three Layers of Protection

| Layer                           | Scope                                  | Enforced By                         |
| ------------------------------- | -------------------------------------- | ----------------------------------- |
| `maxPaymentAmount` (X402Client) | Per-request, client-side               | SDK (can be bypassed)               |
| `maxCalls` (Session)            | Total transaction count                | Smart contract                      |
| **Spending Policy**             | Per-call amount + rolling window total | Smart contract (cannot be bypassed) |

For production x402 deployments, use all three.

## Security Considerations

### Session Scope

Always restrict session keys to the minimum required permissions:

```typescript theme={null}
await registerSession({
  allowedEntrypoints: ["transfer"],  // Only allow transfers
  maxCalls: 100,                     // Limit total calls
});

// Then add on-chain spending limits
await setSpendingPolicy({
  token: USDC_ADDRESS,
  maxPerCall: 100_000n,           // $0.10 per call
  maxPerWindow: 10_000_000n,      // $10 total per session window
  windowSeconds: 21600,           // 6h (match session duration)
});
```

This creates a comprehensive spending cap:

* **Per-request limit**: `maxPerCall` enforced on-chain (\$0.10 per API call)
* **Session window limit**: `maxPerWindow` enforced on-chain (\$10 per 6 hours)
* **Transaction count limit**: `maxCalls: 100` enforced on-chain
* **Client-side guard**: `maxPaymentAmount: "0.10"` (early rejection, saves gas)

### Session Expiry Fallback

If the session expires during a payment, the hook falls back to requiring the owner's signature (wallet popup / biometrics / PIN):

```typescript theme={null}
const { x402Fetch } = useX402Payment({
  wallet,
  encryptKey: pin,
  getBearerToken: getToken,
  session,  // May be expired
  // If session is expired, falls back to standard wallet signing
});
```

### Best Practices

1. **Set spending limits**: Always configure `maxPaymentAmount` and session `maxCalls`
2. **Whitelist merchants**: Use `allowedRecipients` when possible
3. **Monitor payments**: Use `onPaymentComplete` callback for logging/analytics
4. **Short sessions**: Prefer shorter session durations with renewal over long-lived sessions
5. **Transfer-only**: Always set `allowedEntrypoints: ["transfer"]` for x402 sessions
