# Session Keys (SDK)

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

<Info>`@alchemy/wallet-apis` (v5.x.x) is currently in beta but is the recommended replacement for `@account-kit/wallet-client` (v4.x.x). If you run into any issues, please [reach out](mailto:support@alchemy.com).</Info>

### 1. Install

You need `@alchemy/wallet-apis` and `viem`. This guide uses `privateKeyToAccount` from `viem/accounts` as the owner for demonstration purposes.

<CodeGroup>
```shell npm
npm install @alchemy/wallet-apis viem
```

```shell bun
bun add @alchemy/wallet-apis viem
```

```shell yarn
yarn add @alchemy/wallet-apis viem
```

```shell pnpm
pnpm install @alchemy/wallet-apis viem
```

</CodeGroup>

### 2. Create a smart wallet client

Create a client for a given owner (e.g. using `privateKeyToAccount` from `viem/accounts`).

```ts
import { privateKeyToAccount } from "viem/accounts";
import { arbitrumSepolia } from "viem/chains";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

const client = createSmartWalletClient({
  transport: alchemyWalletTransport({
    apiKey: "YOUR_API_KEY",
  }),
  chain: arbitrumSepolia,
  signer: privateKeyToAccount("0xYOUR_PRIVATE_KEY" as const),
});
```

### 3. Create the session key

<Accordion title="Need to delegate your account first?">

EIP-7702 accounts must be delegated onchain before creating a session. If the
account has already sent calls, it will already be delegated. If it hasn't sent
any calls before creating the session key, you can delegate it by sending an
empty call as the owner:

```ts
const { id } = await client.sendCalls({
  calls: [], // empty array since you just want to delegate
});

await client.waitForCallsStatus({ id });
```

Now you can continue to create the session key as described below.

</Accordion>

```ts
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";

// This is where you would use your session key signer
const sessionKeyPrivateKey = generatePrivateKey();
const sessionKey = privateKeyToAccount(sessionKeyPrivateKey);

const permissions = await client.grantPermissions({
  expirySec: Math.floor(Date.now() / 1000) + 60 * 60,
  key: {
    publicKey: sessionKey.address,
    type: "secp256k1",
  },
  permissions: [{ type: "root" }], // Here we grant root permissions as an example, but this is not advised in production!
});
```

### 4. Send calls using the session key

```ts
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

// Create a client with the session key as signer
const sessionKeyClient = createSmartWalletClient({
  transport: alchemyWalletTransport({
    apiKey: "YOUR_API_KEY",
  }),
  chain: arbitrumSepolia,
  signer: sessionKey, // Use the session key as signer
  account: client.account.address, // The original account that granted permissions
});

const { id } = await sessionKeyClient.sendCalls({
  calls: [{ to: "0x0000000000000000000000000000000000000000", value: BigInt(0) }],
  capabilities: {
    permissions,
  },
});

await sessionKeyClient.waitForCallsStatus({ id });
```

<Accordion title="Using @account-kit/wallet-client (v4.x.x)?">
The examples on this page use `@alchemy/wallet-apis` (v5.x.x). If you're using `@account-kit/wallet-client` (v4.x.x), the client setup looks like this:

```ts title="client.ts (v4.x.x)"
import { LocalAccountSigner } from "@aa-sdk/core";
import { createSmartWalletClient } from "@account-kit/wallet-client";
import { alchemy, sepolia } from "@account-kit/infra";

const signer = LocalAccountSigner.privateKeyToAccountSigner("0xYOUR_PRIVATE_KEY" as const);

export const client = createSmartWalletClient({
  transport: alchemy({ apiKey: "YOUR_API_KEY" }),
  chain: sepolia,
  signer,
  account: signer.address, // can also be passed per action as `from` or `account`
  // Optional: sponsor gas for your users (see "Sponsor gas" guide)
  policyId: "YOUR_POLICY_ID", 
});
```

Key v4.x.x differences:
* **Account address** must be specified on the client or per action (`from` or `account`). In v5.x.x, the client automatically uses the owner's address as the account address via [EIP-7702](/docs/wallets/transactions/using-eip-7702).
* **Chain imports** come directly from `@account-kit/infra` instead of `viem/chains`.
* **Numeric values** use hex strings: `value: "0x0"` instead of `value: BigInt(0)`.
* In v4.x.x, the **paymaster capability** on `prepareCalls` or `sendCalls` is called `paymasterService` instead of `paymaster`, or you can set the `policyId` directly on the client.
* **Owners** use `LocalAccountSigner` / `WalletClientSigner` from `@aa-sdk/core`. In v5.x.x, a viem `LocalAccount` or `WalletClient` is used directly.

See the [full migration guide](/docs/wallets/resources/migration-v5) for a complete cheat sheet.
</Accordion>