Skip to main content
The @privy-io/server-auth library is deprecated. This page compiles legacy documentation for existing customers who have not yet migrated. For new integrations, refer to the @privy-io/node guide. For migration instructions, see the migration guide.

Installation

Originally documented at Installation.
In a backend JS environment, the @privy-io/server-auth library authorizes requests and manages your application from your server. Install the library using your package manager of choice:
npm install @privy-io/server-auth@latest

Setup

Originally documented at Setup.

Prerequisites

Before you begin:

Instantiating the PrivyClient

Import the PrivyClient class and create an instance by passing the Privy app ID and app secret as parameters.
import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient('insert-your-app-id', 'insert-your-app-secret');

Quickstart

Originally documented at Quickstart.

1. Creating a wallet

Create a wallet and save its id for future calls.
const {id, address, chainType} = await privy.walletApi.createWallet({chainType: 'ethereum'});

2. Signing a message

Sign a plaintext message with the wallet using the signMessage method.
const {signature, encoding} = await privy.walletApi.ethereum.signMessage({
  walletId: id,
  message: 'Hello Privy!'
});

3. Sending transactions

In order to send a transaction, your wallet must have some funds to pay for gas. Use a testnet faucet to test on a testnet like Base Sepolia.
Use the sendTransaction method to populate missing network-related values, sign, broadcast, and return the transaction hash.
const data = await privy.walletApi.ethereum.sendTransaction({
  walletId: id,
  caip2: 'eip155:84532',
  transaction: {
    to: '0xyourRecipientAddress',
    value: '0x2386F26FC10000',
    chainId: 84532
  }
});

const {hash} = data;

Verify access tokens

Originally documented at Access tokens.
Pass the user’s access token as a string to the PrivyClient’s verifyAuthToken method:
export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;
declare const authToken: string;

// `privy` refers to an instance of the `PrivyClient`
try {
  const verifiedClaims = await privy.verifyAuthToken(authToken);
} catch (error) {
  console.log(`Token verification failed with error ${error}.`);
}
To avoid a network request on each verification, pass the verification key directly:
export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;
declare const authToken: string;

const verifiedClaims = await privy.verifyAuthToken(
  authToken,
  // Pass the verification key copied from the Dashboard as the second parameter
  'paste-your-verification-key-from-the-dashboard'
);

Create or import a user

Originally documented at Create or import a user.
Use the PrivyClient’s importUser method to create or import a single user.
const user = await privy.importUser({
  linkedAccounts: [
    {
      type: 'email',
      address: '[email protected]'
    }
  ],
  wallets: [{chainType: 'ethereum'}],
  customMetadata: {
    key: 'value'
  }
});

Parameters

linkedAccounts
LinkedAccount[]
required
An array of the user’s linked accounts.
customMetadata
object
Custom metadata to associate with the user.
wallets
WalletCreateRequestType[]
An array of wallets to create for the user.

Query users

Originally documented at Querying users.
export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

const user = await privy.getUser({idToken: 'your-idToken'});

Query by Privy DID

export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

const user = await privy.getUserById('did:privy:XXXXXX');

Get all users

export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

const users = await privy.getUsers();

Query by account data

const user = await privy.getUserByEmail('[email protected]');
const user = await privy.getUserByPhoneNumber('+1 555 555 5555');
const user = await privy.getUserByWalletAddress('0xABCDEFGHIJKL01234567895C5cAe8B9472c14328');
const user = await privy.getUserByCustomAuthId('123');
const user = await privy.getUserByFarcasterId(1402);
const user = await privy.getUserByTwitterSubject('456');
const user = await privy.getUserByTwitterUsername('batman');
const user = await privy.getUserByDiscordUsername('batman');
const user = await privy.getUserByTelegramUserId('456');
const user = await privy.getUserByTelegramUsername('batman');

Delete a user

Originally documented at Deleting users.
Use the PrivyClient’s deleteUser method to delete a user. Pass the user’s Privy DID as a string:
export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

await privy.deleteUser('did:privy:XXXXXX');
This method throws an error if the deletion fails (e.g. due to an invalid Privy DID).

Custom metadata

Originally documented at Custom metadata.
Use the PrivyClient’s setCustomMetadata method to set custom metadata for a user by their DID.
import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient(process.env.PRIVY_APP_ID!, process.env.PRIVY_APP_SECRET!);

const user = await privy.setCustomMetadata('did:privy:XXXXXX', {username: 'name'});
When using TypeScript, specify a type generic to enable type inference:
const user = await privy.setCustomMetadata<{key1: string}>('did:privy:XXXXXX', customMetadata);

Allowlist

Originally documented at Allowlist.

Add to allowlist

