> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crossmint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Signers to a Wallet

> Add signers to an existing wallet — passkeys, external wallets, and device signers.

<Note>
  **This page has been updated for Wallets SDK V1.** If you are using the previous version,
  see the [previous version docs](/wallets/v0/overview) or the [V1 migration guide](/wallets/guides/migrate-to-v1).
</Note>

Use `addSigner()` to register additional signers on an existing wallet. The wallet's recovery signer must approve the operation — the SDK handles this automatically.

<Warning>
  Email and phone signers **cannot** be added via `addSigner()`. They can only be configured as recovery signers at wallet creation time. See [Configure Wallet Recovery](/wallets/guides/signers/configure-recovery).
</Warning>

## Prerequisites

* An existing wallet with a recovery signer configured
* **API key** with `wallets:signatures.create` and `wallets:transactions.create` scopes

## How It Works

When you call `addSigner()`, the SDK:

1. Submits the new signer registration request
2. Temporarily activates the recovery signer to approve the operation
3. After approval, restores the previously active signer

For client-side wallets with an email or phone recovery signer, the user is prompted with an OTP to approve adding the new signer. For server signer recovery, approval happens automatically.

## Add a Passkey Signer

Passkey signers use the WebAuthn/FIDO2 standard for biometric authentication. The user registers a passkey via a browser or platform authenticator prompt.

<Note>
  Passkey signers are **EVM-only**. They are not supported on Solana or Stellar.
</Note>

