> ## 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, device signers, email, and phone.

<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.

## 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.

If you want to add restrictions to the signer (such as spending limits, recipient whitelists, or an expiry), see [Restrict a Signer with Scopes](/wallets/guides/signers/scopes).

## 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`                                    |
| Email           | Yes                       | All                  | OTP-based; can also be configured as recovery signer |
| Phone           | Yes                       | All                  | OTP-based; can also be configured as recovery signer |

### Recovery Signers

Recovery signers approve high-privilege operations (adding new signers, recovery). 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>
