A recovery signer lets users regain access to their wallet when their primary (device) signer is no longer available — for example, when they switch to a new phone or clear their browser data. The recovery signer authorizes the enrollment of a new device signer on the new device.
Recovery signers are configured at wallet creation time . You can choose from email OTP, SMS OTP, or a server signer depending on your application’s needs.
Recovery signers can also sign transactions when needed — they are not limited to recovery only. However, they involve higher friction (OTP verification or server-side signing), so they are best reserved for recovery flows and as a fallback.
Prerequisites
A Crossmint API key with wallets.create scope
For email OTP: the user’s email address
For SMS OTP: the user’s phone number in E.164 format (e.g., +1234567890)
For server signer: a signer secret stored on your server
Email OTP Recovery
The user verifies ownership of their email address via a one-time password sent by Crossmint. This is the most common recovery method for consumer applications.
React
Node.js
React Native
REST
Using createOnLogin on the provider (recommended): import {
CrossmintProvider ,
CrossmintAuthProvider ,
CrossmintWalletProvider ,
} from "@crossmint/client-sdk-react-ui" ;
function App ({ children }) {
return (
< CrossmintProvider apiKey = "YOUR_CLIENT_API_KEY" >
< CrossmintAuthProvider
loginMethods = { [ "email" , "google" ]}
>
<CrossmintWalletProvider
createOnLogin={{
chain: "base-sepolia" ,
recovery: { type: "email" },
}}
>
{ children }
</ CrossmintWalletProvider >
</ CrossmintAuthProvider >
</ CrossmintProvider >
);
}
Or using createWallet directly: import { useWallet } from "@crossmint/client-sdk-react-ui" ;
const { createWallet } = useWallet ();
const wallet = await createWallet ({
chain: "base-sepolia" ,
recovery: {
type: "email" ,
email: "user@example.com" ,
},
});
import {
createCrossmint ,
CrossmintWallets ,
} from "@crossmint/wallets-sdk" ;
const crossmint = createCrossmint ({
apiKey: "YOUR_SERVER_API_KEY" ,
});
const crossmintWallets = CrossmintWallets . from ( crossmint );
const wallet = await crossmintWallets . createWallet ({
chain: "base-sepolia" ,
recovery: {
type: "email" ,
email: "user@example.com" ,
},
});
import {
CrossmintProvider ,
CrossmintAuthProvider ,
CrossmintWalletProvider ,
} from "@crossmint/client-sdk-react-native-ui" ;
function App ({ children }) {
return (
< CrossmintProvider apiKey = "YOUR_CLIENT_API_KEY" >
< CrossmintAuthProvider
loginMethods = { [ "email" , "google" ]}
>
<CrossmintWalletProvider
createOnLogin={{
chain: "base-sepolia" ,
recovery: { type: "email" },
}}
>
{ children }
</ CrossmintWalletProvider >
</ CrossmintAuthProvider >
</ CrossmintProvider >
);
}
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '{
"type": "evm-smart-wallet",
"config": {
"adminSigner": {
"type": "email",
"email": "user@example.com"
}
}
}'
The REST API uses the legacy field name adminSigner for the recovery signer. The SDK uses recovery.
SMS OTP Recovery
The user verifies ownership of their phone number via a one-time password delivered by SMS (or optionally WhatsApp). This is ideal for mobile-first applications.
React
Node.js
React Native
REST
Using createOnLogin on the provider: import {
CrossmintProvider ,
CrossmintAuthProvider ,
CrossmintWalletProvider ,
} from "@crossmint/client-sdk-react-ui" ;
function App ({ children }) {
return (
< CrossmintProvider apiKey = "YOUR_CLIENT_API_KEY" >
< CrossmintAuthProvider
loginMethods = { [ "email" , "google" ]}
>
<CrossmintWalletProvider
createOnLogin={{
chain: "base-sepolia" ,
recovery: {
type: "phone" ,
phone: "+1234567890" ,
},
}}
>
{ children }
</ CrossmintWalletProvider >
</ CrossmintAuthProvider >
</ CrossmintProvider >
);
}
Or using createWallet directly: import { useWallet } from "@crossmint/client-sdk-react-ui" ;
const { createWallet } = useWallet ();
const wallet = await createWallet ({
chain: "base-sepolia" ,
recovery: {
type: "phone" ,
phone: "+1234567890" ,
},
});
import {
createCrossmint ,
CrossmintWallets ,
} from "@crossmint/wallets-sdk" ;
const crossmint = createCrossmint ({
apiKey: "YOUR_SERVER_API_KEY" ,
});
const crossmintWallets = CrossmintWallets . from ( crossmint );
const wallet = await crossmintWallets . createWallet ({
chain: "base-sepolia" ,
recovery: {
type: "phone" ,
phone: "+1234567890" ,
},
});
import {
CrossmintProvider ,
CrossmintAuthProvider ,
CrossmintWalletProvider ,
} from "@crossmint/client-sdk-react-native-ui" ;
function App ({ children }) {
return (
< CrossmintProvider apiKey = "YOUR_CLIENT_API_KEY" >
< CrossmintAuthProvider
loginMethods = { [ "email" , "google" ]}
>
<CrossmintWalletProvider
createOnLogin={{
chain: "base-sepolia" ,
recovery: {
type: "phone" ,
phone: "+1234567890" ,
},
}}
>
{ children }
</ CrossmintWalletProvider >
</ CrossmintAuthProvider >
</ CrossmintProvider >
);
}
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '{
"type": "evm-smart-wallet",
"config": {
"adminSigner": {
"type": "phone",
"phone": "+1234567890"
}
}
}'
Server Signer as Recovery
Use a server signer as the recovery signer when you want your backend to manage recovery without user-facing OTP flows. This is common for AI agents, backend automation, and hybrid architectures where the server creates wallets that clients later use.
import {
createCrossmint ,
CrossmintWallets ,
} from "@crossmint/wallets-sdk" ;
const crossmint = createCrossmint ({
apiKey: "YOUR_SERVER_API_KEY" ,
});
const crossmintWallets = CrossmintWallets . from ( crossmint );
const wallet = await crossmintWallets . createWallet ({
chain: "base-sepolia" ,
recovery: {
type: "server" ,
secret: process . env . CROSSMINT_SIGNER_SECRET ,
},
});
For more on generating and managing server signer secrets, see the Server Signer guide .
How Recovery Works on a New Device
When a user accesses their wallet from a new device where no device signer exists:
The user authenticates via your app — the SDK retrieves the wallet
The SDK detects no local device signer on this device
On the first transaction (or when recover() is called), the SDK triggers the recovery flow:
Email OTP : Crossmint sends a one-time code to the user’s email
SMS OTP : Crossmint sends a one-time code to the user’s phone
Server signer : your backend signs the recovery approval automatically
The recovery signer authorizes a new device signer for this device
All subsequent transactions on the new device are frictionless
import { useWallet } from "@crossmint/client-sdk-react-ui" ;
const { wallet } = useWallet ();
// Check if recovery is needed on this device
if ( wallet . needsRecovery ()) {
// Trigger recovery proactively (optional)
await wallet . recover ();
}
The previous device’s signer remains valid. Each device maintains its own independent device signer.
Choosing a Recovery Method
Method Best For User Friction Server Required Email OTP Consumer apps with email-based auth Medium (OTP prompt) No SMS OTP Mobile-first apps Medium (OTP prompt) No Server signer AI agents, backend automation, hybrid apps None (automatic) Yes
Email and phone recovery signers can only be configured at wallet creation time. They cannot be added later via addSigner(). Plan your recovery strategy before creating wallets.
Next Steps
Device Signer Understand how the default client-side signer works
Server Signer Set up server-side signing with key derivation
Add Signers Add passkeys or external wallets to an existing wallet