Previous versions of these guides used the term delegated signer. We have moved to operational signer as the standard terminology. The next breaking API / SDK version will adopt this naming convention in method signatures and types. Until then, the code snippets below still reference
addDelegatedSigner and delegatedSigners.Prerequisites
- Ensure you have a wallet created.
- API Key: Ensure you have an API key with the scopes:
wallets:signatures.createandwallets:transactions.create.
Adding an operational signer
- React
- Node.js
- React Native
- REST
Report incorrect code
Copy
Ask AI
import { useWallet } from '@crossmint/client-sdk-react-ui';
const { wallet } = useWallet();
const externalSigner = {
type: "external-wallet",
address: "0x1234567890123456789012345678901234567890"
}
await wallet.addDelegatedSigner({
signer: externalSigner,
});
Report incorrect code
Copy
Ask AI
import { CrossmintWallets, WalletsApiClient, createCrossmint } from "@crossmint/wallets-sdk";
import { privateKeyToAccount } from "viem/accounts";
const crossmint = createCrossmint({
apiKey: "<your-server-api-key>",
});
const crossmintWallets = CrossmintWallets.from(crossmint);
const apiClient = new WalletsApiClient(crossmint);
// The wallet's admin (recovery) signer — used to approve adding the new signer
const adminAccount = privateKeyToAccount(
process.env.ADMIN_WALLET_PRIVATE_KEY as `0x${string}`
);
// The new operational signer to register
const operationalSignerAddress = "0x1234567890123456789012345678901234567890";
const wallet = await crossmintWallets.getWallet(
"<wallet-address>",
{ chain: "base-sepolia", signer: { type: "external-wallet" } }
);
// 1. Add the operational signer in prepare-only mode
const { signatureId } = await wallet.addDelegatedSigner({
signer: `external-wallet:${operationalSignerAddress}`,
options: { experimental_prepareOnly: true },
});
// 2. Fetch the pending signature to get the message that needs signing
const delegatedSignatureResponse = await apiClient.getSignature(
wallet.address,
signatureId
);
if ("error" in delegatedSignatureResponse) {
throw new Error(
`Failed to get signature: ${JSON.stringify(delegatedSignatureResponse)}`
);
}
const pendingApproval = delegatedSignatureResponse.approvals?.pending?.[0];
if (!pendingApproval) {
throw new Error("No pending approval found");
}
// 3. Sign the pending approval with the admin signer's private key
// (the wallet's original signer, not the signer being added)
const signature = await adminAccount.signMessage({
message: { raw: pendingApproval.message as `0x${string}` },
});
// 4. Submit the approval using the admin signer
await wallet.approve({
signatureId,
options: {
experimental_approval: {
signature,
signer: `external-wallet:${adminAccount.address}`,
},
},
});
console.log("Operational signer added successfully");
If the wallet’s admin (recovery) signer is an
api-key type, the approval is handled automatically — you can call addDelegatedSigner without experimental_prepareOnly and skip the approval steps.Report incorrect code
Copy
Ask AI
import { useWallet } from '@crossmint/client-sdk-react-native-ui';
const { wallet } = useWallet();
const externalSigner = {
type: "external-wallet",
address: "0x1234567890123456789012345678901234567890"
}
await wallet.addDelegatedSigner({
signer: externalSigner,
});
Operational signers must be approved by the wallet’s recovery signer (called Sign the approval message field returned in the response inside
adminSigner in the current API).
The SDK handles this automatically, but with the REST API you must approve the transaction (if using Solana, Stellar)
or signature (if using EVM) to complete it.Create the operational signer
Call the create delegated signer endpoint.See the API reference for more details.
Report incorrect code
Copy
Ask AI
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets/email:user@example.com:evm/signers \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '{
"chain": "base",
"signer": "external-wallet:0x1234567890123456789012345678901234567890"
}'
Sign the approval returned in the response
If you are using an
api-key as the recovery signer (admin signer) you can skip the following steps.transaction.approvals (for EVM) or signature.approvals (for Solana and Stellar) using the recovery signer (admin signer).Approve the transaction or signature to register the operational signer
- EVM
- Solana and Stellar
Call the approve signature endpoint using the signature from the previous step and the signature id returned in the call from Step 1.See the API reference for more details.
Report incorrect code
Copy
Ask AI
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets/email:user@example.com:evm/signatures/b984491a-5785-43c0-8811-45d46fe6e520/approvals \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '{
"approvals": [{
"signer": "email:user@example.com",
"signature": "0x1234567890abcdef..."
}]
}'
Call the approve transaction endpoint using the signature from the previous step and the transaction id returned in the call from Step 1.See the API reference for more details.
Report incorrect code
Copy
Ask AI
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets/email:user@example.com:solana/transactions/b984491a-5785-43c0-8811-45d46fe6e520/approvals \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '{
"approvals": [{
"signer": "email:user@example.com",
"signature": "5qY6wXw6zTn7..."
}]
}'
Listing all operational signers
- React
- Node.js
- React Native
- REST
Report incorrect code
Copy
Ask AI
import { useWallet } from '@crossmint/client-sdk-react-ui';
const { wallet } = useWallet();
const signers = await wallet.delegatedSigners();
Report incorrect code
Copy
Ask AI
import { CrossmintWallets, createCrossmint } 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", signer: { type: "external-wallet" } }
);
const signers = await wallet.delegatedSigners();
React Native
Report incorrect code
Copy
Ask AI
import { useWallet } from '@crossmint/client-sdk-react-native-ui';
const { wallet } = useWallet();
const signers = await wallet.delegatedSigners();
Report incorrect code
Copy
Ask AI
curl --request GET \
--url https://staging.crossmint.com/api/2025-06-09/wallets/email:user@example.com:evm \
--header 'X-API-KEY: <x-api-key>'

