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

# useChipiSession

> Unified session key management for gasless UX - create, register, execute, and revoke sessions with a single hook.

## Overview

`useChipiSession` provides a unified API for managing session keys, enabling gasless transactions without requiring the owner's signature for each operation.

This hook combines all session-related operations:

* **Session creation** (local keypair generation)
* **Session registration** (on-chain)
* **Transaction execution** (using session key)
* **Session revocation** (on-chain)
* **Status checking** (remaining calls, expiration)

> [!WARNING]
>
> Session keys work with **CHIPI** wallets and with **SHHH** wallets whose signer is STARK or ED25519 (every SHHH wallet the SDK creates, including passkey wallets; SHHH needs 14.14.0 or later in this hook). READY wallets do not support session keys. Check `supportsSession` before offering sessions.

## Prerequisites

Before using `useChipiSession`, you need:

1. A wallet that supports sessions: CHIPI, or SHHH with a STARK / ED25519 signer (check `supportsSession`, `wallet.supportsSessionKeys` or `walletSupportsSessions(wallet)`)
2. The user's **encryption key**: a PIN (`encryptKey`) or a function that resolves it on demand, such as a passkey (`getEncryptKey`, 14.14.0+)
3. An **authentication token** (from Clerk, Firebase, etc.)

## Session Lifecycle

| State | Description |
| - | - |
| `none` | No session exists |
| `created` | Session created locally, not yet registered on-chain |
| `active` | Session registered and usable |
| `expired` | Session has expired (time or call limit) |
| `revoked` | Session has been revoked on-chain |

## Quick Start

```tsx theme={null}
import { useChipiWallet, useChipiSession } from "@chipi-stack/chipi-react";

function SessionComponent() {
  const { userId, getToken } = useYourAuthProvider();
  const [pin, setPin] = useState("");
  
  const { wallet } = useChipiWallet({
    externalUserId: userId,
    getBearerToken: getToken,
  });
  
  const {
    session,
    sessionState,
    hasActiveSession,
    remainingCalls,
    createSession,
    registerSession,
    executeWithSession,
    isCreating,
    isRegistering,
    isExecuting,
  } = useChipiSession({
    wallet,
    encryptKey: pin,
    getBearerToken: getToken,
  });

  // Setup session (create + register)
  const handleSetup = async () => {
    await createSession();
    await registerSession();
  };

  // Execute a transfer using the session
  const handleTransfer = async () => {
    await executeWithSession([{
      contractAddress: "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb",
      entrypoint: "transfer",
      calldata: [recipientAddress, amount, "0x0"],
    }]);
  };

  return (
    <div>
      <p>Session: {sessionState}</p>
      {hasActiveSession && <p>Calls remaining: {remainingCalls}</p>}
      
      {!hasActiveSession ? (
        <button onClick={handleSetup} disabled={isCreating || isRegistering}>
          Setup Session
        </button>
      ) : (
        <button onClick={handleTransfer} disabled={isExecuting}>
          Transfer (Gasless)
        </button>
      )}
    </div>
  );
}
```

## Passkey wallets and server-held sessions (14.14.0+)

For an agent that runs on your server, create the session key there and register it from the browser with the user's passkey. The session and its spending caps go in **one** owner-signed transaction, so the user sees one prompt:

```tsx theme={null}
const { registerSession, revokeSession } = useChipiSession({
  wallet,
  // credentialId and prfSupported come from the wallet row (useChipiWallet /
  // getWallet), so this works on any device the passkey synced to. With no
  // argument, getWalletEncryptKey() reads localStorage and fails on a new device.
  getEncryptKey: () =>
    getWalletEncryptKey(wallet?.credentialId ?? undefined, {
      prfSupported: wallet?.prfSupported ?? undefined,
    }),
  getBearerToken: () => getToken({ skipCache: true }),
});

// sessionPublicKey and validUntil come from your server (sdk.sessions.createSessionKey)
await registerSession({
  sessionPublicKey,
  validUntil,
  maxCalls: 500,
  allowedEntrypoints: ["transfer", "approve"],
  spendingPolicies: [
    { token: USDC, maxPerCall: 5_000_000n, maxPerWindow: 20_000_000n, windowSeconds: 86_400 },
  ],
});

// Later, to stop it:
await revokeSession(sessionPublicKey);
```

Function names in `allowedEntrypoints` are converted to selectors. The whole flow, server side included, is in [Agent with a session key and a passkey](/sdk/guides/agent-sessions).

## Configuration Options

```typescript theme={null}
interface UseChipiSessionConfig {
  // Required
  wallet: SessionWallet | null | undefined;
  getBearerToken: () => Promise<string | null | undefined>;
  // One of these two
  encryptKey?: string;                                        // a PIN, or a key you already hold
  getEncryptKey?: () => Promise<string | null | undefined>;  // e.g. a passkey (14.14.0+)
  
  // Optional
  storedSession?: SessionKeyData | null;
  onSessionCreated?: (session: SessionKeyData) => void | Promise<void>;
  defaultDurationSeconds?: number;  // Default: 21600 (6 hours)
  defaultMaxCalls?: number;         // Default: 1000
  autoCheckStatus?: boolean;        // Default: true
}
```

