> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Example Usage

> Complete runnable scripts for the agent wallet flow — email OTP with a human user, and fully autonomous private-key sign-in.

One runnable script per sign-in path, covering the whole flow: session keys → sign in → `authenticateJwt` with the session signer → create and sign with `backUpToDynamic: true` → refresh → re-authenticate at the refresh limit.

<Tabs>
  <Tab title="Email OTP (human user)">
    This script collects the OTP code on stdin; in production, collect it through your own channel.

    ```typescript theme={"system"}
    import readline from 'node:readline/promises';
    import { DynamicEvmWalletClient } from '@dynamic-labs-wallet/node-evm';
    import {
      createAuthClient,
      generateSessionKeyPair,
      signSessionMessage,
    } from '@dynamic-labs-wallet/node';
    import { ThresholdSignatureScheme } from '@dynamic-labs-wallet/core';

    const environmentId = process.env.DYNAMIC_ENVIRONMENT_ID!;

    // 1. Session key pair — persist privateKeyJwk in your secrets vault.
    const { publicKeyHex, privateKeyJwk } = await generateSessionKeyPair();

    // 2. Sign the user in with email OTP, binding the session public key into the JWT.
    const auth = createAuthClient({ environmentId });
    const { verificationUUID } = await auth.email.sendOtp('user@example.com');

    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
    const code = await rl.question('OTP code sent to the user: ');
    rl.close();

    const { jwt } = await auth.email.verifyOtp({
      verificationUUID,
      code,
      sessionPublicKey: publicKeyHex,
    });

    // 3. Authenticate the wallet client with the JWT and the session signer.
    const client = new DynamicEvmWalletClient({
      environmentId,
      enableMPCAccelerator: false, // Set to true only on AWS Nitro Enclave-compatible infrastructure
    });
    await client.authenticateJwt(jwt, {
      getSessionSignature: (message) => signSessionMessage(message, privateKeyJwk),
    });

    // 4. Create a wallet for the user, backed up to Dynamic. First run only —
    // on later runs, load the persisted walletMetadata and externalServerKeyShares
    // instead of creating a new wallet.
    const { walletMetadata, externalServerKeyShares } = await client.createWalletAccount({
      thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
      password: process.env.WALLET_PASSWORD,
      backUpToDynamic: true,
    });
    console.log('Wallet created:', walletMetadata.accountAddress);
    // Persist walletMetadata (durable store) and externalServerKeyShares (secrets vault).

    // 5. Sign on the user's behalf.
    const signature = await client.signMessage({
      message: 'Hello from your agent',
      walletMetadata,
      externalServerKeyShares,
      password: process.env.WALLET_PASSWORD,
    });
    console.log('Signed:', signature);

    // 6. Long-running sessions: refresh and persist the replacement JWT.
    try {
      const refreshedJwt = await client.refreshAuthToken();
      // Overwrite the stored JWT with refreshedJwt.
    } catch {
      // Refresh limit (refreshExp) reached or session invalid: repeat step 2,
      // then authenticateJwt with the new token.
    }
    ```
  </Tab>

  <Tab title="Private key (autonomous agent)">
    No human in the loop: the agent signing token signs the SIWE message, and re-authentication at the refresh limit reuses the same `signIn` function programmatically.

    ```typescript theme={"system"}
    import { DynamicEvmWalletClient } from '@dynamic-labs-wallet/node-evm';
    import {
      createAuthClient,
      generateSessionKeyPair,
      signSessionMessage,
    } from '@dynamic-labs-wallet/node';
    import { ThresholdSignatureScheme } from '@dynamic-labs-wallet/core';
    import { privateKeyToAccount } from 'viem/accounts';

    const environmentId = process.env.DYNAMIC_ENVIRONMENT_ID!;

    // 1. Session key pair — persist privateKeyJwk in your secrets vault.
    const { publicKeyHex, privateKeyJwk } = await generateSessionKeyPair();

    // The agent signing token is the agent's identity — load it from your vault or KMS.
    const agentAccount = privateKeyToAccount(process.env.AGENT_SIGNING_TOKEN as `0x${string}`);

    // 2. Sign in with the agent signing token, binding the session public key into the JWT.
    const auth = createAuthClient({
      environmentId,
      appOrigin: 'https://yourapp.example.com',
    });

    const signIn = () =>
      auth.wallet.signIn({
        address: agentAccount.address,
        signMessage: (message) => agentAccount.signMessage({ message }),
        sessionPublicKey: publicKeyHex,
        statement: 'Autonomous agent sign-in',
      });

    const { jwt } = await signIn();

    // 3. Authenticate the wallet client with the JWT and the session signer.
    const client = new DynamicEvmWalletClient({
      environmentId,
      enableMPCAccelerator: false, // Set to true only on AWS Nitro Enclave-compatible infrastructure
    });
    await client.authenticateJwt(jwt, {
      getSessionSignature: (message) => signSessionMessage(message, privateKeyJwk),
    });

    // 4. Create a wallet for the agent's user, backed up to Dynamic. First run
    // only — on later runs, load the persisted walletMetadata and
    // externalServerKeyShares instead of creating a new wallet.
    const { walletMetadata, externalServerKeyShares } = await client.createWalletAccount({
      thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
      password: process.env.WALLET_PASSWORD,
      backUpToDynamic: true,
    });
    console.log('Wallet created:', walletMetadata.accountAddress);
    // Persist walletMetadata (durable store) and externalServerKeyShares (secrets vault).

    // 5. Sign as the agent's user.
    const signature = await client.signMessage({
      message: 'Hello from your agent',
      walletMetadata,
      externalServerKeyShares,
      password: process.env.WALLET_PASSWORD,
    });
    console.log('Signed:', signature);

    // 6. Long-running sessions: refresh; past the refresh limit (refreshExp),
    // sign in again — no human needed.
    try {
      const refreshedJwt = await client.refreshAuthToken();
      // Overwrite the stored JWT with refreshedJwt.
    } catch {
      const { jwt: newJwt } = await signIn();
      await client.authenticateJwt(newJwt, {
        getSessionSignature: (message) => signSessionMessage(message, privateKeyJwk),
      });
    }
    ```
  </Tab>
</Tabs>

Run either with your environment variables set, for example:

```bash theme={"system"}
npx tsx --env-file=.env agent.ts
```

If sign-in or wallet creation fails, check the [dashboard configuration steps in the quickstart](/node/quickstart) first: embedded wallets enabled, chains enabled, and the environment ID correct.

## Related pages

* [Sign In with Email OTP](/node/agents/sign-in-with-email-otp)
* [Sign In with a Private Key](/node/agents/sign-in-with-a-private-key)
* [Use Agent Wallets](/node/agents/use-agent-wallets)
* [Storage & Security](/node/agents/storage-and-security)
