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

# Session Keys Quickstart

> Enable temporary, delegated transaction execution without requiring the owner's private key for each operation

<Warning>
  **PIN is weak — not recommended for production.**

  A user-typed PIN is a short, low-entropy string. Anyone who shoulder-surfs the PIN, observes a phishing form, or compromises the browser at typing time can decrypt the wallet's private key. PIN remains in the SDK only as a fallback recovery surface for users who lose access to their platform authenticator.

  **Production embedded-wallet apps should default to a platform passkey** (Touch ID, Face ID, Windows Hello, Android biometrics) via the `@chipi-stack/chipi-passkey` package. For SHHH V8.4 wallets, `signerKind: "WEBAUTHN_P256"` keeps the private key inside the platform authenticator — it never leaves the device, never reaches Chipi servers, and is never derived from a user-typed secret.

  Only prompt for a PIN as the encryption key when:

  * The user explicitly opted into a PIN-only flow (e.g. cold-storage / paper-backup recovery), or
  * The platform genuinely has no WebAuthn / biometric support available.

  If you are migrating an existing PIN-based wallet to a passkey, look up `useMigrateWalletToPasskey` in your framework's hook docs.
</Warning>

## What are Session Keys?

Session keys allow your application to execute transactions on behalf of a user without requiring their owner private key for every action. Instead, a temporary keypair is created and registered on-chain with specific permissions (time limit, call limit, allowed functions).

This enables **gasless, frictionless UX** for gaming, DeFi automation, social apps, and more.