Use the inviteToAllowlist method to add a user to your allowlist.
const allowlistEntry = await privy.inviteToAllowlist({
  type: 'email',
  value: '[email protected]'
});
type
'email' | 'phone' | 'wallet'
required
The type of account to add to the allowlist.
value
string
required
The identifier of the account to add to the allowlist.

Remove from allowlist

Use the removeFromAllowlist method to remove a user from your allowlist.
const removedAllowlistEntry = await privy.removeFromAllowlist({
  type: 'email',
  value: '[email protected]'
});

Get allowlist

Use the getAllowlist method to get your app’s current allowlist.
const allowlist = await privy.getAllowlist();

Test accounts

Originally documented at Using test accounts.
Use the getTestAccessToken method to get an access token for a test account.
import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient('insert-your-app-id', 'insert-your-app-secret');

// Uses the first test account by default
const {accessToken} = await privy.getTestAccessToken();

// Or, select a test account by email
const {accessToken: tokenByEmail} = await privy.getTestAccessToken({
  email: '[email protected]'
});

// Or, select a test account by phone number
const {accessToken: tokenByPhone} = await privy.getTestAccessToken({
  phoneNumber: '+1 555 555 XXXX'
});

Create a wallet

Originally documented at Create a wallet.
Use the createWallet method from the Privy client’s walletApi class:
export {};
type WalletApiCreateRequestType = import('@privy-io/server-auth').WalletApiCreateRequestType;
type WalletApiCreateResponseType = import('@privy-io/server-auth').WalletApiWalletResponseType;

createWallet: (input: WalletApiCreateRequestType) => Promise<WalletApiCreateResponseType>;

Usage

export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

const {id, address, chainType} = await privy.walletApi.createWallet({
  chainType: 'ethereum',
  owner: {userId: 'privy:did:xxxxx'}
});

Parameters

chainType
'ethereum' | 'solana' | 'stellar' | 'cosmos' | 'sui' | 'tron' | 'bitcoin-segwit' | 'near' | 'ton' | 'starknet' | 'aptos'
required
Chain type of the wallet to create.
owner
{'userId': string} | {'publicKey': string}
The user ID or P-256 public key to set as the owner. Do not specify ownerId if providing this.
ownerId
string
The key quorum ID of the owner. Do not specify owner if providing this.
policyIds
string[]
List of policy IDs to enforce on the wallet.
idempotencyKey
string
Idempotency key to identify a unique request.
additionalSigners
{'signerId': string}[]
List of key quorum IDs allowed to approve transactions for the wallet.

Returns

id
string
Unique ID of the created wallet.
address
string
Address of the created wallet.
chainType
string
Chain type of the created wallet.

Get wallet by ID

Originally documented at Get wallet by ID.
getWallet: ({id}: {id: string}) => Promise<WalletApiWalletResponseType>;

Usage

const wallet = await client.walletApi.getWallet({id: walletId});

Parameters

id
string
The ID of the wallet to get.

Returns

wallet
WalletApiWalletResponseType

Get all wallets

Originally documented at Get all wallets.
Use the Privy client’s walletApi.getWallets method. This is a paginated query.
getWallets: ({cursor?: string, limit?: number, chainType?: 'ethereum' | 'solana'}) => Promise<{data: WalletApiWalletResponseType[], nextCursor?: string}>

Usage

const wallets = [];
let nextCursor;

do {
  const result = await privy.walletApi.getWallets({chainType: 'ethereum', cursor: nextCursor});
  wallets.push(...result.data);
  nextCursor = result.nextCursor;
} while (nextCursor);

Import a wallet (private key)

Originally documented at Import a wallet (private key).
Use the importWallet method from the Privy client’s walletApi class.
The Privy client accepts a hex-encoded private key, with or without a 0x prefix.
import {PrivyClient, WalletApiWalletResponseType} from '@privy-io/server-auth';

const privy = new PrivyClient('your-app-id', 'your-app-secret');

const wallet: WalletApiWalletResponseType = await privy.walletApi.importWallet({
  address: '<your-wallet-address>',
  chainType: 'ethereum',
  entropy: '<your-hex-encoded-wallet-private-key>',
  entropyType: 'private-key'
});

Import an HD wallet

Originally documented at HD wallets.
Use the importWallet method from the Privy client’s walletApi class.
import {PrivyClient, WalletApiWalletResponseType} from '@privy-io/server-auth';

const privy = new PrivyClient('your-app-id', 'your-app-secret');

const wallet: WalletApiWalletResponseType = await privy.walletApi.importWallet({
  address: '<your-wallet-address>',
  chainType: 'ethereum',
  entropy: '<your-bip39-mnemonic>',
  entropyType: 'hd',
  index: 0
});

