Granular Session Keys via API: Secure Server-Side Automation for User Wallets

Firekeeper

We are excited to announce full support for EIP-7702 Session Keys in the Wallets API. This feature allows you to create secure, scoped permissions for your backend to execute transactions on behalf of your users—without ever taking custody of their main keys.

This is perfect for:

  • AI Agents: Allow an agent to trade or interact with contracts within strict limits.
  • Automated DeFi: Execute recurring payments or rebalancing without user confirmation for every step.
  • Gaming: Perform background actions (like crafting or harvesting) while the user is offline.

How It Works

  1. Create a Session Key: Your server generates a new "Server Wallet". This wallet will act as the authorized signer grantee.
  2. Grant Permissions: The user (client-side) signs a transaction authorizing this Session Key. You can restrict it to specific contracts, functions, or spending limits.
  3. Execute: Your server uses the Session Key to sign and send transactions. The blockchain treats these transactions as if they came from the user's main account.

End-to-End Example

Here is a complete, reproducible example using fetch. We will simulate a full flow: creating a guest user, generating a session key, granting permissions, and executing a transaction.

Prerequisites

  • Secret Key: For server-side calls (backend).
  • Client ID: For client-side calls (frontend/simulation).

Step 1: Create a Guest User (Client-Side)

First, we simulate a user logging in. We'll use the "guest" auth method to instantly generate a wallet and get an authentication token.

const API_URL = "https://api.thirdweb.com/v1";
const CLIENT_ID = "your-client-id";
// 1. Create a Guest Wallet (User)
const guestResponse = await fetch(`${API_URL}/auth/complete`, {
method: "POST",
headers: {
"x-client-id": CLIENT_ID,
"Content-Type": "application/json",
},
body: JSON.stringify({
method: "guest",
sessionId: `session-${Date.now()}`, // Unique session ID
}),
});
const guestData = await guestResponse.json();
const userToken = guestData.token; // JWT for user authentication
const userAddress = guestData.walletAddress; // The user's smart account address
console.log("User Address:", userAddress);
console.log("User Token:", userToken);

Step 2: Create a Server Wallet (Session Key Target)

Your backend creates a fresh wallet that will act as the session key.

const SECRET_KEY = "your-secret-key";
// 2. Create a new Server Wallet to act as the Session Key
const sessionKeyResponse = await fetch(`${API_URL}/wallets/server`, {
method: "POST",
headers: {
"x-secret-key": SECRET_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
identifier: `session-key-${Date.now()}`,
}),
});
const sessionKeyData = await sessionKeyResponse.json();
const sessionKeyAddress = sessionKeyData.result.address;
console.log("Session Key Address:", sessionKeyAddress);

Step 3: User Grants Permission (Client-Side)

The user approves the session key using their auth token.

// 3. Grant permissions to the Session Key
const grantResponse = await fetch(
`${API_URL}/wallets/create-session-key`,
{
method: "POST",
headers: {
Authorization: `Bearer ${userToken}`, // Authenticate as the user
"x-client-id": CLIENT_ID,
"Content-Type": "application/json",
},
body: JSON.stringify({
chainId: 84532, // e.g., Base Sepolia
sessionKeyAddress: sessionKeyAddress,
durationInSeconds: 3600, // Valid for 1 hour
grantFullPermissions: true,
// Optional: Restrict to specific contracts/functions using 'callPolicies'
}),
},
);
const grantData = await grantResponse.json();
console.log("Permission Grant Tx ID:", grantData.transactionId);

Step 4: Execute Transaction (Server-Side)

Finally, your server executes a transaction. The server uses the Session Key to sign, but the transaction is executed on the User's account.

// 4. Execute a transaction
const executeResponse = await fetch(`${API_URL}/wallets/send`, {
method: "POST",
headers: {
"x-secret-key": SECRET_KEY,
"Content-Type": "application/json",
// Context Headers:
"x-account-address": userAddress, // The User's account (context)
"x-session-key-address": sessionKeyAddress, // The Session Key (signer)
},
body: JSON.stringify({
chainId: 84532,
from: sessionKeyAddress, // The signer must match x-session-key-address
recipients: [
{
address: userAddress, // Sending 0 ETH back to self as a test
quantity: "0",
},
],
}),
});
const executeData = await executeResponse.json();
console.log("Execution Tx ID:", executeData.result.transactionIds[0]);

Summary

With just three API calls, you've set up a secure, non-custodial automation flow. The user retains full control and can revoke the session key at any time, while your server gets the convenience of programmatic execution.

Try It Yourself

Visit our API reference here.