For AI agents: read the documentation index. Documentation pages are available as Markdown by appending .md to their URL, or requesting Accept: text/markdown.

Namera Docs

Install Session Keys

Explicitly approve and track session installation from the SDK.

Use an API key with session-key:read and session-key:install. You need a registered session with an unexpired pending installation on the chosen network. The account’s owner determines approval; the session signer’s custody does not.

These are low-level owner-operation methods. Review the selected account, signer, permissions, network, validity, and fees before approving. Use the SDK’s validateManagedOwnerApproval or validateOwnerApproval helpers with independently reconstructed expectations when validating prepared calldata. Do not populate those expectations from the preparation you are validating.

Prepare installation

index.ts
import { NameraClient } from "@namera-ai/sdk";

const namera = new NameraClient({ apiKey: process.env.NAMERA_API_KEY! });

const result = await namera.sessionKeys.prepareOperation({
  sessionKeyId: "0199dfe1-0000-7000-8000-000000000003",
  chainId: "eip155:11155111",
  kind: "install",
  sponsor: true,
});
if (!result.success) throw new Error(result.error.message);

console.log(result.data);
{
  "operationId": "0199dfe1-0000-7000-8000-000000000014",
  "namespace": "eip155",
  "approval": "1claw",
  "prepared": {
    "version": 1,
    "namespace": "eip155",
    "chainId": "eip155:11155111",
    "entryPointVersion": "0.7",
    "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
    "sponsorship": "alchemy-bso",
    "context": {
      "version": 1,
      "namespace": "eip155",
      "chainId": "eip155:11155111",
      "account": "0x3333333333333333333333333333333333333333",
      "block": {
        "number": "0",
        "hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "timestamp": "1970-01-01T00:00:00.000Z"
      },
      "calls": [
        {
          "to": "0x3333333333333333333333333333333333333333",
          "value": "0",
          "data": "0x1234"
        }
      ],
      "userOperation": {
        "nonce": "0",
        "gas": {
          "callGasLimit": "0",
          "verificationGasLimit": "0",
          "preVerificationGas": "0",
          "paymasterVerificationGasLimit": "0",
          "paymasterPostOpGasLimit": "0",
          "maxFeePerGas": "0",
          "maxPriorityFeePerGas": "0"
        },
        "paymaster": null
      },
      "simulation": {
        "userOperation": {
          "source": "eth_estimateUserOperationGas",
          "callGasLimit": "0",
          "verificationGasLimit": "0",
          "preVerificationGas": "0",
          "maxFeePerGas": "1000000000",
          "maxPriorityFeePerGas": "1"
        },
        "calls": {
          "source": "viem.simulateCalls",
          "results": [
            {
              "status": "success",
              "returnData": "0x",
              "gasUsed": "0"
            }
          ],
          "assetChanges": [],
          "transfers": []
        }
      }
    },
    "userOperation": {
      "sender": "0x3333333333333333333333333333333333333333",
      "nonce": "0",
      "callData": "0x",
      "callGasLimit": "0",
      "verificationGasLimit": "0",
      "preVerificationGas": "0",
      "maxFeePerGas": "0",
      "maxPriorityFeePerGas": "0",
      "signature": "0x"
    },
    "billing": {
      "executionMeter": "execution.testnet",
      "sponsorship": null
    }
  },
  "expiresAt": "2026-10-15T02:44:05.075Z"
}

The SDK resolves the session’s network installation and account owner, then chooses the existing managed or passkey preparation endpoint. Retry keys stay internal. Preparation does not sign or submit.

Approve a managed account operation

After reviewing and validating the preparation, explicitly approve its operation ID:

index.ts
import { NameraClient } from "@namera-ai/sdk";

const namera = new NameraClient({ apiKey: process.env.NAMERA_API_KEY! });

const result = await namera.sessionKeys.approveOperation({
  operationId: "0199dfe1-0000-7000-8000-000000000014",
  approval: { type: "1claw" },
});
if (!result.success) throw new Error(result.error.message);

console.log(result.data);
{
  "operationId": "0199dfe1-0000-7000-8000-000000000014",
  "status": "signed"
}

This call authorizes the managed account owner to sign only the stored installation. Creating the session did not grant this approval.

Approve a passkey account operation

Passkey preparation returns approval: "passkey" and options:

