Skip to main content
When the operational signer for a wallet is no longer available — you switched to a new device, cleared browser storage, lost a server secret, or a private key was compromised — you can use the wallet’s recovery signer to regain control. The recovery signer is a separate, higher-privilege signer configured at wallet creation time specifically for this scenario. This guide walks through the full recovery flow for the most common case: a device signer wallet with an email OTP recovery signer, both on the client side. The same principles apply to other signer and recovery combinations.
This guide assumes you are using Wallets SDK V1. If you are on the previous version, see the previous version docs or the V1 migration guide.

When You Need This

Recovery is needed whenever you lose access to the signer that was used for day-to-day wallet operations:
  • Switched devices — you set up a wallet on your laptop and now need to use it on a new computer or phone. The device signer from the original machine cannot be transferred.
  • Cleared browser data — you cleared cookies, local storage, or reinstalled the app, which deleted the locally stored device key.
  • Lost server signer secret — the secret used to derive your server signer was lost, rotated, or compromised.
  • Lost private key — the private key for an external wallet signer is no longer accessible.
  • Infrastructure migration — you need to move signing authority to new infrastructure.
In all of these cases, the recovery signer lets you authorize a new operational signer without needing the original one.

Prerequisites

  • A wallet that was created with a recovery signer (email, phone, or server). If your wallet was not created with a recovery signer, this guide does not apply — see Managed Support Center Recovery below for enterprise options. To configure a recovery signer, see Configure Wallet Recovery.
  • A Crossmint client-side API key with wallets.read, wallets:signatures.create, and wallets:transactions.create scopes
  • Access to the email address or phone number associated with the recovery signer (for OTP-based recovery)
If your wallet has a server signer as the recovery signer and you have also lost that secret, you cannot recover the wallet through the SDK. Contact Crossmint support to discuss enterprise recovery options.

How It Works

The most common recovery scenario is switching to a new device. Here is what happens:
  1. Wallet fetchcreateOnLogin loads the wallet when the user logs in on the new device.
  2. Detection — the SDK finds no valid local device key and automatically attempts to add a new device signer.
  3. OTP prompt — the pending signer enrollment triggers the recovery flow. For email OTP, the user receives a one-time code and enters it to authorize the new key.
  4. New key generation — the SDK generates a new P256 keypair in the device’s secure enclave (or browser credential store).
  5. Signer registration — the SDK calls addSigner() to register the new device key onchain, authorized by the recovery signer.
  6. Activation — the new device signer becomes the active signer. All subsequent transactions on this device are frictionless.
The SDK automatically attempts to add a device signer whenever one is missing. This triggers the recovery flow transparently — the user experiences a one-time OTP prompt, after which all transactions on that device sign silently.

Step 1: Recover on a New Device

When you open your app on a new device (or after clearing browser data), the device signer from the original device is not present. Use createOnLogin on CrossmintWalletProvider so the SDK fetches the wallet automatically when the user logs in — no explicit getWallet() call needed. Once the wallet is loaded, the SDK detects that no local device signer exists and automatically attempts to add one. This pending signer enrollment triggers the recovery flow, prompting the user to verify via OTP before the new device key is registered. With createOnLogin and showOtpSignerPrompt={true} (the default), recovery happens automatically:
  1. The user logs in on the new device — createOnLogin fetches the wallet
  2. The SDK detects no local device key and attempts to add a new device signer
  3. The pending signer enrollment triggers the OTP prompt — the user enters the code from their email
  4. The new device signer is registered for this device
  5. All future transactions on this device are frictionless
No additional code is needed beyond the standard provider setup.

Preemptive Recovery

To avoid interrupting the user’s first transaction with an OTP prompt, trigger recovery on app startup:
"use client";

import { useWallet } from "@crossmint/client-sdk-react-ui";
import { useEffect, useState } from "react";

function WalletRecovery() {
    const { wallet } = useWallet();
    const [recovering, setRecovering] = useState(false);

    useEffect(() => {
        const checkRecovery = async () => {
            if (wallet && wallet.needsRecovery()) {
                setRecovering(true);
                await wallet.recover();
                setRecovering(false);
            }
        };
        checkRecovery();
    }, [wallet]);

    if (recovering) {
        return <p>Verifying your identity to set up this device...</p>;
    }

    return (
        <div>
            <p>Wallet ready: {wallet?.address}</p>
        </div>
    );
}
recover() triggers the same OTP flow as automatic recovery — the only difference is timing. The user sees the OTP prompt immediately on load rather than on their first transaction.

Manual Recovery Flow

For full control over the recovery UI, disable the built-in OTP prompt and manage the flow yourself:
"use client";

import { useWallet, useWalletOtpSigner } from "@crossmint/client-sdk-react-ui";
import { useState } from "react";

function ManualRecovery() {
    const { wallet } = useWallet();
    const { needsAuth, verifyOtp, reject } = useWalletOtpSigner();
    const [otpCode, setOtpCode] = useState("");

    if (!wallet || !wallet.needsRecovery()) {
        return <p>Wallet is ready — no recovery needed.</p>;
    }

    if (!needsAuth) {
        return (
            <button onClick={() => wallet.recover()}>
                Start Recovery
            </button>
        );
    }

    return (
        <div>
            <p>Enter the verification code sent to your email:</p>
            <input
                value={otpCode}
                onChange={(e) => setOtpCode(e.target.value)}
                placeholder="Enter OTP code"
            />
            <button onClick={() => verifyOtp(otpCode)}>Verify</button>
            <button onClick={reject}>Cancel</button>
        </div>
    );
}
To use the manual flow, set showOtpSignerPrompt={false} on CrossmintWalletProvider. The useWalletOtpSigner hook gives you full control over the OTP UI.