<Tabs>
  <Tab title="React">
    ```typescript theme={null}
    import { useWallet } from "@crossmint/client-sdk-react-ui";

    const { wallet } = useWallet();

    const signer = await wallet.addSigner({ type: "passkey" });

    console.log("Passkey added:", signer.locator);
    ```

    After adding, activate the passkey for signing:

    ```typescript theme={null}
    wallet.useSigner({ type: "passkey" });

    const { hash } = await wallet.send(
        "0xRecipientAddress",
        "usdc",
        "10"
    );
    ```
  </Tab>

  <Tab title="Flutter">
    ```dart theme={null}
    import 'package:crossmint_flutter/crossmint_flutter_ui.dart';

    final controller = CrossmintWalletContext.of(context).requireWalletController;
    final wallet = controller.createEvmWallet();

    // The SDK has no built-in passkey implementation — bridge to the passkey
    // plugin of your choice (for example `passkeys` or a native platform
    // channel). The SDK invokes your callback whenever a signature is needed.
    final passkeyConfig = CrossmintPasskeySignerConfig(
      name: 'My device passkey',
      onCreatePasskey: (name) async {
        // Register a new passkey credential with your plugin and return it
        // as a `CrossmintPasskeyCredential`. Called once when the signer is
        // registered.
        return await YourPasskeyPlugin.create(name);
      },
      onSignWithPasskey: (message) async {
        // Sign `message` with the passkey and return the result as a
        // `CrossmintPasskeySignResult`. Called on every signing operation.
        return await YourPasskeyPlugin.sign(message);
      },
    );
    final result = await wallet.addSigner(passkeyConfig);
    print('Passkey added: ${result.signer.locator}');
    ```

    Passkey signers require `onCreatePasskey` and `onSignWithPasskey` callbacks
    that bridge to your native passkey plugin. See the
    [Flutter SDK reference](/sdk-reference/wallets/flutter/controllers#wallet-methods)
    for the `CrossmintPasskeySignerConfig` API. (The EVM-only restriction above
    applies to all SDKs — passkey signers rely on WebAuthn, which is not
    supported on Solana or Stellar.)
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    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(
        "<wallet-address>",
        { chain: "base-sepolia" }
    );

    await wallet.useSigner({
        type: "server",
        secret: process.env.CROSSMINT_SIGNER_SECRET,
    });

    const signer = await wallet.addSigner({ type: "passkey" });

    console.log("Passkey added:", signer.locator);
    ```
  </Tab>
</Tabs>

## Add an External Wallet Signer

An external wallet signer connects an existing blockchain wallet or keypair — such as MetaMask, Phantom, or a raw keypair — as a signer. When adding an external wallet via `addSigner()`, only the `address` is needed (no `onSign` callback required for registration).

<Tabs>
  <Tab title="React">
    ```typescript theme={null}
    import { useWallet } from "@crossmint/client-sdk-react-ui";

    const { wallet } = useWallet();

    const signer = await wallet.addSigner({
        type: "external-wallet",
        address: "0x1234...abcd",
    });

    console.log("External wallet added:", signer.locator);
    ```

    To use the external wallet for signing, activate it with the full config including `onSign`:

    ```typescript theme={null}
    wallet.useSigner({
        type: "external-wallet",
        address: "0x1234...abcd",
        onSign: async (payload) => {
            // Sign the hex payload with your external wallet
            return await externalWallet.signMessage(payload);
        },
    });
    ```
  </Tab>

  <Tab title="Flutter">
    ```dart theme={null}
    import 'package:crossmint_flutter/crossmint_flutter_ui.dart';

    final controller = CrossmintWalletContext.of(context).requireWalletController;
    final wallet = controller.createEvmWallet();

    final result = await wallet.addSigner(
      const CrossmintExternalWalletSignerConfig(address: '0x1234...abcd'),
    );
    print('External wallet added: ${result.signer.locator}');
    ```

    To use the external wallet for signing, wrap it with a sign callback via
    `controller.createEvmWalletWithExternalWalletSigner(...)`.
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    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(
        "<wallet-address>",
        { chain: "base-sepolia" }
    );

    await wallet.useSigner({
        type: "server",
        secret: process.env.CROSSMINT_SIGNER_SECRET,
    });

    const signer = await wallet.addSigner({
        type: "external-wallet",
        address: "0x1234...abcd",
    });

    console.log("External wallet added:", signer.locator);
    ```
  </Tab>

  <Tab title="REST">
    <Steps>
      <Step title="Register the signer">
        Call the register signer endpoint with the external wallet address.

        <CodeGroup>
          ```bash cURL theme={null}
          curl --request POST \
              --url https://staging.crossmint.com/api/2025-06-09/wallets/<walletAddress>/signers \
              --header 'Content-Type: application/json' \
              --header 'X-API-KEY: <x-api-key>' \
              --data '{
                  "chain": "base-sepolia",
                  "signer": "external-wallet:0x1234...abcd"
              }'
          ```

          ```js Node.js theme={null}
          const url =
              "https://staging.crossmint.com/api/2025-06-09/wallets/<walletAddress>/signers";

          const payload = {
              chain: "base-sepolia",
              signer: "external-wallet:0x1234...abcd",
          };

          const response = await fetch(url, {
              method: "POST",
              headers: {
                  "X-API-KEY": "<x-api-key>",
                  "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
          });

          const data = await response.json();
          console.log(data);
          ```

          ```python Python theme={null}
          import requests

          url = "https://staging.crossmint.com/api/2025-06-09/wallets/<walletAddress>/signers"

          payload = {
              "chain": "base-sepolia",
              "signer": "external-wallet:0x1234...abcd"
          }
          headers = {
              "X-API-KEY": "<x-api-key>",
              "Content-Type": "application/json"
          }

          response = requests.post(
              url, json=payload, headers=headers
          )
          print(response.json())
          ```
        </CodeGroup>

        See the [API reference](/api-reference/wallets/register-delegated-key) for more details.
      </Step>

      <Step title="Approve the registration">
        Sign the approval message returned in the response using your recovery signer, then submit the approval. For EVM wallets, use the [approve signature](/api-reference/wallets/approve-signature) endpoint. For Solana and Stellar, use the [approve transaction](/api-reference/wallets/approve-transaction) endpoint.
      </Step>
    </Steps>
  </Tab>
</Tabs>

### External Wallet `onSign` by Chain

When using an external wallet signer for transactions (via `useSigner`), the `onSign` callback receives chain-specific payloads:

| Chain   | `onSign` Signature                                            | Payload              | Expected Return       |
| ------- | ------------------------------------------------------------- | -------------------- | --------------------- |
| EVM     | `(payload: string) => Promise<string>`                        | Hex-encoded message  | Hex-encoded signature |
| Solana  | `(tx: VersionedTransaction) => Promise<VersionedTransaction>` | Unsigned transaction | Signed transaction    |
| Stellar | `(payload: string) => Promise<string>`                        | String payload       | Signed string         |

## Add a Device Signer on a New Device

When a user accesses their wallet from a new device, the SDK automatically handles device signer creation through the [recovery flow](/wallets/guides/signers/device-signer#new-device-recovery). You do not need to call `addSigner()` manually for this — it happens as part of the recovery process.

If you want to trigger recovery before the first transaction:

<Tabs>
  <Tab title="React">
    ```typescript theme={null}
    import { useWallet } from "@crossmint/client-sdk-react-ui";

    const { wallet } = useWallet();

    if (wallet.needsRecovery()) {
        await wallet.recover();
    }
    ```
  </Tab>

  <Tab title="React Native">
    ```typescript theme={null}
    import { useWallet } from "@crossmint/client-sdk-react-native-ui";

    const { wallet } = useWallet();

    if (wallet.needsRecovery()) {
        await wallet.recover();
    }
    ```
  </Tab>

  <Tab title="Flutter">
    ```dart theme={null}
    import 'package:crossmint_flutter/crossmint_flutter_ui.dart';

    final controller = CrossmintWalletContext.of(context).requireWalletController;
    final wallet = controller.createEvmWallet();

    if (wallet.needsRecovery) {
      await wallet.recover();
    }
    ```
  </Tab>
</Tabs>

## List Signers

To list all signers registered on a wallet, see [List Wallet Signers](/wallets/guides/signers/list-signers).

## Signer Type Reference

### Operational Signers

These signers are used for day-to-day transactions and can be listed with [`wallet.signers()`](/wallets/guides/signers/list-signers).

| Signer Type     | Can Add via `addSigner()` | Chains               | Notes                                                |
| --------------- | ------------------------- | -------------------- | ---------------------------------------------------- |
| Passkey         | Yes                       | EVM only             | Biometric WebAuthn prompt                            |
| External wallet | Yes                       | EVM, Solana, Stellar | Requires `address`; `onSign` needed for transactions |
| Device          | Automatic                 | EVM, Stellar         | Created during recovery flow on new devices          |
| Server          | Yes                       | EVM, Solana, Stellar | Requires `secret`                                    |

### Recovery Signers

Recovery signers approve high-privilege operations (adding new signers, recovery). They cannot be added via `addSigner()` — they are configured at wallet creation time. Access the recovery signer via `wallet.recovery`. See [Configure Wallet Recovery](/wallets/guides/signers/configure-recovery).

| Signer Type | Chains | Notes              |
| ----------- | ------ | ------------------ |
| Email       | All    | OTP-based approval |
| Phone       | All    | OTP-based approval |

## Next Steps

<CardGroup cols={3}>
  <Card title="Device Signer" icon="microchip" href="/wallets/guides/signers/device-signer">
    Understand the default client-side signer
  </Card>

  <Card title="Configure Recovery" icon="shield-halved" href="/wallets/guides/signers/configure-recovery">
    Set up recovery signers for your wallets
  </Card>

  <Card title="Transfer Tokens" icon="arrow-right-arrow-left" href="/wallets/guides/transfer-tokens">
    Send tokens from your wallet
  </Card>
</CardGroup>
