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

### Installation Timing

A signer must be installed onchain before it can sign. You can control when this happens. Set the `deployImmediately` option on `addSigner()`:

* Set `deployImmediately: true` to install the signer immediately. The recovery signer approves an onchain registration transaction. The signer is ready to sign after that transaction confirms.
* Set `deployImmediately: false` to install the signer later. Registration records a signature request. The signer is installed on its first transaction.

```typescript theme={null}
const signer = await wallet.addSigner(
    { type: "external-wallet", address: "0x1234...abcd" },
    { deployImmediately: false }
);
```

When you use `prepareOnly: true`, the return value shows which approval endpoint to use. Approve a `transactionId` with the [approve transaction](/api-reference/wallets/approve-transaction) endpoint. Approve a `signatureId` with the [approve signature](/api-reference/wallets/approve-signature) endpoint.

This option is currently available on EVM through the TypeScript SDK, which installs the signer immediately by default. Solana and Stellar always install signers with an onchain transaction.

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. By default, the EVM endpoint returns a deferred signature. To get an onchain transaction immediately, set `deployImmediately: true` in the request body.

        <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. The EVM endpoint defaults to the deferred signature flow, so use the [approve signature](/api-reference/wallets/approve-signature) endpoint. If you passed `deployImmediately: true` in the request, the response contains a transaction and you must use the [approve transaction](/api-reference/wallets/approve-transaction) endpoint. For Solana and Stellar, the response is always a transaction and uses 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, Solana, 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                                                                                                                 |
| Server      | EVM, Solana, Stellar | Automatic approval (no OTP); see [Configure Wallet Recovery](/wallets/guides/signers/configure-recovery#server-signer-as-recovery) |

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