Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.privy.io/llms.txt

Use this file to discover all available pages before exploring further.

The @privy-io/js-sdk-core library is a vanilla JavaScript library for browser-like environments. It provides secure authentication, non-custodial embedded wallets, and user management without requiring React or any other UI framework.
@privy-io/js-sdk-core is a low-level library. Please do not attempt to use this library without first reaching out to the Privy team to discuss your project and which Privy SDK options may be better suited to it.

Prerequisites

Before you begin:
  • Set up your Privy app and obtain your app ID from the Privy Dashboard
  • Obtain your client ID from the Dashboard under Settings → Clients

Installation

npm install @privy-io/js-sdk-core@latest

1. Create the Privy client

Import the Privy class and create a single instance for your application. The client accepts the following configuration:
import Privy, {LocalStorage} from '@privy-io/js-sdk-core';

const privy = new Privy({
  appId: '<your-privy-app-id>',
  clientId: '<your-privy-client-id>',
  storage: new LocalStorage()
});
Only instantiate a single Privy client for your application. Creating multiple instances will cause unexpected behavior.
The Storage interface requires four methods. Implement this interface if LocalStorage is not suitable for your environment (e.g., encrypted storage, server-side rendering, or non-browser runtimes):
import type {Storage} from '@privy-io/js-sdk-core';

const myStorage: Storage = {
  get(key: string): Promise<string | null> {
    /* return value for key, or null */
  },
  put(key: string, val: string): Promise<void> {
    /* persist key-value pair */
  },
  del(key: string): Promise<void> {
    /* delete key */
  },
  getKeys(): Promise<string[]> {
    /* return all stored keys */
  }
};

2. Initialize the client

After creating the client, call initialize() to establish a connection with the Privy backend and restore any existing session. This must complete before performing any other operations.
try {
  await privy.initialize();
} catch (e) {
  // Initialization can fail if storage access is blocked or network is unavailable
  console.error('Privy initialization failed:', e);
}
After initialize() resolves, call client.user.get() to check for an existing authenticated session. If the user previously logged in and the session is still valid, this returns the user object without requiring re-authentication.

Restoring a returning user’s session

await privy.initialize();

// Check if a user is already logged in from a previous session
const {user} = await privy.user.get();
if (user) {
  // Store the user object in your application state (e.g., a store, context, or signal)
  // This is the source of truth for the authenticated user throughout your app
} else {
  // No active session — prompt the user to log in
}

3. Connect to the secure context

The Privy secure context is an iframe that handles embedded wallet key material. Your app must mount this iframe and wire up bidirectional message passing.

Mount the iframe

const iframe = document.createElement('iframe');
iframe.src = privy.embeddedWallet.getURL();
iframe.style.display = 'none';

// Track when the iframe is ready
let isProxyReady = false;
iframe.onload = () => {
  isProxyReady = true;
};

document.body.appendChild(iframe);

Wire up message passing

// Allow the Privy client to post messages to the iframe
privy.setMessagePoster(iframe.contentWindow);

// Forward messages from the iframe to the Privy client
window.addEventListener('message', (e) => {
  // Only process messages from the Privy iframe
  if (e.source !== iframe.contentWindow) return;
  const data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
  privy.embeddedWallet.onMessage(data);
});
If you are using a UI rendering library or framework, render the iframe and register event listeners using that library’s lifecycle methods instead of manipulating the DOM directly.

4. Authenticate a user

The Privy core SDK supports email, SMS, OAuth, and JWT-based authentication.
const emailAddress = '[email protected]';
await privy.auth.email.sendCode(emailAddress);

// Collect the OTP from your UI
const otp = '123456';
const session = await privy.auth.email.loginWithCode(emailAddress, otp);

5. Get access tokens for your backend

After authentication, use getAccessToken() to retrieve the user’s access token. Include this token in requests to your backend to verify the user’s identity.
const token = await privy.getAccessToken();

// Send authenticated requests to your backend
const response = await fetch('https://your-server.com/api/protected', {
  headers: {
    Authorization: `Bearer ${token}`
  }
});
getAccessToken() automatically handles token refresh when the access token is near expiration. The returned token is always valid at the time of return.

