- React
- React Native
- Android
- Swift
- Flutter
The React SDK supports linking all supported account types via our modal-guided link methods.
To prompt a user to link an account, use the respective method from the

Below is an example button for prompting a user to link an email to their account:
useLinkAccount hook:| Method | Description | User Experience |
|---|---|---|
linkEmail | Links email address | Opens modal |
linkPhone | Links phone number | Opens modal |
linkWallet | Links external wallet | Opens modal |
linkGoogle | Links Google account | Direct redirect |
linkApple | Links Apple account | Direct redirect |
linkTwitter | Links Twitter account | Direct redirect |
linkDiscord | Links Discord account | Direct redirect |
linkGithub | Links Github account | Direct redirect |
linkLinkedIn | Links LinkedIn account | Direct redirect |
linkTikTok | Links TikTok account | Direct redirect |
linkSpotify | Links Spotify account | Direct redirect |
linkInstagram | Links Instagram account | Direct redirect |
linkTelegram | Links Telegram account | Direct redirect |
linkFarcaster | Links Farcaster account | Displays QR code |
linkPasskey | Links passkey | Opens modal |
Users are only permitted to link a single account for a given account type, except for wallets and passkeys. Concretely, a user may link at most one email address, but can link as many wallets and passkeys as they’d like.

Report incorrect code
Copy
Ask AI
import {useLinkAccount} from '@privy-io/react-auth';
function LinkOptions() {
const {linkEmail, linkGoogle, linkWallet} = useLinkAccount();
return (
<div className="link-options">
<button onClick={linkEmail}>Link Email to user</button>
<button onClick={linkGoogle}>Link Google account to user</button>
<button onClick={linkWallet}>Link Wallet to user</button>
</div>
);
}
Callbacks
You can optionally register anonSuccess or onError callback on the useLinkAccount hook.Report incorrect code
Copy
Ask AI
const {linkGoogle} = useLinkAccount({
onSuccess: ({user, linkMethod, linkedAccount}) => {
console.log('Linked account to user ', linkedAccount);
},
onError: (error) => {
console.error('Failed to link account with error ', error)
}
})
Optional callback to run after a user successfully links an account.
Optional callback to run after there is an error during account linkage.
Looking for whitelabel
link methods? Our useLoginWith<AccountType> hooks allow will link an account to a user, provided that the user is already logged in whenever the authentication flow is completed. For headless wallet linking with useLinkWithSiwe or useLinkWithSiws, see the whitelabel user management documentation.To prompt a user to link an account, use the respective hooks:
The steps to implementing the link flows are analogous to the login hooks
| Account type | Description | Hook to invoke |
|---|---|---|
Email | Links email address | useLinkEmail |
Phone | Links phone number | useLinkSms |
Wallet | Links external wallet | useLinkWithSiwe, useLinkWithSiws |
Google | Links Google account | useLinkWithOAuth |
Apple | Links Apple account | useLinkWithOAuth |
Twitter | Links Twitter account | useLinkWithOAuth |
Discord | Links Discord account | useLinkWithOAuth |
Github | Links Github account | useLinkWithOAuth |
LinkedIn | Links LinkedIn account | useLinkWithOAuth |
TikTok | Links TikTok account | useLinkWithOAuth |
Spotify | Links Spotify account | useLinkWithOAuth |
Instagram | Links Instagram account | useLinkWithOAuth |
Farcaster | Links Farcaster account | useLinkWithFarcaster |
Passkey | Links passkey | useLinkPasskey from @privy-io/expo/passkey |
useLoginWith<AccountType.Users are only permitted to link a single account for a given account type, except for wallets and passkeys. Concretely, a user may link at most one email address, but can link as many wallets and passkeys as they’d like.
- Email/Phone
- External Wallets
- Passkeys
The steps to implementing the
linkWithCode flow are analogous to the loginWithCode flow. The same thing applies for the PhoneAccount type, using privy.sms.linkWithCode similar to how privy.sms.loginWithCode works.First, prompt the user for their email address and use the Privy client’s privy.email.sendCode method to send them a one-time passcode:Report incorrect code
Copy
Ask AI
sendCode(email: String): Result<Unit>
Link With Code
Then, prompt the user for the code they received and use thelinkWithCode method:Report incorrect code
Copy
Ask AI
linkWithCode(code: String, email: String? = null): Result<PrivyUser>
The one-time passcode sent to the user’s email address.
(Optional) The user’s email address. Though this parameter is optional, it is highly recommended that you pass the user’s email address explicitly. If email is omitted, the email from
sendCode will be used.Returns
A result type encapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
// Send code to user's email
val sendResult: Result<Unit> = privy.email.sendCode(email = "[email protected]")
sendResult.fold(
onSuccess = {
// OTP was successfully sent, now prompt user for code
},
onFailure = {
println("Error sending OTP: ${it.message}")
}
)
// Link the email using the code
val linkResult: Result<PrivyUser> = privy.email.linkWithCode(code = "123456", email = "[email protected]")
linkResult.fold(
onSuccess = { updatedUser ->
println("Email successfully linked!")
// updatedUser contains the updated user object with the email added
},
onFailure = {
println("Error linking email: ${it.message}")
}
)
The steps to implementing the
link flow are analogous to the login flow’s of both Sign in with Ethereum (SIWE) and Sign in with Solana (SIWS).- Ethereum Wallet
- Solana Wallet
To link an Ethereum wallet, use the Privy client’s
siwe handler.Generate SIWE message
Report incorrect code
Copy
Ask AI
public suspend fun generateMessage(params: SiweMessageParams): Result<String>
Parameters
Set of parameters required to generate the message.
Returns
A result type encapsulating the SIWE message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiweMessageParams(
appDomain = domain,
appUri = uri,
chainId = chainId,
walletAddress = walletAddress
)
privy.siwe.generateSiweMessage(params = params).fold(
onSuccess = { message ->
// request an EIP-191 `personal_sign` signature on the message
},
onFailure = { e ->
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
}
)
Sign the SIWE message
Using the message returned bygenerateMessage, request an EIP-191 personal_sign signature from the user’s connected wallet. You should do this using the library your app uses to connect to external wallets (e.g. the MetaMask SDK or WalletConnect). Once the user successfully signs the message, pass the signature into the link function.Link with SIWE
Report incorrect code
Copy
Ask AI
public suspend fun link(
message: String,
signature: String,
params: SiweMessageParams,
metadata: WalletLoginMetadata?,
): Result<PrivyUser>
Parameters
The message returned from “generateMessage”.
The signature of the SIWE message, signed by the user’s wallet.
The same SiweMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
A result type encapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiweMessageParams(
appDomain = domain,
appUri = uri,
chainId = chainId,
walletAddress = walletAddress
)
// optional metadata
val metadata = WalletLoginMetadata(walletClientType = walletClient, connectorType = connectorType)
privy.siwe.link(message, signature, params, metadata).fold(
onSuccess = { updatedUser ->
// Link success
// updatedUser contains the updated user object with the wallet added
},
onFailure = { e ->
// Link failure, either due to invalid signature or network error
}
)
To link a Solana wallet, use the Privy client’s
siws handler.Generate SIWS message
Report incorrect code
Copy
Ask AI
public suspend fun generateMessage(params: SiwsMessageParams): Result<String>
Parameters
Set of parameters required to generate the message.
Hide child attributes
Hide child attributes
Your app’s domain. e.g. “my-domain.com”
Your app’s URI. e.g. “https://my-domain.com”
The user’s Solana wallet address.
Returns
A result type encapsulating the SIWS message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiwsMessageParams(
appDomain = domain,
appUri = uri,
walletAddress = walletAddress
)
privy.siws.generateMessage(params = params).fold(
onSuccess = { message ->
// request a Solana wallet signature on the message
},
onFailure = { e ->
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
}
)
Sign the SIWS message
Using the message returned bygenerateMessage, request a signature from the user’s connected Solana wallet. You should do this using the library your app uses to connect to external wallets (e.g. the Solana Mobile SDK or Phantom). Once the user successfully signs the message, pass the signature into the link function.Link with SIWS
Report incorrect code
Copy
Ask AI
public suspend fun link(
message: String,
signature: String,
params: SiwsMessageParams,
metadata: WalletLoginMetadata?,
): Result<PrivyUser>
Parameters
The message returned from “generateMessage”.
The signature of the SIWS message, signed by the user’s wallet.
The same SiwsMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
A result type encapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
val params = SiwsMessageParams(
appDomain = domain,
appUri = uri,
walletAddress = walletAddress
)
// optional metadata
val metadata = WalletLoginMetadata(
walletClientType = walletClient,
connectorType = connectorType
)
privy.siws.link(message, signature, params, metadata).fold(
onSuccess = { updatedUser ->
// Link success
// updatedUser contains the updated user object with the wallet added
},
onFailure = { e ->
// Link failure, either due to invalid signature or network error
}
)
Use the following method from the
passkey handler to link a new passkey to a user. Be sure to follow Android passkey setup instructions here.Report incorrect code
Copy
Ask AI
suspend fun link(
relyingParty: String,
displayName: String? = null
): Result<PrivyUser>
Parameters
The URL origin where your Digital Asset Links are available (e.g.,
https://example.com).An optional display name to associate with the passkey. This name will be shown to the user when selecting which passkey to use for authentication.
Returns
A result type encapsulating the PrivyUser on success.
Usage
Report incorrect code
Copy
Ask AI
val relyingParty = "https://yourdomain.com"
val displayName = "Optional Display Name" // Optional parameter
privy.passkey.link(
relyingParty = relyingParty,
displayName = displayName
).fold(
onSuccess = { updatedUser ->
// Successfully linked a passkey to an existing user
// updatedUser contains the updated user object with the passkey added
println("Passkey linked successfully!")
},
onFailure = { error ->
// Handle error
println("Link failed: ${error.message}")
}
)
- Email/Phone
- Solana Wallet
- Passkeys
The steps to implementing the
linkWithCode flow are analogous to the loginWithCode flow.
The same thing applies for the PhoneNumberAccount type, using privy.sms.linkWithCode similar to how privy.sms.loginWithCode works.First, prompt the user for their email address and use the Privy client’s privy.email.sendCode method to send them a one-time passcode:Report incorrect code
Copy
Ask AI
sendCode(to email: String) async throws
Link With Code
Then, prompt the user for the code they received and use thelinkWithCode method:Report incorrect code
Copy
Ask AI
linkWithCode(_ code: String, sentTo email: String) async throws
The one-time passcode sent to the user’s email address.
The user’s email address.
Returns
Nothing, indicating success.Throws
An error if linking the account is unsuccessful.Usage
Report incorrect code
Copy
Ask AI
// Send code to user's email
do {
try await privy.email.sendCode(to: "[email protected]")
// successfully sent code to users email
} catch {
print("error sending code to \(email): \(error)")
}
// Link the email using the code
do {
try await privy.email.linkWithCode("123456", sentTo: "[email protected]")
print("Email successfully linked!")
// user has linked their email!
} catch {
print("error linking email: \(error)")
}
Use the following method from the
siws handler to link a Solana wallet:Report incorrect code
Copy
Ask AI
func link(address: String) async throws
Usage
Report incorrect code
Copy
Ask AI
do {
// generate SIWS message
let message = try await privy.siws.generateMessage(params: params)
// request a signature from the user's wallet using your
// preferred library or wallet provider
let signature = try await signWithWallet(message)
// link the Solana wallet providing the message and signature
try await privy.siws.link(message: message, signature: signature)
// successfully linked Solana wallet
} catch {
// error linking Solana wallet
}
Use the following method from the
passkey handler to link a new passkey to a user. Be sure to follow iOS passkey setup instructions here.Report incorrect code
Copy
Ask AI
func link(relyingParty: String, displayName: String?) async throws
Usage
Report incorrect code
Copy
Ask AI
do {
let displayName = "Optional Display Name"
try await privy.passkey.link(
relyingParty: relyingParty,
displayName: displayName
)
// Successfully linked a passkey to an existing user
} catch {
print("Signup failed: \(error.localizedDescription)")
}
- Email/Phone
- External Wallets
- Passkeys
The steps to implementing the
linkWithCode flow are analogous to the loginWithCode flow. The same thing applies for phone accounts, using privy.sms.linkWithCode similar to how privy.sms.loginWithCode works.First, prompt the user for their email address and use the Privy client’s privy.email.sendCode method to send them a one-time passcode:Report incorrect code
Copy
Ask AI
Future<Result<void>> sendCode(String email)
Link With Code
Then, prompt the user for the code they received and use thelinkWithCode method:Report incorrect code
Copy
Ask AI
Future<Result<void>> linkWithCode({
required String code,
required String email,
})
The one-time passcode sent to the user’s email address.
The user’s email address.
Returns
A Result object that indicates whether the operation was successful. Returns
Success if the email account was linked successfully, or Failure if there was an error.Usage
Report incorrect code
Copy
Ask AI
// Send code to user's email
final sendResult = await privy.email.sendCode("[email protected]");
sendResult.fold(
onSuccess: (_) {
// OTP was successfully sent, now prompt user for code
},
onFailure: (error) {
print("Error sending OTP: ${error.message}");
},
);
// Link the email using the code
final linkResult = await privy.email.linkWithCode(
code: "123456",
email: "[email protected]",
);
linkResult.fold(
onSuccess: (_) {
print("Email successfully linked!");
// User linked email
},
onFailure: (error) {
print("Error linking email: ${error.message}");
},
);
The steps to implementing the
link flow are analogous to the login flows of both Sign in with Ethereum (SIWE) and Sign in with Solana (SIWS).- Ethereum Wallet
- Solana Wallet
To link an Ethereum wallet, use the Privy client’s
siwe handler.Generate SIWE message
Report incorrect code
Copy
Ask AI
Future<Result<String>> generateMessage(SiweMessageParams params)
Parameters
Set of parameters required to generate the message.
Returns
A result type encapsulating the SIWE message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
final params = SiweMessageParams(
appDomain: domain,
appUri: uri,
chainId: chainId,
walletAddress: walletAddress,
);
final result = await privy.siwe.generateMessage(params);
result.fold(
onSuccess: (message) {
// request an EIP-191 `personal_sign` signature on the message
},
onFailure: (error) {
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
},
);
Sign the SIWE message
Using the message returned bygenerateMessage, request an EIP-191 personal_sign signature from the user’s connected wallet. You should do this using the library your app uses to connect to external wallets (e.g. the MetaMask SDK or WalletConnect). Once the user successfully signs the message, pass the signature into the link function.Link with SIWE
Report incorrect code
Copy
Ask AI
Future<Result<void>> link({
required String message,
required String signature,
required SiweMessageParams params,
WalletLoginMetadata? metadata,
})
Parameters
The message returned from “generateMessage”.
The signature of the SIWE message, signed by the user’s wallet.
The same SiweMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
A result type indicating success or failure of the link operation.
Usage
Report incorrect code
Copy
Ask AI
final params = SiweMessageParams(
appDomain: domain,
appUri: uri,
chainId: chainId,
walletAddress: walletAddress,
);
// optional metadata
final metadata = WalletLoginMetadata(
walletClientType: walletClient,
connectorType: connectorType,
);
final result = await privy.siwe.link(
message: message,
signature: signature,
params: params,
metadata: metadata,
);
result.fold(
onSuccess: (_) {
// Link success
},
onFailure: (error) {
// Link failure, either due to invalid signature or network error
},
);
To link a Solana wallet, use the Privy client’s
siws handler.Generate SIWS message
Report incorrect code
Copy
Ask AI
Future<Result<String>> generateMessage(SiwsMessageParams params)
Parameters
Set of parameters required to generate the message.
Hide child attributes
Hide child attributes
Your app’s domain. e.g. “my-domain.com”
Your app’s URI. e.g. “https://my-domain.com”
The user’s Solana wallet address.
Returns
A result type encapsulating the SIWS message that can be signed by the wallet on success.
Usage
Report incorrect code
Copy
Ask AI
final params = SiwsMessageParams(
appDomain: domain,
appUri: uri,
walletAddress: walletAddress,
);
final result = await privy.siws.generateMessage(params);
result.fold(
onSuccess: (message) {
// request a signature on the message from the Solana wallet
},
onFailure: (error) {
// An error can be thrown if the network call to generate the message fails,
// or if invalid metadata was passed in.
},
);
Sign the SIWS message
Using the message returned bygenerateMessage, request a signature from the user’s connected Solana wallet. You should do this using the library your app uses to connect to Solana wallets (e.g. the Phantom SDK or Solana Mobile Wallet Adapter). Once the user successfully signs the message, pass the signature into the link function.Link with SIWS
Report incorrect code
Copy
Ask AI
Future<Result<void>> link({
required String message,
required String signature,
required SiwsMessageParams params,
WalletLoginMetadata? metadata,
})
Parameters
The message returned from “generateMessage”.
The signature of the SIWS message, signed by the user’s Solana wallet.
The same SiwsMessageParams passed into “generateMessage”.
(Optional) you can pass additional metadata that will be stored with the linked wallet.
Returns
A result type indicating success or failure of the link operation.
Usage
Report incorrect code
Copy
Ask AI
final params = SiwsMessageParams(
appDomain: domain,
appUri: uri,
walletAddress: walletAddress,
);
// optional metadata
final metadata = WalletLoginMetadata(
walletClientType: walletClient,
connectorType: connectorType,
);
final result = await privy.siws.link(
message: message,
signature: signature,
params: params,
metadata: metadata,
);
result.fold(
onSuccess: (_) {
// Link success
},
onFailure: (error) {
// Link failure, either due to invalid signature or network error
},
);
Use the following method from the
passkey handler to link a new passkey to a user. Be sure to follow the Android and iOS setup instructions.Report incorrect code
Copy
Ask AI
Future<Result<void>> link({
required String relyingParty,
String? displayName,
})
Parameters
The URL origin where your Digital Asset Links are available (e.g.,
https://example.com).An optional display name to associate with the passkey. This name will be shown to the user when selecting which passkey to use for authentication.
Returns
A Result object that indicates whether the operation was successful. Returns
Success if the passkey was linked successfully, or Failure if there was an error.Usage
Report incorrect code
Copy
Ask AI
final relyingParty = "https://yourdomain.com";
final displayName = "Optional Display Name"; // Optional parameter
final result = await privy.passkey.link(
relyingParty: relyingParty,
displayName: displayName,
);
result.fold(
onSuccess: (_) {
// Successfully linked a passkey to an existing user
print("Passkey linked successfully!");
},
onFailure: (error) {
// Handle error
print("Link failed: ${error.message}");
},
);