<Info>
  Session keys only work with [CHIPI wallets](https://github.com/chipi-pay/sessions-smart-contract) - OpenZeppelin accounts extended with SNIP-9 session key support.
</Info>

## When to Use Session Keys

| Use Case            | Why Sessions Help                                 |
| ------------------- | ------------------------------------------------- |
| **Gaming**          | Players make moves without wallet popups          |
| **DeFi Automation** | Yield optimizers execute strategies automatically |
| **Social dApps**    | Post, like, comment without friction              |
| **Subscriptions**   | Recurring payments without manual approval        |
| **Airdrops**        | Zero-friction claiming for users                  |

## Requirements

* A [CHIPI wallet](https://github.com/chipi-pay/sessions-smart-contract) (not READY)
* Valid bearer token from your auth provider
* User's `encryptKey` (PIN) for signing

***

<Steps>
  <Step title="Install the Backend SDK" titleSize="h2">
    <CodeGroup>
      ```bash npm theme={null}
      npm install @chipi-stack/backend
      ```

      ```bash yarn theme={null}
      yarn add @chipi-stack/backend
      ```

      ```bash pnpm theme={null}
      pnpm add @chipi-stack/backend
      ```
    </CodeGroup>
  </Step>

  <Step title="Initialize the SDK" titleSize="h2">
    ```typescript theme={null}
    import { ChipiSDK } from "@chipi-stack/backend";

    const sdk = new ChipiSDK({
      apiPublicKey: process.env.CHIPI_PUBLIC_KEY!,
      apiSecretKey: process.env.CHIPI_SECRET_KEY!,
    });
    ```
  </Step>

  <Step title="Create a Session Key" titleSize="h2">
    Generate a session keypair locally. The SDK returns the data but does **NOT** store it - you must persist it on the client side.

    ```typescript theme={null}
    // Create session key (self-custodial - store in client localStorage)
    const session = sdk.sessions.createSessionKey({
      encryptKey: "user-secure-pin",
      durationSeconds: 3600, // 1 hour (default: 6 hours)
    });

    console.log(session);
    // {
    //   publicKey: "0x...",           // Session public key
    //   encryptedPrivateKey: "...",   // AES encrypted with encryptKey
    //   validUntil: 1702500000        // Unix timestamp (seconds)
    // }

    // Store in client-side secure storage (NOT your database!)
    localStorage.setItem('chipiSession', JSON.stringify(session));
    ```

    <Danger>
      Never store session keys in your backend database. Store only in client-side secure storage (localStorage, Secure Enclave, Keychain).
    </Danger>
  </Step>

  <Step title="Register Session On-Chain" titleSize="h2">
    Before using a session key, you must register it on the [CHIPI wallet](https://github.com/chipi-pay/sessions-smart-contract) smart contract. This requires the owner's signature (one-time setup).

    ```typescript theme={null}
    // Get user's wallet
    const wallet = await sdk.getWallet({ externalUserId: "user-123" });

    // Register session on contract (uses owner signature via paymaster)
    const txHash = await sdk.sessions.addSessionKeyToContract({
      encryptKey: "user-secure-pin",
      wallet: {
        publicKey: wallet.publicKey,
        encryptedPrivateKey: wallet.encryptedPrivateKey,
        walletType: "CHIPI",
      },
      sessionConfig: {
        sessionPublicKey: session.publicKey,
        validUntil: session.validUntil,
        maxCalls: 100,                // Max transactions allowed
        allowedEntrypoints: [],       // Empty = all functions allowed
      },
    }, bearerToken);

    console.log("Session registered:", txHash);
    ```

    ### Session Configuration Options

    | Parameter            | Type      | Description                                             |
    | -------------------- | --------- | ------------------------------------------------------- |
    | `sessionPublicKey`   | string    | Public key from `createSessionKey()`                    |
    | `validUntil`         | number    | Unix timestamp when session expires                     |
    | `maxCalls`           | number    | Maximum transactions allowed (decrements with each use) |
    | `allowedEntrypoints` | string\[] | Whitelisted function selectors. Empty = all allowed     |

    <Tip>
      For security, whitelist specific entrypoints when possible:

      ```typescript theme={null}
      allowedEntrypoints: [
        "0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e", // transfer
        "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", // approve
      ]
      ```
    </Tip>
  </Step>

  <Step title="Execute Transactions with Session" titleSize="h2">
    Now you can execute transactions using the session key - **no owner private key needed!**

    ```typescript theme={null}
    // Retrieve session from client storage
    const session = JSON.parse(localStorage.getItem('chipiSession')!);

    // Execute transaction with session signature (4-element format)
    const txHash = await sdk.executeTransactionWithSession({
      params: {
        encryptKey: "user-secure-pin",
        wallet: {
          publicKey: wallet.publicKey,
          encryptedPrivateKey: wallet.encryptedPrivateKey,
          walletType: "CHIPI",
        },
        session,
        calls: [
          {
            contractAddress: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
            entrypoint: "transfer",
            calldata: [recipientAddress, amount, "0x0"],
          },
        ],
      },
      bearerToken,
    });

    console.log("Transaction executed:", txHash);
    ```

    <Info>
      **Signature Format Difference:**

      * Owner signature: `[r, s]` (2 elements)
      * Session signature: `[sessionPubKey, r, s, validUntil]` (4 elements)

      The SDK handles this automatically.
    </Info>
  </Step>

  <Step title="Query Session Status" titleSize="h2">
    Check if a session is still active and how many calls remain:

    ```typescript theme={null}
    const sessionData = await sdk.sessions.getSessionData({
      walletAddress: wallet.publicKey,
      sessionPublicKey: session.publicKey,
    });

    console.log(sessionData);
    // {
    //   isActive: true,
    //   validUntil: 1702500000,
    //   remainingCalls: 95,
    //   allowedEntrypoints: []
    // }

    if (sessionData.isActive) {
      console.log(`Session has ${sessionData.remainingCalls} calls remaining`);
    }
    ```
  </Step>

  <Step title="Revoke Session" titleSize="h2">
    Always revoke sessions when no longer needed or when the user logs out:

    ```typescript theme={null}
    const revokeTxHash = await sdk.sessions.revokeSessionKey({
      encryptKey: "user-secure-pin",
      wallet: {
        publicKey: wallet.publicKey,
        encryptedPrivateKey: wallet.encryptedPrivateKey,
        walletType: "CHIPI",
      },
      sessionPublicKey: session.publicKey,
    }, bearerToken);

    // Clear from client storage
    localStorage.removeItem('chipiSession');

    console.log("Session revoked:", revokeTxHash);
    ```

    <Warning>
      Always revoke sessions on logout to prevent unauthorized access.
    </Warning>
  </Step>
</Steps>

***

## Security and Best Practices

### Storage Guidelines

<Danger>
  **Never store session keys in your backend database.** This defeats the purpose of self-custodial security.
</Danger>

Store session keys only in client-side secure storage:

| Platform         | Recommended Storage                            |
| ---------------- | ---------------------------------------------- |
| **Web**          | `localStorage` or `sessionStorage`             |
| **iOS**          | Secure Enclave / Keychain                      |
| **Android**      | Android Keystore                               |
| **React Native** | `expo-secure-store` or `react-native-keychain` |

<Tip>
  Use `sessionStorage` instead of `localStorage` for sessions that should expire when the browser tab closes.
</Tip>

### Lifecycle Best Practices

1. **Always revoke on logout** - Never leave active sessions when user signs out
2. **Set short durations** - Use 1-6 hours, not days
3. **Limit `maxCalls`** - Set realistic limits based on expected usage
4. **Whitelist entrypoints** - Restrict to only the functions your app needs

### Example: Secure Logout Handler

```typescript theme={null}
const handleLogout = async () => {
  // Retrieve active session
  const sessionStr = localStorage.getItem('chipiSession');
  
  if (sessionStr) {
    const session = JSON.parse(sessionStr);
    
    try {
      // Revoke session on-chain before logout
      await sdk.sessions.revokeSessionKey({
        encryptKey: userPin,
        wallet,
        sessionPublicKey: session.publicKey,
      }, bearerToken);
    } catch (error) {
      console.warn("Failed to revoke session:", error);
      // Continue with logout even if revoke fails
    }
    
    // Clear from local storage
    localStorage.removeItem('chipiSession');
  }
  
  // Proceed with auth logout
  await authProvider.signOut();
};
```

### Example: Session with Restricted Permissions

```typescript theme={null}
// Create a session that can ONLY transfer USDC
const restrictedSession = await sdk.sessions.addSessionKeyToContract({
  encryptKey: userPin,
  wallet,
  sessionConfig: {
    sessionPublicKey: session.publicKey,
    validUntil: session.validUntil,
    maxCalls: 10,  // Only 10 transfers allowed
    allowedEntrypoints: [
      "0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e", // transfer
    ],
  },
}, bearerToken);
```

***

## Full Example: Gaming Session

```typescript theme={null}
import { ChipiSDK } from "@chipi-stack/backend";

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

// Game constants
const GAME_CONTRACT = "0x...";
const MAKE_MOVE_SELECTOR = "0x...";
const CLAIM_REWARD_SELECTOR = "0x...";

async function startGameSession(userId: string, userPin: string, bearerToken: string) {
  // Get player's CHIPI wallet
  const wallet = await sdk.getWallet({ externalUserId: userId });
  
  // Create 24-hour gaming session
  const session = sdk.sessions.createSessionKey({
    encryptKey: userPin,
    durationSeconds: 24 * 60 * 60, // 24 hours
  });
  
  // Register with game-only permissions
  await sdk.sessions.addSessionKeyToContract({
    encryptKey: userPin,
    wallet: { ...wallet, walletType: "CHIPI" },
    sessionConfig: {
      sessionPublicKey: session.publicKey,
      validUntil: session.validUntil,
      maxCalls: 1000,  // Many moves allowed
      allowedEntrypoints: [MAKE_MOVE_SELECTOR, CLAIM_REWARD_SELECTOR],
    },
  }, bearerToken);
  
  // Store session in client
  localStorage.setItem('gameSession', JSON.stringify(session));
  
  return session;
}

async function makeMove(moveData: string, bearerToken: string) {
  const session = JSON.parse(localStorage.getItem('gameSession')!);
  const wallet = await sdk.getWallet({ externalUserId: currentUserId });
  
  // Execute move instantly - no wallet popup!
  return sdk.executeTransactionWithSession({
    params: {
      encryptKey: userPin,
      wallet: { ...wallet, walletType: "CHIPI" },
      session,
      calls: [{
        contractAddress: GAME_CONTRACT,
        entrypoint: "make_move",
        calldata: [moveData],
      }],
    },
    bearerToken,
  });
}
```

***

## Next Steps

* **[Session Methods Reference](/sdk/backend/methods/sessions)** - Detailed API for each session method
* **[CHIPI Wallet Smart Contract](https://github.com/chipi-pay/sessions-smart-contract)** - Cairo implementation details
* **[Transfer Tokens](/sdk/backend/methods/transfer)** - Basic token transfers

<Tip>
  Need help? Join our [Telegram Community](https://t.me/+e2qjHEOwImkyZDVh) for support.
</Tip>