6. Create an embedded wallet

Your app can manually create wallets for users when desired.
Privy can provision wallets for your users on both Ethereum and Solana.
import {getUserEmbeddedEthereumWallet, getEntropyDetailsFromUser} from '@privy-io/js-sdk-core';

const {user} = await privy.embeddedWallet.create({});
const wallet = getUserEmbeddedEthereumWallet(user);
const {entropyId, entropyIdVerifier} = getEntropyDetailsFromUser(user);
const provider = await privy.embeddedWallet.getEthereumProvider({
  wallet,
  entropyId,
  entropyIdVerifier
});

7. Connect to an existing wallet

When a user returns to your app on a subsequent visit, the wallet already exists but a provider must be obtained.
import {getUserEmbeddedEthereumWallet, getEntropyDetailsFromUser} from '@privy-io/js-sdk-core';

const {user} = await privy.user.get();
const wallet = getUserEmbeddedEthereumWallet(user);
const {entropyId, entropyIdVerifier} = getEntropyDetailsFromUser(user);

const provider = await privy.embeddedWallet.getEthereumProvider({
  wallet,
  entropyId,
  entropyIdVerifier
});

8. Use the embedded wallet

Your wallet must have funds to pay for gas. Use a testnet faucet to test on Base Sepolia, or send funds to the wallet on your preferred network.
With the embedded wallet provider, your app can prompt the user to sign messages and send transactions.
const provider = await privy.embeddedWallet.getEthereumProvider({...args});

// Sign a message
await provider.request({
  method: 'personal_sign',
  params: ['hello', signingAddress]
});

// Send a transaction
await provider.request({
  method: 'eth_sendTransaction',
  params: [
    {
      to: '<recipient_address>',
      value: '0x2386F26FC10000' // 0.01 ETH in wei
    }
  ]
});
Learn more about sending transactions with the embedded wallet. Privy enables you to take many actions on the embedded wallet, including sign a message, sign typed data, and sign a transaction.

9. Log the user out

To end the user’s session and clean up resources:
const {user} = await privy.user.get();
await privy.auth.logout({userId: user.id});

// Clean up the iframe and event listeners
window.removeEventListener('message', listener);
iframe.remove();
After logout, the user must authenticate again to access any protected resources or wallet functionality.

Full integration example

Below is a minimal end-to-end integration showing the complete lifecycle:
import Privy, {
  LocalStorage,
  getUserEmbeddedEthereumWallet,
  getEntropyDetailsFromUser
} from '@privy-io/js-sdk-core';

// 1. Create the client (once, at app startup)
const privy = new Privy({
  appId: '<your-privy-app-id>',
  clientId: '<your-privy-client-id>',
  storage: new LocalStorage()
});

// 2. Initialize and check for existing session
await privy.initialize();
let {user} = await privy.user.get();

// 3. Set up the secure context
const iframe = document.createElement('iframe');
iframe.src = privy.embeddedWallet.getURL();
iframe.style.display = 'none';
document.body.appendChild(iframe);

privy.setMessagePoster(iframe.contentWindow);
const listener = (e) => {
  if (e.source !== iframe.contentWindow) return;
  const data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
  privy.embeddedWallet.onMessage(data);
};
window.addEventListener('message', listener);

// 4. Authenticate (if no existing session)
if (!user) {
  await privy.auth.email.sendCode('[email protected]');
  const session = await privy.auth.email.loginWithCode('[email protected]', '123456');
  user = session.user;
}

// 5. Get the wallet provider
const wallet = getUserEmbeddedEthereumWallet(user);
const {entropyId, entropyIdVerifier} = getEntropyDetailsFromUser(user);

const provider = await privy.embeddedWallet.getEthereumProvider({
  wallet,
  entropyId,
  entropyIdVerifier
});

// 6. Use the wallet
await provider.request({
  method: 'personal_sign',
  params: ['hello world', wallet.address]
});

// 7. Get a token for backend requests
const accessToken = await privy.getAccessToken();