| Option | Type | Default | Description |
| - | - | - | - |
| `wallet` | `SessionWallet` | Required | CHIPI, or SHHH with a STARK / ED25519 signer |
| `encryptKey` | `string` | - | PIN (or a key you already hold). Set this or `getEncryptKey` |
| `getEncryptKey` | `() => Promise<string>` | - | Resolves the key only when an action runs, e.g. `() => getWalletEncryptKey(wallet.credentialId ?? undefined, { prfSupported: wallet.prfSupported ?? undefined })` (pass the wallet row's values: with no argument it relies on `localStorage` and fails on a new device). Called **before** `getBearerToken`, so the token is fresh after the passkey prompt. Every action resolves it again, so `executeWithSession` prompts too; execute server-held sessions on your server for prompt-free runs. Ignored when `encryptKey` is set (14.14.0+) |
| `getBearerToken` | `() => Promise<string>` | Required | Auth token function |
| `storedSession` | `SessionKeyData` | `null` | Previously stored session |
| `onSessionCreated` | `(session) => void` | - | Callback to persist new session |
| `defaultDurationSeconds` | `number` | `21600` | Session duration (6 hours) |
| `defaultMaxCalls` | `number` | `1000` | Max calls per session |
| `autoCheckStatus` | `boolean` | `true` | Auto-fetch on-chain status |

## Return Values

### Session Data

| Property | Type | Description |
| - | - | - |
| `session` | `SessionKeyData \| null` | Current session data |
| `sessionStatus` | `SessionDataResponse` | On-chain session status |
| `sessionState` | `SessionState` | Lifecycle state |
| `hasActiveSession` | `boolean` | Whether session is usable |
| `isSessionExpired` | `boolean` | Whether session has expired |
| `remainingCalls` | `number \| undefined` | Calls left in session |
| `supportsSession` | `boolean` | Whether wallet supports sessions |

### Actions

| Method | Returns | Description |
| - | - | - |
| `createSession(config?)` | `Promise<SessionKeyData>` | Create new session locally |
| `registerSession(config?)` | `Promise<string>` | Register on-chain (returns txHash). Pass `sessionPublicKey` + `validUntil` to register a key created elsewhere (e.g. on your server), and `spendingPolicies` to set its caps in the same transaction (14.14.0+) |
| `revokeSession(sessionPublicKey?)` | `Promise<string>` | Revoke on-chain (returns txHash). Defaults to the current session; pass a key to revoke one created elsewhere (14.14.0+) |
| `executeWithSession(calls)` | `Promise<string>` | Execute calls (returns txHash) |
| `clearSession()` | `void` | Clear local session state |
| `refetchStatus()` | `Promise<void>` | Refresh on-chain status |

### Loading States

| Property | Description |
| - | - |
| `isCreating` | Creating session locally |
| `isRegistering` | Registering session on-chain |
| `isRevoking` | Revoking session on-chain |
| `isExecuting` | Executing transaction |
| `isLoadingStatus` | Fetching on-chain status |

## Persisting Sessions

Sessions should be persisted between page loads. Here's an example using local state:

```tsx theme={null}
import { useState, useEffect } from "react";
import { useChipiWallet, useChipiSession } from "@chipi-stack/chipi-react";

function PersistentSession() {
  const { wallet } = useChipiWallet({ ... });
  
  // Load session from storage
  const [storedSession, setStoredSession] = useState(() => {
    const saved = localStorage.getItem("chipi-session");
    return saved ? JSON.parse(saved) : null;
  });
  
  const {
    session,
    hasActiveSession,
    createSession,
    registerSession,
  } = useChipiSession({
    wallet,
    encryptKey: pin,
    getBearerToken: getToken,
    storedSession,
    onSessionCreated: (newSession) => {
      // Persist to localStorage
      localStorage.setItem("chipi-session", JSON.stringify(newSession));
      setStoredSession(newSession);
    },
  });

  // Session is automatically restored on mount!
}
```

## Executing Transactions

The `executeWithSession` method accepts an array of Starknet calls:

```typescript theme={null}
// Single transfer
await executeWithSession([{
  contractAddress: USDC_CONTRACT,
  entrypoint: "transfer",
  calldata: [recipient, amount, "0x0"],
}]);

// Batch multiple calls
await executeWithSession([
  {
    contractAddress: USDC_CONTRACT,
    entrypoint: "approve",
    calldata: [spender, amount, "0x0"],
  },
  {
    contractAddress: DEX_CONTRACT,
    entrypoint: "swap",
    calldata: [...],
  },
]);
```

## Custom Session Configuration

### Custom Duration

```typescript theme={null}
// Create session valid for 1 hour
await createSession({ durationSeconds: 3600 });
```

### Custom Max Calls

```typescript theme={null}
// Register with 500 max calls
await registerSession({ maxCalls: 500 });
```

## Error Handling

```tsx theme={null}
const { error, session } = useChipiSession({...});

// Check for errors
if (error) {
  console.error("Session error:", error.message);
}

// Handle specific errors
const handleSetup = async () => {
  try {
    await createSession();
    await registerSession();
  } catch (err) {
    if (err.message.includes("does not support sessions")) {
      alert("Please use a CHIPI wallet for sessions");
    } else {
      alert(`Error: ${err.message}`);
    }
  }
};
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.