This guide is for session keys that were manually installed onchain via installValidation. For new session keys, use wallet_createSession or grantPermissions instead — they handle installation, permissions, and context automatically.
If you have session keys installed directly on a Modular Account V2 (for example, from a v4 @account-kit/wallet-client integration) and are migrating to Wallet APIs (@alchemy/wallet-apis v5), you can use those existing keys without reinstalling them. Build a permissions context hex string and pass it when sending transactions.
- The smart wallet must be a Modular Account V2 (
sma-b) or an EIP-7702 account delegated to Modular Account V2. - The session key must already be installed onchain with a known
entityIdviainstallValidation. - You must know whether the session key was installed as a global validation (
isGlobal: true) or scoped to specific selectors.
The permissions context tells Wallet APIs how to locate and validate the session key onchain. Construct it by concatenating three values:
| Byte(s) | Value | Meaning |
|---|---|---|
0x02 | Fixed prefix | Indicates a session key installed outside of Wallet APIs |
0x01 or 0x00 | isGlobal flag | 0x01 if the key was installed with isGlobal: true, 0x00 otherwise |
| 4 bytes | entityId | The entityId used in the installValidation call, as a uint32 hex value |
import { concatHex, toHex } from "viem";
const isGlobal = true; // match the value used in installValidation
const entityId = 1; // match the entityId used in installValidation
const permissionsContext = concatHex([
"0x02",
isGlobal ? "0x01" : "0x00",
toHex(entityId, { size: 4 }),
]);Pass the permissions context in capabilities.permissions.context when sending transactions.
import { privateKeyToAccount } from "viem/accounts";
import { arbitrumSepolia } from "viem/chains";
import { concatHex, toHex } from "viem";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";
const sessionKey = privateKeyToAccount("0xSESSION_PRIVATE_KEY" as const);
// The address of the account that the session key is installed on
const accountAddress = "0xACCOUNT_ADDRESS";
const isGlobal = true;
const entityId = 1;
const permissionsContext = concatHex([
"0x02",
isGlobal ? "0x01" : "0x00",
toHex(entityId, { size: 4 }),
]);
const client = createSmartWalletClient({
transport: alchemyWalletTransport({
apiKey: "YOUR_API_KEY",
}),
chain: arbitrumSepolia,
signer: sessionKey,
account: accountAddress,
});
const { id } = await client.sendCalls({
calls: [{ to: "0x...", value: BigInt(0) }],
capabilities: {
permissions: {
context: permissionsContext,
},
},
});
await client.waitForCallsStatus({ id });