Sign a message (Ethereum)

Originally documented at Sign a message.
Use the signMessage method on the Ethereum client.
signMessage: async ({walletId: string, message: string, idempotencyKey?: string}) => Promise<{signature: string, encoding: 'hex'}>

Usage

const {signature, encoding} = await privy.walletApi.ethereum.signMessage({
  walletId: 'insert-wallet-id',
  message: 'Hello world'
});

Parameters

walletId
string
required
Unique ID of the wallet.
message
string | Uint8Array
required
The string or bytes to sign.
idempotencyKey
string

Returns

signature
string
The signature produced by the wallet.
encoding
'hex'
The encoding format for the signature.

Sign typed data (EIP-712)

Originally documented at Sign typed data.
Use the signTypedData method on the Ethereum client.
signTypedData: (input: {walletId: string, typedData: TypedData}) => Promise<{signature: string, encoding: 'hex'}>

Usage

const {signature, encoding} = await privy.walletApi.ethereum.signTypedData({
  walletId: 'insert-wallet-id',
  typedData: {
    domain: {
      name: 'Ether Mail',
      version: '1',
      chainId: 1,
      verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
    },
    types: {
      EIP712Domain: [
        {name: 'name', type: 'string'},
        {name: 'version', type: 'string'},
        {name: 'chainId', type: 'uint256'},
        {name: 'verifyingContract', type: 'address'}
      ],
      Person: [
        {name: 'name', type: 'string'},
        {name: 'wallet', type: 'address'}
      ],
      Mail: [
        {name: 'from', type: 'Person'},
        {name: 'to', type: 'Person'},
        {name: 'contents', type: 'string'}
      ]
    },
    primaryType: 'Mail',
    message: {
      from: {name: 'Cow', wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826'},
      to: {name: 'Bob', wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB'},
      contents: 'Hello, Bob!'
    }
  }
});

Sign a transaction (Ethereum)

Originally documented at Sign a transaction.
Use the signTransaction method on the Ethereum client.
signTransaction: (input: EthereumSignTransactionInputType) => Promise<EthereumSignTransactionResponseType>

Usage

const {signedTransaction, encoding} = await privy.walletApi.ethereum.signTransaction({
  walletId: 'insert-wallet-id',
  transaction: {
    to: '0xE3070d3e4309afA3bC9a6b057685743CF42da77C',
    value: '0x2386F26FC10000',
    chainId: 8453
  }
});

Parameters

walletId
string
required
The ID of the wallet.
transaction
EthereumTransactionType
required
The transaction to sign.

Returns

signedTransaction
string
The signed transaction.
encoding
'rlp'
The encoding format. Only 'rlp' is supported.

Sign a raw hash

Originally documented at Sign a raw hash.
Use the secp256k1Sign method on the Ethereum client.
secp256k1Sign: async ({walletId: string, hash: string}) => Promise<{signature: string, encoding: 'hex'}>

Usage

const {signature, encoding} = await privy.walletApi.ethereum.secp256k1Sign({
  walletId: 'insert-wallet-id',
  hash: '0x6503b027a625549f7be691646404f275f149d17a119a6804b855bac3030037aa'
});

Parameters

walletId
string
required
Unique ID of the wallet.
hash
hash
required
The hash to sign. Must start with 0x.

Sign EIP-7702 authorization

Originally documented at Sign EIP-7702 authorization.
Use the sign7702Authorization method on the Ethereum client.
sign7702Authorization: async ({walletId: string, contract: string, chainId: number, nonce?: number, idempotencyKey?: string}) => Promise<{chainId, contract, nonce, yParity, r, s}>

Usage

const authorization = await privy.walletApi.ethereum.sign7702Authorization({
  walletId: 'insert-wallet-id',
  contract: '0x1234567890abcdef1234567890abcdef12345678',
  chainId: 1,
  nonce: 0
});

Parameters

walletId
string
required
Unique ID of the wallet.
contract
Hex
required
The smart contract address to delegate to.
chainId
number
required
The chain ID for the authorization.
nonce
number
The nonce for the authorization. Defaults to the current transaction count.
idempotencyKey
string

Send a transaction (Ethereum)

Originally documented at Send a transaction.
Use the sendTransaction method on the Ethereum client.
sendTransaction: (input: EthereumSendTransactionInputType) => Promise<EthereumSendTransactionResponseType>

Usage

const {hash, caip2} = await privy.walletApi.ethereum.sendTransaction({
  walletId: 'insert-wallet-id',
  caip2: 'eip155:8453',
  transaction: {
    to: '0xE3070d3e4309afA3bC9a6b057685743CF42da77C',
    value: '0x2386F26FC10000',
    chainId: 8453
  },
  sponsor: true
});

Parameters

walletId
string
required
The ID of the wallet.
caip2
`eip155:${number}`
required
The CAIP2 chain ID.
transaction
EthereumTransactionType
required
The transaction to send.
sponsor
boolean
Enable gas sponsorship. Learn more.

Returns

hash
string
The transaction hash.
caip2
`eip155:${number}`
The CAIP2 chain ID.

Web3 integrations (Ethereum)

Originally documented at Interfacing with common libraries.

viem

Use Privy’s createViemAccount method to initialize a viem Account for an EVM wallet.
import {PrivyClient} from '@privy-io/server-auth';
import {createViemAccount} from '@privy-io/server-auth/viem';

const privy = new PrivyClient(...);
const account = await createViemAccount({
  walletId: 'insert-wallet-id',
  address: 'insert-address',
  privy
});
From the returned Account, initialize a viem WalletClient:
import {createWalletClient, http, parseEther} from 'viem';
import {base} from 'viem/chains';

const client = createWalletClient({
  account,
  chain: base,
  transport: http()
});

const hash = await client.sendTransaction({
  to: '0x59D3eB21Dd06A211C89d1caBE252676e2F3F2218',
  value: parseEther('0.001')
});

ethers

Use Privy’s createEthersSigner method to initialize an ethers signer.
import {ethers, TransactionRequest} from 'ethers';
import {PrivyClient} from '@privy-io/server-auth';
import {createEthersSigner} from '@privy-io/server-auth/ethers';

const privyClient = new PrivyClient('insert-your-app-id', 'insert-your-app-secret');
const provider = new ethers.JsonRpcProvider('https://base.llamarpc.com');
const walletId = 'insert-wallet-id';
const wallet = await privyClient.walletApi.getWallet({id: walletId});

const signer = createEthersSigner({
  walletId,
  address: wallet.address,
  provider,
  privyClient: privyClient as any
});

const TO_ADDRESS = '0xE3070d3e4309afA3bC9a6b057685743CF42da77C';
const hash = await signer.sendTransaction({to: TO_ADDRESS, value: 100, chainId: 8453});

Sign a message (Solana)

Originally documented at Sign a message (Solana).
Use the signMessage method on the Solana client.

Usage

const {signature, encoding} = await privy.walletApi.solana.signMessage({
  walletId: 'insert-wallet-id',
  message: 'Hello world'
});

Parameters

walletId
string
required
Unique ID of the wallet.
message
string | Uint8Array
required
The string or bytes to sign.
idempotencyKey
string

Sign a transaction (Solana)

Originally documented at Sign a transaction (Solana).
Use the signTransaction method on the Solana client.
signTransaction: (input: SolanaSignTransactionInputType) => Promise<SolanaSignTransactionResponseType>

Usage

import {
  clusterApiUrl,
  Connection,
  LAMPORTS_PER_SOL,
  PublicKey,
  SystemProgram,
  Transaction,
  VersionedTransaction,
  TransactionMessage
} from '@solana/web3.js';

const walletPublicKey = new PublicKey(wallet.address);
const connection = new Connection(clusterApiUrl('devnet'));
const instruction = SystemProgram.transfer({
  fromPubkey: walletPublicKey,
  toPubkey: new PublicKey(address),
  lamports: value * LAMPORTS_PER_SOL
});

const {blockhash: recentBlockhash} = await connection.getLatestBlockhash();

const message = new TransactionMessage({
  payerKey: walletPublicKey,
  instructions: [instruction],
  recentBlockhash
});

const yourSolanaTransaction = new VersionedTransaction(message.compileToV0Message());

const {signedTransaction} = await privy.walletApi.solana.signTransaction({
  walletId: wallet.id,
  transaction: yourSolanaTransaction
});

Parameters

walletId
string
required
The ID of the wallet.
transaction
Transaction | VersionedTransaction
required
The transaction to sign.

Returns

signedTransaction
string
The signed transaction.
encoding
'base64'
The encoding format. Only 'base64' is supported.

Send a transaction (Solana)

Originally documented at Send a transaction (Solana).
Use the signAndSendTransaction method on the Solana client.
signAndSendTransaction: (input: SolanaSignAndSendTransactionInputType) => Promise<SolanaSignAndSendTransactionResponseType>

Usage

import {PublicKey, SystemProgram, VersionedTransaction, TransactionMessage} from '@solana/web3.js';

const walletPublicKey = new PublicKey(wallet.address);
const instruction = SystemProgram.transfer({
  fromPubkey: walletPublicKey,
  toPubkey: new PublicKey(recipientAddress),
  lamports: amount
});

const message = new TransactionMessage({
  payerKey: walletPublicKey,
  instructions: [instruction],
  recentBlockhash
});

const transaction = new VersionedTransaction(message.compileToV0Message());

const {hash} = await privy.walletApi.solana.signAndSendTransaction({
  walletId: 'insert-wallet-id',
  caip2: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
  transaction: transaction,
  sponsor: true
});

Parameters

walletId
string
required
The ID of the wallet.
caip2
string
required
The CAIP2 chain ID.
transaction
Transaction | VersionedTransaction
required
The transaction to sign and send.
sponsor
boolean
Enable gas sponsorship.

Returns

hash
string
The transaction hash.
caip2
string
The CAIP2 chain ID.

Send SOL

Originally documented at Sending a SOL transaction.
import {PrivyClient} from '@privy-io/server-auth';

declare function createSOLTransferTransaction(...args: any[]): Promise<{transaction: any}>;

const privy = new PrivyClient(process.env.PRIVY_APP_ID!, process.env.PRIVY_APP_SECRET!);

const {transaction} = await createSOLTransferTransaction(
  'insert-wallet-address',
  'recipient-wallet-address',
  0.01 // amount in SOL
);

const response = await privy.walletApi.solana.signAndSendTransaction({
  walletId: 'insert-wallet-id',
  address: 'insert-wallet-address',
  caip2: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
  transaction: transaction
});

Send SPL tokens

Originally documented at Sending SPL tokens.
import {PrivyClient} from '@privy-io/server-auth';

declare function createSPLTransferTransaction(...args: any[]): Promise<{transaction: any}>;

const privy = new PrivyClient(process.env.PRIVY_APP_ID!, process.env.PRIVY_APP_SECRET!);

const {transaction} = await createSPLTransferTransaction(
  'insert-wallet-address',
  'recipient-wallet-address',
  'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC mint address
  10 // Amount to send
);

const response = await privy.walletApi.solana.signAndSendTransaction({
  walletId: 'insert-wallet-id',
  address: 'insert-wallet-address',
  caip2: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
  transaction: transaction
});

Send USDC (ERC-20)

Originally documented at Sending USDC.
import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient(process.env.PRIVY_APP_ID!, process.env.PRIVY_APP_SECRET!);

const usdcContractAddress = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // on Base

const {hash} = await privy.walletApi.ethereum.sendTransaction({
  walletId: 'insert-wallet-id',
  caip2: 'eip155:8453',
  transaction: {
    to: usdcContractAddress,
    data: '0x...', // encoded transfer call
    chainId: 8453
  }
});

Create a policy

Originally documented at Create a policy.
Use the PrivyClient’s createPolicy method.
const policy = await privy.walletApi.createPolicy({
  name: 'Allowlist certain smart contracts',
  version: '1.0',
  chainType: 'ethereum',
  rules: [
    {
      name: 'Allowlist USDC',
      method: 'eth_sendTransaction',
      action: 'ALLOW',
      conditions: [
        {
          fieldSource: 'ethereum_transaction',
          field: 'to',
          operator: 'eq',
          value: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
        }
      ]
    }
  ],
  ownerId: 'fmfdj6yqly31huorjqzq38zc'
});

Get a policy

Originally documented at Get a policy.
Use the PrivyClient’s getPolicy method.
const policy = await privy.getPolicy({
  id: 'fmfdj6yqly31huorjqzq38zc'
});

Update a policy

Originally documented at Update a policy.

Add a rule to a policy

const rule = await client.walletApi.addRuleToPolicy({
  policyId: 'fmfdj6yqly31huorjqzq38zc',
  name: 'Allowlist USDT',
  method: 'eth_sendTransaction',
  conditions: [
    {
      fieldSource: 'ethereum_transaction',
      field: 'to',
      operator: 'eq',
      value: '0xdAC17F958D2ee523a2206206994597C13D831ec7'
    }
  ],
  action: 'ALLOW'
});

Edit a rule in a policy

const rule = await client.walletApi.updateRuleInPolicy({
  policyId: 'fmfdj6yqly31huorjqzq38zc',
  ruleId: 'allow-list-usdt-18381838',
  name: 'Allowlist USDT',
  method: 'eth_sendTransaction',
  conditions: [
    {
      fieldSource: 'ethereum_transaction',
      field: 'to',
      operator: 'eq',
      value: '0xdAC17F958D2ee523a2206206994597C13D831ec7'
    }
  ],
  action: 'ALLOW'
});

Delete a rule from a policy

import {PrivyClient} from '@privy-io/server-auth';

const client = new PrivyClient('insert-app-id', 'insert-app-secret');

const rule = await client.walletApi.deleteRuleFromPolicy({
  policyId: 'fmfdj6yqly31huorjqzq38zc',
  ruleId: 'allow-list-usdt-18381838'
});

Update a whole policy

const policy = await client.walletApi.updatePolicy({
  id: 'fmfdj6yqly31huorjqzq38zc',
  name: 'Transactions must be <= 5ETH',
  rules: [
    {
      name: 'Transactions must be <= 5ETH',
      method: 'eth_sendTransaction',
      action: 'ALLOW',
      conditions: [
        {
          fieldSource: 'ethereum_transaction',
          field: 'value',
          operator: 'lte',
          value: '0x2386F26FC10000'
        }
      ]
    }
  ]
});

Request a user authorization key

Originally documented at Using user owners & signers.

1. Request a user key

Use the generateUserSigner method of the Privy client.
import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient('insert-your-app-id', 'insert-your-app-secret');

const {authorizationKey} = await privy.walletApi.generateUserSigner({
  userJwt: 'insert-user-jwt'
});
userJwt
string
required
The user’s JWT to authenticate the user.

2. Update the Privy client to use the user’s keypair

export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

privy.walletApi.updateAuthorizationKey('insert-user-authorization-key');

3. Execute requests with the user’s authorization key

Once updated, the Privy client automatically signs requests made via privy.walletApi.ethereum.* and privy.walletApi.solana.* methods.

Use signers

Originally documented at Use signers.
Use the Privy client’s getUser method to get a user object. Pass the user’s identity token:
const user = await client.getUser({identityToken});
Filter for wallets with signers:
const walletsWithSessionSigners = user.linkedAccounts.filter(
  (account): account is WalletWithMetadata =>
    account.type === 'wallet' && account.delegated === true
);

Using user signers

Originally documented at Using user signers.
Once you have created a wallet with a user signer, your application will:
  1. Generate a SPKI-formatted ECDH P-256 keypair.
  2. Request a time-bound session key from /v1/wallets/authenticate using the user’s JWT and the public key.
  3. Send a transaction to the Wallet API, signed with the session key.

Generate an ECDH P-256 keypair

import * as crypto from 'crypto';

async function generateEcdhP256KeyPair(): Promise<{
  privateKey: crypto.webcrypto.CryptoKey;
  recipientPublicKey: string;
}> {
  const keyPair = await crypto.subtle.generateKey({name: 'ECDH', namedCurve: 'P-256'}, true, [
    'deriveBits'
  ]);

  const privateKey = keyPair.privateKey;
  const publicKeyInSpkiFormat = await crypto.subtle.exportKey('spki', keyPair.publicKey);
  const recipientPublicKey = Buffer.from(publicKeyInSpkiFormat).toString('base64');

  return {privateKey, recipientPublicKey};
}

POST /v1/wallets/authenticate

export {};
declare function generateEcdhP256KeyPair(): Promise<{
  privateKey: CryptoKey;
  recipientPublicKey: string;
}>;

async function authenticateUserWallet() {
  const jwt = 'your-user-jwt';
  const appId = 'your-app-id';
  const appSecret = 'your-app-secret';

  const {recipientPublicKey} = await generateEcdhP256KeyPair();

  const basicAuth = Buffer.from(`${appId}:${appSecret}`).toString('base64');
  const response = await fetch(`https://api.privy.io/v1/wallets/authenticate`, {
    method: 'POST',
    headers: {
      Authorization: `Basic ${basicAuth}`,
      'Content-Type': 'application/json',
      'privy-app-id': appId
    },
    body: JSON.stringify({
      user_jwt: jwt,
      encryption_type: 'HPKE',
      recipient_public_key: recipientPublicKey
    })
  });

  return await response.json();
}

Decrypt the response

import {CipherSuite, DhkemP256HkdfSha256, HkdfSha256} from '@hpke/core';
import {Chacha20Poly1305} from '@hpke/chacha20poly1305';

async function decryptEncapsulatedKey(
  encrypted_authorization_key: any,
  privateKey: CryptoKey
): Promise<string> {
  const {encapsulated_key: encapsulatedKey, ciphertext} = encrypted_authorization_key;

  const suite = new CipherSuite({
    kem: new DhkemP256HkdfSha256(),
    kdf: new HkdfSha256(),
    aead: new Chacha20Poly1305()
  });

  const context = await suite.createRecipientContext({
    recipientKey: privateKey,
    enc: Buffer.from(encapsulatedKey, 'base64')
  });

  const decrypted = await context.open(Buffer.from(ciphertext, 'base64'));
  return Buffer.from(decrypted).toString('utf8');
}

Send a transaction using the decrypted key

import {PrivyClient} from '@privy-io/server-auth';

declare function authenticateUserWallet(): Promise<any>;
declare function decryptEncapsulatedKey(key: any, privateKey: CryptoKey): Promise<string>;
declare const privateKey: CryptoKey;

const resp = await authenticateUserWallet();
const {encrypted_authorization_key, wallets} = resp;
const decryptedKey = await decryptEncapsulatedKey(encrypted_authorization_key, privateKey);
const walletId = wallets[0].id;

const client = new PrivyClient('insert-your-app-id', 'insert-your-app-secret', {
  walletApi: {
    authorizationPrivateKey: decryptedKey
  }
});

const res = await client.walletApi.ethereum.signMessage({
  walletId: walletId,
  message: 'Hello world'
});

Originally documented at Sponsoring transactions on Ethereum.

0. Install dependencies

npm i @privy-io/server-auth permissionless viem

1. Create a wallet

import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient('your privy app id', 'your privy app secret');

const {
  id: walletId,
  address,
  chainType
} = await privy.walletApi.createWallet({chainType: 'ethereum'});

2. Get a viem LocalAccount

import {createViemAccount} from '@privy-io/server-auth/viem';

const serverWalletAccount = await createViemAccount({walletId, address, privy});

3. Create a smart wallet

import {toKernelSmartAccount} from 'permissionless/accounts';
import {entryPoint07Address} from 'viem/account-abstraction';

const kernelSmartAccount = await toKernelSmartAccount({
  client: publicClient,
  entryPoint: {address: entryPoint07Address, version: '0.7'},
  owner: serverWalletAccount
});

4. Create a smart account client

import {createSmartAccountClient} from 'permissionless';
import {createPublicClient, http} from 'viem';

const smartAccountClient = createSmartAccountClient({
  account: kernelSmartAccount,
  chain: sepolia,
  paymaster: paymasterClient,
  bundlerTransport: http(bundlerUrl),
  userOperation: {
    estimateFeesPerGas: async () => (await paymasterClient.getUserOperationGasPrice()).fast
  }
});

Fetch a transaction

Originally documented at Fetch transaction via API.
Use the getTransaction method from the Privy client.
import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient('insert-your-app-id', 'insert-your-app-secret');

const transaction = await privy.walletApi.getTransaction({
  id: 'insert-transaction-id'
});

Parameters

id
string
required
ID of the transaction to fetch.

Pregenerate wallets

Originally documented at Pregenerating wallets.
To pregenerate wallets for a new user, use the importUser method.
const privy = new PrivyClient('your-app-id', 'your-app-secret');

const user = await privy.importUser({
  linkedAccounts: [
    {
      type: 'email',
      address: '[email protected]'
    }
  ],
  wallets: [
    {
      chainType: 'ethereum',
      walletIndex: 0,
      additionalSigners: [
        {
          signerId: '<signer-id>',
          overridePolicyIds: ['<policy-id>']
        }
      ],
      policyIds: ['<policy-id>']
    },
    {
      chainType: 'solana',
      walletIndex: 0,
      additionalSigners: [
        {
          signerId: '<signer-id>',
          overridePolicyIds: ['<policy-id>']
        }
      ],
      policyIds: []
    }
  ],
  createDirectSigner: true
});

Server-side user wallets

Originally documented at Server-side user wallets.
Use the Privy client’s importUser method to create a user.
export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

const user = await privy.importUser({
  linkedAccounts: [
    {
      type: 'custom_auth',
      customUserId: 'insert-user-id-from-authentication-provider'
    }
  ]
});

const id = user.id;

Verify access token (optimizing)

Originally documented at Optimize your setup.
To avoid a network call when verifying access tokens, pass the verification key directly:
import {PrivyClient} from '@privy-io/server-auth';

const privy = new PrivyClient('your-privy-app-id', 'your-privy-app-secret');

const verifiedClaims = await privy.verifyAuthToken(
  '$AUTH_TOKEN',
  'paste-your-verification-key-from-the-dashboard'
);

Idempotency keys

Originally documented at Idempotency keys.
import {PrivyClient} from '@privy-io/server-auth';
import {v4 as uuidv4} from 'uuid';

const client = new PrivyClient('$PRIVY_APP_ID', '$PRIVY_APP_SECRET');

const idempotencyKey = uuidv4();

const res = await client.walletApi.ethereum.sendTransaction({
  walletId: '$WALLET_ID',
  idempotencyKey,
  caip2: 'eip155:8453',
  transaction: {
    to: '0xE3070d3e4309afA3bC9a6b057685743CF42da77C',
    value: '0x2386F26FC10000',
    chainId: 8453
  }
});

tRPC integration

Originally documented at Integrating with tRPC.
import * as trpc from '@trpc/server';
import {inferAsyncReturnType} from '@trpc/server';
import * as trpcNext from '@trpc/server/adapters/next';

import {PrivyClient, AuthTokenClaims} from '@privy-io/server-auth';

const privy = new PrivyClient(
  process.env.NEXT_PUBLIC_PRIVY_APP_ID || '',
  process.env.PRIVY_APP_SECRET || ''
);

export async function createContext({req, res}: trpcNext.CreateNextContextOptions) {
  const authToken = req.headers.authorization.replace('Bearer ', '');
  let userClaim: AuthTokenClaims | undefined = undefined;

  if (authToken) {
    try {
      userClaim = await privy.verifyAuthToken(authToken);
    } catch (_) {
      // Expected error for unauthenticated procedures
    }
  }
  return {userClaim};
}
export type Context = inferAsyncReturnType<typeof createContext>;

Speeding up transactions

Originally documented at Speeding up transactions on EVM chains.
Send a replacement transaction using the same nonce:
export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;
declare const payload: any;

const {hash, caip2} = await privy.walletApi.ethereum.sendTransaction({
  walletId: payload.wallet_id,
  caip2: payload.caip2,
  transaction: {
    to: payload.transaction_request.to,
    value: payload.transaction_request.value,
    data: payload.transaction_request.data,
    nonce: payload.transaction_request.nonce
  }
});

Telegram bot

Originally documented at Building a Telegram trading bot.

Initialize the client

const TelegramBot = require('node-telegram-bot-api');
const {PrivyClient} = require('@privy-io/server-auth');

const token = 'YOUR_TELEGRAM_BOT_TOKEN';
const bot = new TelegramBot(token, {polling: true});
const privy = new PrivyClient('insert-app-id', 'insert-app-secret');

Send a transaction on command

import type {WalletWithMetadata} from '@privy-io/server-auth';

declare const bot: any;
declare const privy: import('@privy-io/server-auth').PrivyClient;
declare function getTransactionDetailsFromMsg(msg: any): any;

bot.onText(/\/transact/, async (msg: any) => {
  const transaction = getTransactionDetailsFromMsg(msg);
  const user = await privy.getUserByTelegramUserId(msg.from.id);

  const wallet = user?.linkedAccounts.find(
    (account): account is WalletWithMetadata =>
      account.type === 'wallet' && account.walletClientType === 'privy'
  );
  const walletId = wallet?.id;

  if (!walletId) throw new Error('Cannot determine wallet ID for user');

  await privy.walletApi.solana.signAndSendTransaction({walletId, ...transaction});
});

Bot-first wallet creation

export {};
declare const bot: any;
declare const privy: import('@privy-io/server-auth').PrivyClient;

bot.onText(/\/start/, async (msg: any) => {
  const telegramUserId = msg.from.id;
  const privyUser = await privy.importUser({
    linkedAccounts: [{type: 'telegram', telegramUserId}]
  });

  const wallet = await privy.walletApi.createWallet({
    chainType: 'solana',
    owner: {userId: privyUser.id},
    additionalSigners: [{signerId: 'id-of-authorization-key-from-dashboard'}]
  });
});

Flashblocks

Originally documented at Using Flashblocks with Privy.
Sign a transaction with the @privy-io/server-auth SDK and broadcast to your custom Flashblocks RPC URL:
export {};
declare const privy: import('@privy-io/server-auth').PrivyClient;

const {signedTransaction} = await privy.walletApi.ethereum.signTransaction({
  walletId: 'insert-wallet-id',
  transaction: {
    to: '0xE3070d3e4309afA3bC9a6b057685743CF42da77C',
    value: '0x2386F26FC10000',
    chainId: 8453
  }
});

Aave integration

Originally documented at Integrating Aave with Privy.

Installation

npm install @aave/client@latest @privy-io/server-auth@latest

Setup

import {PrivyClient} from '@privy-io/server-auth';
import {AaveClient} from '@aave/client';

const privyClient = new PrivyClient('insert-your-app-id', 'insert-your-app-secret');

const walletId = 'privy-wallet-id';
const walletAddress = 'privy-wallet-address';

const aaveClient = AaveClient.create();

Signing utility functions

Originally documented at Signing with utility functions.

Format a request for an authorization signature

import {formatRequestForAuthorizationSignature} from '@privy-io/server-auth/wallet-api';

const input = {
  version: 1,
  url: 'https://api.privy.io/v1/wallets/<insert-wallet-id>/rpc',
  method: 'POST',
  headers: {
    'privy-app-id': '<insert-app-id>'
  },
  body: {
    method: 'personal_sign',
    params: {
      message: 'Hello from Privy!',
      encoding: 'utf-8'
    }
  }
} as const;
const serializedPayload = formatRequestForAuthorizationSignature({input});