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
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
1. Creating a wallet
Create a wallet and save its id for future calls.
const { id , address , chainType } = await privy . walletApi . createWallet ({ chainType: 'ethereum' });
const { id , address , chainType } = await privy . walletApi . createWallet ({ chainType: 'solana' });
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!'
});
const { signature , encoding } = await privy . walletApi . solana . signMessage ({
walletId: 'insert-wallet-id' ,
message: 'Hello world'
});
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 ;
const { hash } = await privy . walletApi . solana . signAndSendTransaction ({
walletId: wallet . id ,
caip2: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1' ,
transaction: yourSolanaTransaction
});
Verify 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
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
An array of the user’s linked accounts.
Custom metadata to associate with the user.
wallets
WalletCreateRequestType[]
An array of wallets to create for the user. chainType
'ethereum' | 'solana' | 'stellar' | 'cosmos' | 'sui' | 'tron' | 'bitcoin-segwit' | 'near' | 'ton' | 'starknet' | 'aptos'
required
The chain type of the wallet to create.
List of policy IDs for the wallet.
Set to true to create a smart wallet with the user’s wallet as the signer. Ethereum only.
Query users
Query by identity token (recommended)
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 . 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 . getUserByDiscordUsername ( 'batman' );
const user = await privy . getUserByTelegramUserId ( '456' );
const user = await privy . getUserByTelegramUsername ( 'batman' );
Delete a user
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).
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
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.
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
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
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.
The key quorum ID of the owner. Do not specify owner if providing this.
List of policy IDs to enforce on the wallet.
List of key quorum IDs allowed to approve transactions for the wallet.
Returns
Unique ID of the created wallet.
Address of the created wallet.
Chain type of the created wallet.
Get wallet by ID
getWallet : ({ id } : { id : string }) => Promise < WalletApiWalletResponseType > ;
Usage
const wallet = await client . walletApi . getWallet ({ id: walletId });
Parameters
The ID of the wallet to get.
Returns
wallet
WalletApiWalletResponseType
Chain type of the wallet.
List of policy IDs associated with the wallet.
The key quorum ID of the owner.
The key quorum IDs of additional signers.
The creation date of the wallet.
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)
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'
});
The Privy client accepts a base58-encoded private key. 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: 'solana' ,
entropy: '<your-base58-encoded-wallet-private-key>' ,
entropyType: 'private-key'
});
Import an HD wallet
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
});
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: 'solana' ,
entropy: '<your-bip39-mnemonic>' ,
entropyType: 'hd' ,
index: 0
});
Sign a message (Ethereum)
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
message
string | Uint8Array
required
The string or bytes to sign.
Returns
The signature produced by the wallet.
The encoding format for the signature.
Sign typed data (EIP-712)
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)
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
transaction
EthereumTransactionType
required
The transaction to sign.
Returns
The encoding format. Only 'rlp' is supported.
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
The hash to sign. Must start with 0x.
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
The smart contract address to delegate to.
The chain ID for the authorization.
The nonce for the authorization. Defaults to the current transaction count.
Send a transaction (Ethereum)
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
caip2
`eip155:${number}`
required
The CAIP2 chain ID.
transaction
EthereumTransactionType
required
The transaction to send.
Returns
Web3 integrations (Ethereum)
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)
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
message
string | Uint8Array
required
The string or bytes to sign.
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
transaction
Transaction | VersionedTransaction
required
The transaction to sign.
Returns
The encoding format. Only 'base64' is supported.
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
transaction
Transaction | VersionedTransaction
required
The transaction to sign and send.
Returns
Send SOL
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
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)
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
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
Use the PrivyClient’s getPolicy method.
const policy = await privy . getPolicy ({
id: 'fmfdj6yqly31huorjqzq38zc'
});
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
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'
});
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
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
Once you have created a wallet with a user signer, your application will:
Generate a SPKI-formatted ECDH P-256 keypair.
Request a time-bound session key from /v1/wallets/authenticate using the user’s JWT and the public key.
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'
});
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
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 of the transaction to fetch.
Pregenerate 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
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)
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
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
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
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
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
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
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
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 });