{
  "operationId": "0199dfe1-0000-7000-8000-000000000021",
  "namespace": "eip155",
  "options": {
    "challenge": "ERERERERERERERERERERERERERERERERERERERERERE",
    "rpId": "app.example.com",
    "timeout": 300000,
    "allowCredentials": [
      {
        "id": "fH9q_W4I4NAClGWOrbumZv4mrefSBfKPdY2UFfTKvcI",
        "type": "public-key"
      }
    ],
    "userVerification": "required"
  },
  "prepared": {
    "version": 1,
    "namespace": "eip155",
    "chainId": "eip155:11155111",
    "entryPointVersion": "0.7",
    "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
    "sponsorship": "alchemy-bso",
    "context": {
      "version": 1,
      "namespace": "eip155",
      "chainId": "eip155:11155111",
      "account": "0x3333333333333333333333333333333333333333",
      "block": {
        "number": "0",
        "hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "timestamp": "1970-01-01T00:00:00.000Z"
      },
      "calls": [
        {
          "to": "0x3333333333333333333333333333333333333333",
          "value": "0",
          "data": "0x1234"
        }
      ],
      "userOperation": {
        "nonce": "0",
        "gas": {
          "callGasLimit": "0",
          "verificationGasLimit": "0",
          "preVerificationGas": "0",
          "paymasterVerificationGasLimit": "0",
          "paymasterPostOpGasLimit": "0",
          "maxFeePerGas": "0",
          "maxPriorityFeePerGas": "0"
        },
        "paymaster": null
      },
      "simulation": {
        "userOperation": {
          "source": "eth_estimateUserOperationGas",
          "callGasLimit": "0",
          "verificationGasLimit": "0",
          "preVerificationGas": "0",
          "maxFeePerGas": "1000000000",
          "maxPriorityFeePerGas": "1"
        },
        "calls": {
          "source": "viem.simulateCalls",
          "results": [
            {
              "status": "success",
              "returnData": "0x",
              "gasUsed": "0"
            }
          ],
          "assetChanges": [],
          "transfers": []
        }
      }
    },
    "userOperation": {
      "sender": "0x3333333333333333333333333333333333333333",
      "nonce": "0",
      "callData": "0x",
      "callGasLimit": "0",
      "verificationGasLimit": "0",
      "preVerificationGas": "0",
      "maxFeePerGas": "0",
      "maxPriorityFeePerGas": "0",
      "signature": "0x"
    },
    "billing": {
      "executionMeter": "execution.testnet",
      "sponsorship": null
    }
  },
  "expiresAt": "2026-10-15T02:44:06.347Z"
}

Return the preparation to your application’s browser for review and validation. Install @simplewebauthn/browser in your frontend and call startAuthentication({ optionsJSON: preparation.options }) from a user action. The prompt must run on the account’s stored origin. Keep the API key on your backend.

Send the assertion to your authenticated backend, which loads the pending operation ID belonging to that user and calls:

index.ts
import { NameraClient, type ApproveSessionKeyOperationRequest } from "@namera-ai/sdk";

const namera = new NameraClient({ apiKey: process.env.NAMERA_API_KEY! });

export async function finishPasskeyInstallation(
  operationId: string,
  response: Extract<ApproveSessionKeyOperationRequest["approval"], { type: "passkey" }>["response"],
) {
  const result = await namera.sessionKeys.approveOperation({
    operationId,
    approval: { type: "passkey", response },
  });
  if (!result.success) throw new Error(result.error.message);
  return result.data;
}

The success shape is the same operation status shown above. Namera verifies the actual assertion; an API-key permission does not replace the passkey signature.

Track confirmation and recover interruptions

index.ts
import { NameraClient } from "@namera-ai/sdk";

const namera = new NameraClient({ apiKey: process.env.NAMERA_API_KEY! });
const result = await namera.sessionKeys.getOperation("0199dfe1-0000-7000-8000-000000000014");
if (!result.success) throw new Error(result.error.message);

console.log(result.data);
{
  "operationId": "0199dfe1-0000-7000-8000-000000000014",
  "status": "confirmed"
}

Workers submit signed requests and reconcile receipts. signed and submitted are not confirmation. Check the session installation for installed before using it.

After a restart, call sessionKeys.getActiveOperation({ sessionKeyId, chainId, kind: "install" }). An unsigned operation includes a recovered preparation; review it before explicit approval. Signed/submitted operations include no preparation and should be polled. No caller-managed idempotency key is needed. cancelOperation(operationId) cancels an unsigned preparation; it cannot cancel signed work.