Step 2: Choose a Recovery Action

After recovery, the new device signer is registered and the wallet resumes normal operations on the new device. In most cases, no additional action is needed — the recovery flow handles everything automatically. However, if you want to explicitly add a different type of signer or move funds, you can do so while the recovery signer is active.

Option A: Add a Different Signer Type

If you want to add a signer other than a device signer (which is handled automatically by the recovery flow), you can do so from the client side. You can also explicitly create a device signer by calling createDeviceSigner() from the useWallet hook — this is optional since the SDK handles device signer creation automatically during recovery.
Use createPasskeySigner from the useWallet hook to create a passkey signer via WebAuthn biometric prompt, then register it with addSigner:
import { useWallet } from "@crossmint/client-sdk-react-ui";

function AddPasskeySigner() {
    const { wallet, createPasskeySigner } = useWallet();

    const handleAddPasskey = async () => {
        if (!wallet) return;

        const passkey = await createPasskeySigner("my-wallet-passkey");
        const signer = await wallet.addSigner(passkey);
        console.log("Passkey added:", signer.locator);

        await wallet.useSigner({ type: "passkey" });
    };

    return (
        <button onClick={handleAddPasskey}>
            Add Passkey as New Signer
        </button>
    );
}
Passkey signers are EVM-only and require a browser that supports WebAuthn.

Option B: Transfer Funds to a Safe Wallet

If you suspect the original signer was compromised (not just lost), move assets out of the wallet immediately. Use the recovery signer to send funds directly:
import { useWallet } from "@crossmint/client-sdk-react-ui";

function EmergencyTransfer() {
    const { wallet } = useWallet();

    const handleTransfer = async () => {
        if (!wallet) return;

        const result = await wallet.send(
            "YOUR_SAFE_WALLET_ADDRESS",
            "usdc",
            "100"
        );

        console.log("Transfer complete:", result.explorerLink);
    };

    return (
        <button onClick={handleTransfer}>
            Transfer Funds to Safe Wallet
        </button>
    );
}
Check your balance first with wallet.balances() to confirm the available amount before transferring. You can also transfer native tokens and other assets — see Transfer Tokens for the full API.

Alternative: Recovery with Phone OTP

The flow is identical to email recovery — replace the email recovery signer with a phone recovery signer when creating the wallet. At recovery time, the user receives an SMS with a verification code instead of an email. Everything else — automatic recovery, preemptive recovery, manual flow — works the same way. See Configure Recovery for details on setting up phone OTP as the recovery signer.

Alternative: Server-to-Server Recovery

If your wallet uses a server signer as both the operational and recovery signer (common for AI agent wallets and backend automation), and you still have access to the recovery secret (which should be different from the operational secret), recovery is fully server-side:
import { createCrossmint, CrossmintWallets } from "@crossmint/wallets-sdk";

const crossmint = createCrossmint({
    apiKey: "YOUR_SERVER_API_KEY",
});
const crossmintWallets = CrossmintWallets.from(crossmint);

const wallet = await crossmintWallets.getWallet(
    "YOUR_WALLET_ADDRESS",
    { chain: "base-sepolia" }
);

// Activate the recovery server signer
await wallet.useSigner({
    type: "server",
    secret: process.env.RECOVERY_SIGNER_SECRET,
});

// Add a new operational server signer
const newSigner = await wallet.addSigner({
    type: "server",
    secret: process.env.NEW_OPERATIONAL_SECRET,
});

console.log("New operational signer added:", newSigner.locator);
Always use different secrets for your operational signer and recovery signer. If both use the same secret, losing it means losing access to both — making recovery impossible through the SDK.

Best Practices

  • Always configure a recovery signer at wallet creation time. Recovery signers cannot be added later via addSigner().
  • Use preemptive recovery on app startup to give users a smoother experience — they verify once on load rather than being interrupted mid-transaction.
  • Use different secrets for your operational and recovery server signers. Store them in separate secure locations.
  • After recovery, the new device signer works automatically. You do not need to manually call addSigner() for device signers — the recover() flow handles this.
  • The old device signer remains valid. Each device has its own independent device key. Recovery adds a new key; it does not revoke the old one.
  • Consider adding multiple signer types for defense in depth — for example, a device signer for daily use and a passkey as a backup.
  • For compromised signers, transfer funds to a safe wallet first, then set up new signers on the original wallet (or create a fresh wallet).

Managed Support Center

Managed support center recovery is a white-glove recovery option for enterprise customers: the wallet owner contacts a specialized support center, completes identity verification (KYC), and a trained agent facilitates the enrollment of a new operational signer. This is the most robust recovery option available and is best reserved as a last resort for situations where all other recovery methods have been exhausted.
Managed support center recovery requires an enterprise agreement. Contact Crossmint to set up managed recovery for your project.

See Also

Recovery Concepts

How recovery works — detection, authentication, and signer registration

Configure Recovery

Set up email, phone, or server recovery at wallet creation time

Device Signer

Understand how the default client-side signer works

Add Signers

Add passkeys, external wallets, or device signers to a wallet